git merge: paano pagsamahin ang mga branch at ayusin ang conflict
Pinagsasama ng git merge ang kasaysayan ng ibang branch sa kasalukuyang branch: kung hindi nakalayo ang kasalukuyang branch, ililipat lang ng Git ang pointer (fast-forward); kung hindi, gagawa ito ng merge commit na may dalawang parent. Lumipat sa target na branch at patakbuhin ang git merge pangalan-ng-branch; kapag may conflict, ayusin ang mga file, gawin ang git add at git commit, o kanselahin ang lahat gamit ang git merge --abort.
Ano ang ginagawa ng git merge
Kinukuha ng git merge ang mga pagbabago mula sa tinukoy na branch at ipinapasok ang mga ito sa branch na kinaroroonan ninyo ngayon. Mahalagang tandaan ang direksyon: sa sarili pinagsasama. Para ipasok ang feature/login sa main, lumipat muna sa main, saka tawagin ang merge.
git switch main
git pull # kunin ang bagong main mula sa server
git merge feature/login
Hindi kinokopya ng Git ang code linya-por-linya nang manwal. Hahanapin nito ang karaniwang ninuno ng dalawang branch (merge base), ihahambing ang dalawang bersyon sa ninunong iyon, at pagsasamahin ang mga pagbabago. Kung magkaiba ang bahaging binago ng dalawang panig, awtomatiko ang resulta. Kung iisang bahagi ang binago, magkakaroon ng conflict, at kayo ang magpapasya.
Dalawang mode: fast-forward at three-way merge
Fast-forward: kapag hindi kailangan ng merge commit
Ipagpalagay na humiwalay ang feature mula sa main, nagawa ninyo ang tatlong commit, at walang idinagdag sa main sa panahong iyon. Ang history ng main ay simpleng simula ng history ng feature. Ililipat ng Git ang pointer ng main pasulong, sa huling commit ng feature, at walang bagong commit na gagawin.
$ git merge feature
Updating 3f2a1c4..9b7e0d2
Fast-forward
src/auth.js | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
Nananatiling tuwid ang history, pero nawawala ang katotohanang "may branch na feature dito". Minsan ayos ito, minsan hindi; tatalakayin ito sa ibaba, sa bahagi tungkol sa --no-ff.
Three-way merge at merge commit
Kapag pareho nang lumayo ang dalawang branch mula sa karaniwang ninuno, hindi na maililipat ang pointer. Nagsasagawa ang Git ng three-way merge: ang base, ang "amin" na panig (kasalukuyang branch), at ang "kanila" na panig (sinasamang branch). Ang resulta ay inire-record bilang hiwalay na merge commit na may dalawang parent.
$ git merge feature
Merge made by the 'ort' strategy.
src/auth.js | 42 ++++++++++++++++++++
src/router.js | 3 +-
2 files changed, 44 insertions(+), 1 deletion(-)
Magbubukas ang Git ng editor na may mensaheng gaya ng Merge branch 'feature'. Puwede itong iwan, o dagdagan ng paliwanag kung bakit isinagawa ang pagsasama. Para makita ang nagresultang "sanga", ganito:
git log --oneline --graph --all
Ang estratehiyang ort ang default simula Git 2.34; dati ay recursive. Sa araw-araw na trabaho, hindi na kailangang palitan ang estratehiya.
Mga kapaki-pakinabang na flag ng git merge
| Flag | Ano ang ginagawa | Kailan kapaki-pakinabang |
|---|---|---|
--no-ff | Laging gumagawa ng merge commit, kahit posible ang fast-forward | Gusto ninyong makita sa history kung aling mga commit ang galing sa iisang feature |
--ff-only | Sinasama lang kung fast-forward, kung hindi, nagkakaroon ng error | Pag-update ng lokal na branch nang walang hindi sinasadyang merge commit |
--squash | Inilalagay ang lahat ng pagbabago ng branch sa index, pero hindi nagko-commit at hindi gumagawa ng ugnayan sa branch | Gusto ninyong gawing iisang maayos na commit ang feature |
--no-commit | Ginagawa ang merge pero humihinto bago ang commit | Gusto ninyong suriin ang resulta o magpatakbo ng test bago i-record |
-m "teksto" | Itinatakda ang mensahe ng merge commit | Merge sa mga script at walang editor |
--abort | Kinakansela ang nagsisimulang merge at ibinabalik ang estado bago ito | Sobrang dami ng conflict, kailangang magsimula ulit |
--continue | Tinatapos ang merge pagkatapos ayusin ang conflict | Alternatibo sa git commit sa huling hakbang |
-X ours / -X theirs | Sa mga conflicting na bahagi, awtomatikong kinukuha ang bersyon natin o ang kanila | Sigurado kayong alin ang tamang bersyon (mag-ingat!) |
--no-ff: panatilihin ang hangganan ng feature
Maraming team ang nagme-merge ng feature gamit lang ang --no-ff. Sa ganitong paraan, bawat task ay mukhang "bula" sa graph, at maaari itong i-revert gamit ang iisang git revert -m 1 <merge-commit>. Sa fast-forward, walang ganitong punto, at kailangang i-undo ang bawat commit nang isa-isa.
--squash: isang commit sa halip na sampu
git switch main
git merge --squash feature/login
git commit -m "Login gamit ang e-mail at password"
Pagkatapos ng --squash, hindi itinuturing ng Git na na-merge ang branch: tatanggi ang git branch -d feature/login na may babalang "not fully merged". Gamitin ang git branch -D kapag nakumpirma nang tama ang lahat. At huwag ituloy ang trabaho sa parehong branch pagkatapos ng squash: dadalhin ng susunod na merge ulit ang mga pagbabagong naisama na, at halos tiyak na magkakaroon ng conflict.
Conflict sa merge: paano ito ayusin
Ang conflict ay normal na sitwasyon sa trabaho, hindi aksidente. Ibig sabihin nito: binago ng dalawang branch ang iisang linya (o binura ng isa ang file na ginalaw ng isa pa), at hindi huhulaan ng Git kung aling bersyon ang tama.
$ git merge feature/login
Auto-merging src/config.js
CONFLICT (content): Merge conflict in src/config.js
Automatic merge failed; fix conflicts and then commit the result.
Hakbang 1. Tingnan kung ano ang nagkakaconflict
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/config.js
Hakbang 2. I-edit ang file
Naglalagay ang Git ng mga marker sa file na may conflict:
<<<<<<< HEAD
const TIMEOUT = 5000;
=======
const TIMEOUT = 10000;
>>>>>>> feature/login
Ang itaas na bahagi ay ang kasalukuyan ninyong branch (HEAD), ang ibaba ay ang sinasamang branch. Iwan ang tamang bersyon (o pagsamahin ang dalawa), burahin ang tatlong marker na linya, at i-save ang file. Karaniwang pagkakamali ang pagkalimot sa marker sa gitna ng file; bago mag-commit, hanapin ang mga ito: ipapakita ng git diff --check ang mga natitirang marker.
Hakbang 3. Markahan ang solusyon at tapusin ang merge
git add src/config.js
git commit # o git merge --continue
Kung buong conflict ay malulutas sa pagpili ng isang panig, hindi na kailangang i-edit nang manwal ang file:
git checkout --ours src/config.js # panatilihin ang bersyon ng kasalukuyang branch
git checkout --theirs src/config.js # kunin ang bersyon ng sinasamang branch
git add src/config.js
Mas maginhawa sa editor
Ipinapakita ng VS Code, mga IDE ng JetBrains, at iba pang editor ang conflict sa tatlong kolum at nagbibigay ng mga button na "accept current / incoming / both". Puwede ring ikabit ang external na tool gamit ang git mergetool. Kapaki-pakinabang na setting ang git config --global merge.conflictStyle zdiff3: idinaragdag nito sa mga marker ang orihinal na bersyon mula sa karaniwang ninuno, kaya malinaw kung sino ang nagbago ng ano.
Nagbago ang isip: kanselahin ang merge
git merge --abort
Ibinabalik ng command ang working directory at index sa estado bago ang git merge. Kaya mas mainam na malinis ang working tree bago mag-merge: mag-commit o itabi muna ang hindi pa tapos na pagbabago gamit ang git stash, kung hindi, mahirap itong ihiwalay sa mga resulta ng merge.
Paano i-undo ang merge na nagawa na
Kung hindi pa naipapadala sa server ang merge commit, ang pinakamadaling paraan ay ibalik ang branch sa dating posisyon:
git reset --hard ORIG_HEAD
Itinatala ng Git ang ORIG_HEAD bago ang mga mapanganib na operasyon, kasama na ang merge; ito ang posisyon ng branch bago ang merge. Binubura ng --hard ang mga hindi pa naka-commit na pagbabago, kaya tingnan muna ang git status.
Kung nasa shared repository na ang merge, hindi binabago ang history. Gumagawa ng kabaligtarang commit:
git revert -m 1 a1b2c3d
Itinuturing ng -m 1 na "pangunahin" ang unang parent (ang branch kung saan isinama). Tandaan ang bitag: pagkatapos ng ganitong revert, itinuturing ng Git na naisama na ang mga commit ng feature. Kung gusto ninyong isama ulit ang inayos na feature sa susunod, kailangan muna ninyong i-revert ang revert commit.
Merge sa team work
Ang git pull ay fetch plus merge
Ang karaniwang git pull ay nagda-download ng mga pagbabago at sinasama ang remote branch sa lokal. Kung sabay kayong nag-commit ng katrabaho, lilitaw ang commit na "Merge branch 'main' of ...". Para hindi magkaroon ng ganitong teknikal na merge, i-set ang git config --global pull.ff only (babagsak ang pull kung hindi posible ang fast-forward, at kayo ang magpapasya), o gamitin ang git pull --rebase.
Pull request at ang button na Merge
Sa GitHub at GitLab, kadalasang nagaganap ang merge sa pamamagitan ng pull/merge request. Ang button na "Create a merge commit" ay katumbas ng git merge --no-ff, ang "Squash and merge" ay merge na may squash, at ang "Rebase and merge" ay rebase. Ang pagpili ng mode ay kasunduan ng team, at pareho ang mekanismo sa likod nito sa console.
Isama ang main sa sarili ninyong branch
Kapag matagal nang nabubuhay ang branch at lumalayo sa main, nag-iipon ang mga conflict. Regular na kunin ang pangunahing branch sa sarili ninyong branch:
git switch feature/login
git fetch origin
git merge origin/main
Sa ganitong paraan, inaayos ang mga conflict nang maliliit na bahagi habang lumilitaw, at maayos ang huling merge sa main.
Karaniwang pagkakamali
- Nasa maling direksyon ang merge. Nagpatakbo ng
git merge mainhabang nasamainna, o isinama ang feature sa ibang feature. Suriin ang kasalukuyang branch gamit anggit branch --show-currentbago mag-merge. - Merge sa lumang branch. Isinama ang lokal na
mainna isang linggo nang hindi na-update. Unahin anggit pull, saka ang merge. - "Your local changes would be overwritten by merge". May hindi pa naka-commit na pagbabago sa parehong file sa working directory. I-commit o itabi sa stash.
- "refusing to merge unrelated histories". Walang karaniwang ninuno ang mga branch, halimbawa, ginawa ang lokal na repository gamit ang
git init, pero may README na ang remote. Kung talagang kailangang pagsamahin, idagdag ang--allow-unrelated-histories. - Naka-commit ang mga conflict marker. Nag-fail ang build sa
<<<<<<<. Suriin gamit anggit diff --checkat magpatakbo ng test bago mag-commit.
Buod
Ang git merge ay ligtas na paraan para pagsamahin ang mga branch: hindi nito binabago ang umiiral nang commit; nagdaragdag lang ito ng bago, o ililipat ang pointer. Sumama sa kasalukuyang branch, panatilihing malinis ang working tree, gamitin ang --no-ff kung mahalaga ang hangganan ng feature, at ang --squash kung gusto ninyo ng iisang commit. Inaayos ang conflict sa tatlong hakbang: i-edit ang file, git add, git commit; at palaging maibabalik ng git merge --abort ang ninyong panimulang punto. Ang pagsasama ng branch at pag-ayos ng conflict ay itinuturo nang praktikal sa kursong Git namin.
Mga madalas itanong
Ano ang pagkakaiba ng git merge at git pull?
Unang kinukuha ng git pull ang mga pagbabago mula sa server (git fetch), at pagkatapos ay sinasama ang remote branch sa kasalukuyan gamit ang git merge, o rebase kung naka-set. Ang git merge ay gumagana lang sa mga bagay na nasa lokal na.
Puwede bang pagsamahin ang ilang branch nang sabay?
Oo, ang git merge feature-a feature-b ay gagawa ng octopus merge na may maraming parent, pero kung walang conflict lamang. Sa praktika, karaniwang isa-isa ang pagsasama ng branch.
Paano malalaman kung aling mga branch na ang nasama sa kasalukuyan?
Ipinapakita ng git branch --merged ang mga branch na buong nasama na sa kasalukuyan, at ng git branch --no-merged ang hindi pa. Ligtas nang burahin ang mga nasamang branch gamit ang git branch -d.
Paano makikita nang maaga kung ano ang dadalhin ng merge?
Patakbuhin ang git log main..feature para makita ang mga commit na wala sa main, at git diff main...feature para makita ang pagbabago ng branch laban sa karaniwang ninuno.
Ano ang ORIG_HEAD pagkatapos ng merge?
Ito ang reference na itinatala ng Git bago ang merge; tumuturo ito sa posisyon ng branch bago ang merge. Sa pamamagitan ng git reset --hard ORIG_HEAD, puwede ninyong i-undo ang lokal na merge habang hindi pa ito naipapadala sa server.
Detalyadong tinatalakay ang paksang ito, may praktis, sa kursong “Git с нуля до уверенного разработчика”.
Pumunta sa kurso