Git三方合并策略詳解
一、什么是合并(Merge)
在 Git 中,合并是將兩條獨(dú)立的開(kāi)發(fā)線(分支)的修改整合到一起的操作。當(dāng)你執(zhí)行 git merge 時(shí),Git 需要決定如何將兩個(gè)分支的代碼變更合并為一個(gè)最終結(jié)果。
二、快進(jìn)合并(Fast-Forward Merge)
在了解三方合并之前,先了解最簡(jiǎn)單的合并方式。
場(chǎng)景
A --- B --- C (main)
\
D --- E (feature)如果 main 分支在你創(chuàng)建 feature 分支后沒(méi)有任何新提交,合并時(shí) Git 只需要把 main 的指針直接移動(dòng)到 feature 的最新提交即可:
A --- B --- C --- D --- E (main, feature)
這就是快進(jìn)合并,不會(huì)產(chǎn)生新的合并提交。
特點(diǎn)
- 不產(chǎn)生合并提交(merge commit)
- 歷史記錄是一條直線
- 只有在目標(biāo)分支沒(méi)有新提交時(shí)才會(huì)發(fā)生
三、三方合并(Three-Way Merge)
什么時(shí)候觸發(fā)
當(dāng)兩個(gè)分支都有各自的新提交時(shí),Git 無(wú)法簡(jiǎn)單地快進(jìn),必須執(zhí)行三方合并:
A --- B --- C --- F --- G (main)
\
D --- E (feature)
此時(shí) main 有了 F、G 兩個(gè)新提交,feature 有了 D、E 兩個(gè)新提交,Git 需要把兩邊的修改合在一起。
三方合并的"三方"是什么
三方合并涉及三個(gè)版本:
| 角色 | 說(shuō)明 | 示例 |
|---|---|---|
| 共同祖先(Merge Base) | 兩個(gè)分支最近的共同提交 | 提交 C |
| 當(dāng)前分支(Ours) | 你當(dāng)前所在的分支的最新?tīng)顟B(tài) | 提交 G |
| 目標(biāo)分支(Theirs) | 你要合并進(jìn)來(lái)的分支的最新?tīng)顟B(tài) | 提交 E |
為什么需要共同祖先
假設(shè)某個(gè)文件的某一行:
- 在共同祖先中是
x = 10 - 在 main 中是
x = 10(沒(méi)改) - 在 feature 中是
x = 20(改了)
如果沒(méi)有共同祖先作為參照,Git 只看到 main 是 x = 10,feature 是 x = 20,無(wú)法判斷應(yīng)該用哪個(gè)。
有了共同祖先,Git 的判斷邏輯就很清晰:
- 共同祖先是
x = 10,main 也是x = 10→ main 沒(méi)改 - 共同祖先是
x = 10,feature 是x = 20→ feature 改了 - 結(jié)論:采用 feature 的修改
四、共同祖先(Merge Base)
定義
共同祖先是兩個(gè)分支在提交歷史中最近的共同節(jié)點(diǎn)。它代表了兩個(gè)分支"分道揚(yáng)鑣"的那個(gè)時(shí)間點(diǎn)。
圖示
E --- F (feature-A)
/
A --- B --- C --- D (main)
\
G --- H (feature-B)
feature-A和main的共同祖先是 Bfeature-B和main的共同祖先是 Bfeature-A和feature-B的共同祖先也是 B
查找共同祖先
git merge-base <branch1> <branch2>
這個(gè)命令會(huì)輸出共同祖先的 commit SHA。
復(fù)雜場(chǎng)景:多次合并后的共同祖先
A --- B --- C --- D --- M1 --- E (main)
\ /
F --- G --- H (feature)
如果 feature 曾經(jīng)被合并到 main(M1),之后 feature 又繼續(xù)開(kāi)發(fā),那么再次合并時(shí):
- 共同祖先不再是 B,而是 H(上次合并時(shí) feature 的狀態(tài))
Git 會(huì)自動(dòng)找到最近的共同祖先,確保只合并上次合并之后的新變更。
五、三方合并的決策規(guī)則
對(duì)于文件中的每一處差異,Git 按以下規(guī)則決定最終結(jié)果:
| 共同祖先 | Ours(當(dāng)前分支) | Theirs(目標(biāo)分支) | Git 的決策 |
|---|---|---|---|
| A | A(未改) | B(改了) | 采用 B |
| A | B(改了) | A(未改) | 采用 B |
| A | B(改了) | C(也改了,但不同) | 沖突! |
| A | B(改了) | B(改了,且相同) | 采用 B |
| A | A(未改) | A(未改) | 保持 A |
規(guī)則總結(jié)
- 只有一方修改 → 自動(dòng)采用修改方的版本
- 雙方做了相同修改 → 自動(dòng)采用(無(wú)沖突)
- 雙方做了不同修改 → 產(chǎn)生沖突,需要人工解決
- 雙方都沒(méi)改 → 保持原樣
六、沖突(Conflict)
什么時(shí)候產(chǎn)生沖突
當(dāng)同一處代碼兩個(gè)分支都做了不同的修改時(shí),Git 無(wú)法自動(dòng)決定用哪個(gè)版本,就會(huì)標(biāo)記為沖突。
沖突標(biāo)記
<<<<<<< HEAD // 當(dāng)前分支(ours)的代碼 int count = getCount(); ======= // 目標(biāo)分支(theirs)的代碼 long count = getCount(); >>>>>>> feature
解決沖突
- 手動(dòng)編輯文件,選擇保留哪個(gè)版本(或合并兩者)
- 刪除沖突標(biāo)記(
<<<<<<<、=======、>>>>>>>) git add <file>標(biāo)記為已解決git commit完成合并
七、合并提交(Merge Commit)
三方合并完成后,Git 會(huì)創(chuàng)建一個(gè)特殊的合并提交,它有兩個(gè)父提交:
A --- B --- C --- F --- G --- M (main)
\ /
D --- E ---- (feature)
M 就是合并提交,它的兩個(gè)父提交分別是 G(main 的最新)和 E(feature 的最新)。
查看合并提交的父提交
git log -1 --format="%P" <merge_commit>
輸出兩個(gè) SHA,第一個(gè)是當(dāng)前分支的父提交(ours),第二個(gè)是被合并分支的父提交(theirs)。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
八、實(shí)際場(chǎng)景分析
場(chǎng)景:從舊分支合并到已更新的目標(biāo)分支
時(shí)間線: 1月:dev 上有人把 Integer 改為 Long 3月:你從 master(還是 Integer)拉出 feature 分支 5月:你把 feature 合并到 dev
合并時(shí)的三方對(duì)比:
| 共同祖先(master 3月?tīng)顟B(tài)) | 你的分支 | dev |
|---|---|---|
Integer count = ... | Integer count = ... | Long count = ... |
Git 判斷:你沒(méi)改這行,dev 改了 → 采用 dev 的 Long。
場(chǎng)景:你修改了同一行附近的代碼
如果你在 feature 分支修改了 Integer count = ... 這行附近的代碼(比如上下幾行),Git 可能會(huì):
- 把你修改的部分保留
- 把 dev 修改的部分也保留
- 如果兩者修改了同一行 → 產(chǎn)生沖突
場(chǎng)景:你的分支不是從 dev 拉的
如果你從 master 拉分支,而 master 和 dev 的代碼狀態(tài)不同,合并到 dev 時(shí)共同祖先可能是很早之前的提交,導(dǎo)致大量差異需要合并,增加了自動(dòng)合并出錯(cuò)的風(fēng)險(xiǎn)。
九、合并策略選項(xiàng)
recursive(默認(rèn))
Git 默認(rèn)使用 recursive 策略進(jìn)行三方合并。當(dāng)存在多個(gè)共同祖先時(shí),它會(huì)遞歸地合并這些祖先來(lái)構(gòu)造一個(gè)"虛擬祖先"。
ours
git merge -s ours feature
完全忽略對(duì)方的修改,保留當(dāng)前分支的所有內(nèi)容。
theirs(通過(guò)選項(xiàng)實(shí)現(xiàn))
git merge -X theirs feature
沖突時(shí)自動(dòng)選擇對(duì)方的版本。
十、最佳實(shí)踐
- 頻繁同步:定期將目標(biāo)分支(dev/main)合并到你的 feature 分支,減少最終合并時(shí)的差異
- 從正確的分支拉取:如果要合并到 dev,就從 dev 拉分支,而不是從 master
- 小步提交:每次提交的改動(dòng)盡量小且聚焦,減少?zèng)_突范圍
- 合并前先拉取最新:
git fetch+git merge確保本地是最新?tīng)顟B(tài) - 合并后立即驗(yàn)證:編譯、運(yùn)行測(cè)試,確保合并結(jié)果正確
十一、總結(jié)
三方合并的核心思想:
通過(guò)共同祖先作為參照基準(zhǔn),判斷每一處差異是"誰(shuí)改的",從而自動(dòng)決定最終結(jié)果。只有當(dāng)雙方都改了同一處且改法不同時(shí),才需要人工介入。
理解了這個(gè)原理,你就能理解為什么合并后代碼會(huì)"自動(dòng)變化"——不是 Git 出了問(wèn)題,而是它正確地執(zhí)行了三方合并的邏輯。
以上就是Git三方合并策略詳解的詳細(xì)內(nèi)容,更多關(guān)于Git三方合并策略的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
301重定向代碼合集(iis,asp,php,asp.net,apache)
腳本之家將SEO工作中所需要的301轉(zhuǎn)向代碼進(jìn)行了整理,收藏并分享,以備查閱。2011-02-02
VS2019無(wú)法啟動(dòng)程序(系統(tǒng)找不到指定文件)解決辦法
這篇文章主要介紹了VS2019無(wú)法啟動(dòng)程序(系統(tǒng)找不到指定文件)解決辦法,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
解決idea git切換多個(gè)分支后maven不生效的問(wèn)題
這篇文章主要介紹了解決idea git切換多個(gè)分支后maven不生效的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09

