_突解決實(shí)戰(zhàn))
1. 從一次合并沖突說(shuō)起為什么你需要理解git merge那天下午我正在處理一個(gè)功能模塊本地feature/login分支的開發(fā)已經(jīng)接近尾聲。按照流程我需要將最新的develop分支代碼合并進(jìn)來(lái)確保我的功能能兼容主線的更新。我熟練地切回develop拉取了最新代碼然后切換回我的功能分支輸入了那個(gè)再熟悉不過(guò)的命令git merge develop。幾秒鐘后終端里一片飄紅。沖突而且不止一處。更頭疼的是沖突不僅出現(xiàn)在我修改過(guò)的業(yè)務(wù)邏輯文件里連一些我根本沒動(dòng)過(guò)的、由構(gòu)建工具自動(dòng)生成的配置文件也出現(xiàn)了沖突。那一刻我意識(shí)到我對(duì)git merge的理解可能還停留在“把別人的代碼拿過(guò)來(lái)”這個(gè)膚淺的層面。它背后關(guān)于提交歷史、三方合并、快進(jìn)模式這些機(jī)制我并沒有真正吃透。結(jié)果就是面對(duì)復(fù)雜的沖突我有點(diǎn)手足無(wú)措只能憑感覺去解決既耗時(shí)又容易引入新的錯(cuò)誤。我相信很多開發(fā)者都有類似的經(jīng)歷。git merge是 Git 日常使用中最核心的命令之一但大多數(shù)人只是記住了命令格式對(duì)其內(nèi)在邏輯和不同場(chǎng)景下的表現(xiàn)一知半解。這就像開車只會(huì)踩油門和剎車卻不了解變速箱和四驅(qū)系統(tǒng)的工作原理在平坦大道上沒問(wèn)題一旦遇到復(fù)雜路況就容易趴窩。這篇文章我想和你深入聊聊git merge。我們不止步于git merge branch_name這個(gè)簡(jiǎn)單的語(yǔ)法而是要拆解它的幾種核心合并策略、理解合并提交Merge Commit是如何產(chǎn)生的、掌握快進(jìn)Fast-forward與非快進(jìn)合并的區(qū)別以及最重要的——當(dāng)合并遇到?jīng)_突時(shí)一套清晰、可復(fù)現(xiàn)的排查和解決思路。無(wú)論你是剛接觸 Git 的新手還是想理順團(tuán)隊(duì)協(xié)作流程的資深開發(fā)者這些內(nèi)容都能幫你更自信、更安全地管理代碼分支。2. 合并的本質(zhì)不是復(fù)制文件而是整合歷史在深入命令之前我們必須建立一個(gè)正確的認(rèn)知Git 的合并操作的對(duì)象是提交歷史而不是直接操作工作目錄里的文件。這是理解后續(xù)所有行為的基礎(chǔ)。想象一下我們的項(xiàng)目歷史就是一棵不斷生長(zhǎng)的大樹。每個(gè)提交Commit都是樹上的一個(gè)節(jié)點(diǎn)分支Branch就是指向某個(gè)節(jié)點(diǎn)的可移動(dòng)指針。當(dāng)我們創(chuàng)建feature分支時(shí)相當(dāng)于從main分支所在的節(jié)點(diǎn)長(zhǎng)出了一條新枝杈。之后main和feature分支各自向前演進(jìn)提交新的節(jié)點(diǎn)。git merge要做的事情是找到這兩個(gè)分支最近的共同祖先Common Ancestor也就是它們“分叉”的那個(gè)提交節(jié)點(diǎn)。然后Git 會(huì)嘗試創(chuàng)造一個(gè)新的提交這個(gè)提交的內(nèi)容包含了自共同祖先以來(lái)兩個(gè)分支上所有變更的合集。這個(gè)過(guò)程通常通過(guò)“三方合并”來(lái)實(shí)現(xiàn)。三方指的是合并基礎(chǔ)Base兩個(gè)分支最近的共同祖先提交。當(dāng)前分支Ours你執(zhí)行merge命令時(shí)所處的分支例如feature。目標(biāo)分支Theirs你命令中指定要合并進(jìn)來(lái)的分支例如develop。Git 會(huì)比較這三個(gè)版本中每一個(gè)文件的變化如果某個(gè)文件只在Ours或Theirs一個(gè)分支上被修改了那么合并后的文件就采用那個(gè)被修改的版本。這很簡(jiǎn)單。如果同一個(gè)文件的同一行或相鄰行在Ours和Theirs分支上都被修改了但修改的內(nèi)容不同Git 就無(wú)法自動(dòng)決定該用哪個(gè)版本。這時(shí)就會(huì)產(chǎn)生我們最不愿看到的——合并沖突。所以當(dāng)你執(zhí)行g(shù)it merge時(shí)Git 首先在后臺(tái)進(jìn)行一系列復(fù)雜的圖計(jì)算和差異分析試圖自動(dòng)生成一個(gè)合并結(jié)果。只有無(wú)法自動(dòng)處理時(shí)才會(huì)把問(wèn)題拋給你手動(dòng)解決。理解了這個(gè)底層邏輯我們就能更好地預(yù)判合并行為并在沖突發(fā)生時(shí)知道從哪里入手。3. 兩種主要的合并策略快進(jìn)合并與創(chuàng)建合并提交根據(jù)分支歷史的結(jié)構(gòu)不同git merge會(huì)采用兩種不同的策略產(chǎn)生截然不同的歷史記錄。這是影響項(xiàng)目歷史圖譜清晰度的關(guān)鍵。3.1 快進(jìn)合并Fast-forward Merge這是最簡(jiǎn)單、最理想的情況。當(dāng)你要合并的目標(biāo)分支例如develop的尖端直接領(lǐng)先于你當(dāng)前分支例如feature時(shí)就會(huì)發(fā)生快進(jìn)合并。場(chǎng)景還原假設(shè)你從develop分支的C2提交創(chuàng)建了feature分支。之后你只在feature上工作而develop分支沒有任何新的提交。此時(shí)develop指針仍然指向C2而feature指針已經(jīng)指向了C3。feature的歷史是develop歷史的直接延伸沒有分叉。develop: C1 --- C2 \ feature: C3在這種情況下將develop合并到feature是沒有意義的因?yàn)閐evelop更舊。但如果我們討論將feature合并回develop命令git merge feature會(huì)直接將develop指針從C2“快進(jìn)”到C3。整個(gè)歷史線仍然是一條直線不會(huì)產(chǎn)生額外的合并提交。操作與影響# 當(dāng)前在 develop 分支 (指向 C2) git merge feature合并后歷史變?yōu)閐evelop: C1 --- C2 --- C3 feature: (指向C3)優(yōu)點(diǎn)歷史非常清晰、線性。缺點(diǎn)丟失了“曾經(jīng)存在過(guò)一個(gè)獨(dú)立功能分支”的信息。在團(tuán)隊(duì)協(xié)作中如果所有人都用快進(jìn)合并主分支的歷史就無(wú)法直觀反映出哪些提交屬于哪個(gè)功能或修復(fù)。強(qiáng)制與非快進(jìn)合并有時(shí)即使?jié)M足快進(jìn)條件我們可能也希望保留分支信息。這時(shí)可以使用--no-ff(no fast-forward) 選項(xiàng)git merge --no-ff feature這將會(huì)強(qiáng)制 Git 創(chuàng)建一個(gè)新的合并提交M即使可以快進(jìn)。develop: C1 --- C2 ------- M \ / feature: C3很多團(tuán)隊(duì)的規(guī)范會(huì)要求合并功能分支時(shí)必須使用--no-ff以便在歷史中明確看到分支的整合點(diǎn)。3.2 非快進(jìn)合并 / 創(chuàng)建合并提交這是更常見的情況。當(dāng)兩個(gè)分支自從共同祖先之后都有新的提交時(shí)就無(wú)法進(jìn)行快進(jìn)合并。場(chǎng)景還原同樣從develop的C2創(chuàng)建feature。但之后develop分支也有其他人提交了新的內(nèi)容C4。歷史產(chǎn)生了分叉。develop: C1 --- C2 --- C4 \ feature: C3此時(shí)在feature分支上執(zhí)行g(shù)it merge developGit 會(huì)發(fā)現(xiàn)共同祖先是C2然后嘗試將C2到C3的變化我們的修改和C2到C4的變化develop的修改整合起來(lái)。如果自動(dòng)合并成功Git 會(huì)創(chuàng)建一個(gè)新的合并提交Merge CommitM。這個(gè)提交有兩個(gè)父提交C3和C4。feature: C1 --- C2 --- C3 --- M \ / develop: C4 ---合并提交的特點(diǎn)它有兩個(gè)或更多父提交。它本身不包含源代碼的變更變更已經(jīng)體現(xiàn)在它的父提交里了它的作用是“記錄一次合并事件”。在圖形化歷史工具中如git log --graph它會(huì)清晰地顯示出分支的匯合。實(shí)操建議在團(tuán)隊(duì)開發(fā)中非快進(jìn)合并是常態(tài)。理解合并提交的意義有助于你閱讀復(fù)雜的歷史。當(dāng)你看到一個(gè)有多個(gè)父提交的提交時(shí)就知道這是一次分支合并的里程碑。4. 核心實(shí)戰(zhàn)git merge命令詳解與操作流程掌握了原理我們來(lái)看具體怎么用。命令本身很簡(jiǎn)單但圍繞它的最佳實(shí)踐卻值得深究。4.1 基礎(chǔ)命令格式與步驟最標(biāo)準(zhǔn)的合并操作流程如下# 1. 確保當(dāng)前分支是你想接收變更的分支目標(biāo)分支 git checkout main # 或 develop 假設(shè)你想把 feature 合并進(jìn)來(lái) # 2. 拉取遠(yuǎn)程最新代碼確保本地分支是最新的避免后續(xù)沖突復(fù)雜化 git pull origin main # 3. 執(zhí)行合并命令將 feature 分支的更改合并到當(dāng)前分支main git merge feature # 4. 如果合并成功無(wú)沖突Git 可能會(huì)打開編輯器讓你輸入合并提交的信息保存退出即可。 # 5. 將合并后的結(jié)果推送到遠(yuǎn)程倉(cāng)庫(kù) git push origin main關(guān)鍵點(diǎn)解析步驟2的git pull至關(guān)重要。它相當(dāng)于git fetch獲取遠(yuǎn)程最新歷史 git merge origin/main將遠(yuǎn)程分支合并到本地。如果跳過(guò)這一步你的本地main可能落后于遠(yuǎn)程直接合并feature后再推送可能會(huì)被遠(yuǎn)程倉(cāng)庫(kù)拒絕或者導(dǎo)致歷史出現(xiàn)不必要的分叉和合并提交。合并的方向命令git merge feature永遠(yuǎn)是將feature分支的更改整合到當(dāng)前所在分支。一定要先通過(guò)git branch或git status確認(rèn)自己站在了正確的“接收方”分支上。4.2 常用選項(xiàng)與場(chǎng)景除了基礎(chǔ)的git merge branch這些選項(xiàng)能幫你處理更復(fù)雜的情況--no-commit執(zhí)行合并但即使成功也不自動(dòng)創(chuàng)建提交。這允許你在最終提交前檢查合并結(jié)果甚至做進(jìn)一步的修改。檢查無(wú)誤后需要手動(dòng)git commit。git merge feature --no-commit # ... 檢查代碼運(yùn)行測(cè)試 ... git commit -m Merge branch feature注意這個(gè)選項(xiàng)在合并解決沖突后會(huì)自動(dòng)生效因?yàn)闆_突解決后需要你手動(dòng)提交。--abort當(dāng)你發(fā)現(xiàn)合并過(guò)程一團(tuán)糟或者沖突太難解決想重來(lái)時(shí)這個(gè)命令是“后悔藥”。它會(huì)中止合并過(guò)程嘗試將倉(cāng)庫(kù)狀態(tài)恢復(fù)到合并開始之前。# 合并后出現(xiàn)沖突感覺處理不了 git merge --abort你的工作目錄和暫存區(qū)會(huì)回到執(zhí)行g(shù)it merge之前的狀態(tài)。--continue在手動(dòng)解決完所有合并沖突并且用git add將解決后的文件標(biāo)記為已解決后使用此命令來(lái)完成合并提交。# 解決完所有沖突文件后 git add . git merge --continue # 隨后會(huì)進(jìn)入編輯器填寫合并提交信息--squash這是一個(gè)非常有爭(zhēng)議但有時(shí)很有用的選項(xiàng)。它不會(huì)執(zhí)行真正的合并而是將目標(biāo)分支feature上的所有更改壓縮成一個(gè)差異集應(yīng)用到當(dāng)前分支的暫存區(qū)和工作目錄。你需要手動(dòng)進(jìn)行一次提交。git merge feature --squash git commit -m Squashed changes from feature branch效果主分支歷史上看不到feature分支的任何原始提交只有一個(gè)包含了所有功能變更的新提交。歷史極其清晰。代價(jià)完全丟失了feature分支的開發(fā)過(guò)程信息誰(shuí)、在什么時(shí)候、做了什么。不利于追溯和二分查找問(wèn)題。團(tuán)隊(duì)使用前需達(dá)成一致。4.3 合并遠(yuǎn)程分支合并遠(yuǎn)程分支比如同事推送到倉(cāng)庫(kù)的origin/feature-x和合并本地分支在本質(zhì)上沒有區(qū)別。通常的流程是將遠(yuǎn)程分支獲取到本地創(chuàng)建一個(gè)對(duì)應(yīng)的本地追蹤分支。合并這個(gè)本地追蹤分支。# 方法一先 fetch再合并遠(yuǎn)程追蹤分支 git fetch origin git merge origin/feature-x # 直接合并遠(yuǎn)程分支的引用 # 方法二創(chuàng)建本地分支并跟蹤遠(yuǎn)程分支然后合并本地分支 git fetch origin git checkout -b feature-x origin/feature-x # 創(chuàng)建并切換到本地 feature-x跟蹤遠(yuǎn)程 # ... 可能先在這個(gè)分支上做些檢查 ... git checkout main git merge feature-x第一種方法更直接但第二種方法讓你在合并前有一個(gè)本地的、可操作的分支副本有時(shí)更方便。5. 無(wú)法回避的挑戰(zhàn)合并沖突的完整解決手冊(cè)沖突是合并的常態(tài)而非異常。害怕沖突不如理解沖突。當(dāng) Git 無(wú)法自動(dòng)合并時(shí)它會(huì)在沖突文件中留下特殊的標(biāo)記并暫停合并過(guò)程等待你手動(dòng)解決。5.1 識(shí)別沖突狀態(tài)合并命令執(zhí)行后如果終端輸出包含CONFLICT (content): Merge conflict in file_name這樣的信息并且最后一行是Automatic merge failed; fix conflicts and then commit the result.說(shuō)明沖突發(fā)生了。此時(shí)git status命令是你最好的朋友。它會(huì)明確告訴你Unmerged paths哪些文件有沖突狀態(tài)是both modified。哪些文件已自動(dòng)合并成功等待被提交。5.2 沖突文件內(nèi)部解析Git 會(huì)在沖突文件中插入標(biāo)記格式如下 HEAD // 這是當(dāng)前分支ours的內(nèi)容 console.log(Hello from main branch); // 這是要合并進(jìn)來(lái)的分支theirs的內(nèi)容 console.log(Hello from feature branch); feature HEAD到之間是當(dāng)前分支你執(zhí)行g(shù)it merge時(shí)所在分支的代碼。到 feature之間是要合并進(jìn)來(lái)的分支feature的代碼。你的任務(wù)就是移除這些標(biāo)記并整合成一段正確的代碼。這可能意味著選擇其中一段也可能意味著將兩段邏輯融合。5.3 系統(tǒng)化的沖突解決流程面對(duì)沖突遵循一個(gè)清晰的流程可以避免混亂保持冷靜閱讀沖突不要急于修改。先仔細(xì)閱讀沖突標(biāo)記附近的代碼理解兩個(gè)分支分別做了什么修改意圖是什么。與同事溝通如果涉及如果沖突的修改來(lái)自另一位同事立刻溝通了解他修改的上下文共同商定解決方案。盲目選擇“我的”或“他的”版本都可能引入錯(cuò)誤。選擇解決策略接受當(dāng)前分支Ours完全采用HEAD部分的代碼。使用命令git checkout --ours file。接受傳入分支Theirs完全采用feature部分的代碼。使用命令git checkout --theirs file。手動(dòng)編輯融合大多數(shù)情況需要這個(gè)。刪除沖突標(biāo)記編輯成最終想要的代碼。標(biāo)記沖突已解決對(duì)每一個(gè)修改過(guò)的沖突文件使用git add file將其添加到暫存區(qū)。這告訴 Git“這個(gè)文件的沖突我已經(jīng)處理好了。”完成合并所有沖突文件都add之后執(zhí)行g(shù)it merge --continue或git commit來(lái)創(chuàng)建合并提交。5.4 使用圖形化工具與比較工具對(duì)于復(fù)雜的沖突純文本編輯效率很低??梢岳霉ぞ逫DE 集成VS Code, IntelliJ IDEA 等現(xiàn)代 IDE 都提供了優(yōu)秀的可視化沖突解決工具可以并排顯示兩個(gè)版本方便點(diǎn)選合并。專用合并工具如meld,kdiff3,Beyond Compare。配置 Git 使用它們git config --global merge.tool meld git config --global mergetool.prompt false發(fā)生沖突后運(yùn)行g(shù)it mergetool工具會(huì)自動(dòng)打開引導(dǎo)你解決每個(gè)沖突文件。5.5 一個(gè)真實(shí)的沖突解決案例假設(shè)在main分支和feature分支上都修改了utils.js文件中的同一個(gè)函數(shù)。沖突文件內(nèi)容function calculateTotal(items) { HEAD // main分支增加了折扣邏輯 let sum items.reduce((a, b) a b.price, 0); return sum * 0.9; // 9折 // feature分支修復(fù)了空數(shù)組的bug if (items.length 0) return 0; let sum items.reduce((a, b) a b.price, 0); return sum; feature }分析main分支增加了打折功能但沒處理空數(shù)組。feature分支修復(fù)了空數(shù)組 bug但沒打折。兩者都需要。手動(dòng)融合解決function calculateTotal(items) { // 融合兩個(gè)分支的修改先檢查空數(shù)組再計(jì)算打折 if (items.length 0) return 0; let sum items.reduce((a, b) a b.price, 0); return sum * 0.9; // 9折 }刪除沖突標(biāo)記保存文件。然后git add utils.js。6. 高級(jí)話題與避坑指南掌握了基本操作和沖突解決你已經(jīng)能應(yīng)對(duì) 90% 的場(chǎng)景。下面這些進(jìn)階知識(shí)和“坑點(diǎn)”能讓你在剩下的 10% 里游刃有余。6.1 合并無(wú)關(guān)的歷史--allow-unrelated-histories當(dāng)你嘗試合并兩個(gè)從完全不同的初始提交發(fā)展而來(lái)的分支時(shí)比如將一個(gè)舊項(xiàng)目導(dǎo)入到新的 Git 倉(cāng)庫(kù)作為分支Git 會(huì)拒絕合并提示fatal: refusing to merge unrelated histories。這是一種安全機(jī)制防止你誤操作。如果你確認(rèn)就是要合并這兩個(gè)獨(dú)立的歷史可以使用--allow-unrelated-histories選項(xiàng)強(qiáng)制合并。git merge other-branch --allow-unrelated-histories合并后歷史圖會(huì)顯示兩個(gè)原本獨(dú)立的根合并在一起。這種情況在項(xiàng)目初始化或大規(guī)模重構(gòu)時(shí)可能會(huì)遇到。6.2 合并策略的選擇我們前面討論的其實(shí)是默認(rèn)的recursive策略。Git 還有其他合并策略適用于特定場(chǎng)景通過(guò)-s選項(xiàng)指定resolve僅使用三路合并算法對(duì)于常規(guī)合并結(jié)果和recursive幾乎一樣。octopus用于一次性合并兩個(gè)以上的分支頭。默認(rèn)策略無(wú)法做到。如果合并多個(gè)分支時(shí)出現(xiàn)沖突octopus會(huì)直接失敗而recursive可以逐個(gè)處理。git merge -s octopus branch-a branch-b branch-cours這個(gè)策略很“霸道”。無(wú)論其他分支有什么修改合并結(jié)果完全采用當(dāng)前分支ours的版本。它會(huì)產(chǎn)生一個(gè)合并提交但內(nèi)容沒變。常用于“記錄我們合并過(guò)某個(gè)分支但拒絕其所有更改”的場(chǎng)景。git merge -s ours feature # 合并feature但最終代碼和合并前一模一樣6.3 撤銷一次合并合并提交一旦推送到遠(yuǎn)程共享分支撤銷就需要格外小心因?yàn)榭赡苡绊懰?。如果還在本地有幾種回退方法使用git reset如果合并剛剛完成還沒做其他操作可以用git reset --hard HEAD~1回退到合并前的狀態(tài)。警告這是破壞性操作會(huì)丟棄合并提交的所有更改。使用git revert這是更安全、更推薦的方式尤其對(duì)于已推送的合并。git revert會(huì)創(chuàng)建一個(gè)新的提交來(lái)抵消指定合并提交引入的更改。# 找到合并提交的哈希值 git log --oneline --graph # 假設(shè)合并提交是 a1b2c3d git revert -m 1 a1b2c3d-m 1表示我們要回退到合并提交的第一個(gè)父提交即合并前的當(dāng)前分支狀態(tài)。revert本身可能會(huì)產(chǎn)生新的沖突需要解決。6.4 預(yù)防沖突的最佳實(shí)踐與其擅長(zhǎng)解決沖突不如減少?zèng)_突發(fā)生頻繁合并主干在功能分支開發(fā)時(shí)定期例如每天將主分支develop或main合并到你的功能分支。這能讓沖突早點(diǎn)、小批量地暴露和解決避免最后集成時(shí)面對(duì)一個(gè)巨大的、難以理解的沖突。保持分支短小一個(gè)分支的生命周期和修改范圍越小與其他分支產(chǎn)生沖突的概率就越低。遵循“小步快跑”的原則。清晰的溝通團(tuán)隊(duì)內(nèi)對(duì)文件、模塊的職責(zé)有大致劃分修改公共模塊前在團(tuán)隊(duì)內(nèi)同步一下。使用.gitattributes文件對(duì)于二進(jìn)制文件如圖片、文檔或者總是希望接受特定版本的文件如鎖定的依賴配置文件package-lock.json可以設(shè)置合并策略。# .gitattributes *.png binary # 將png文件標(biāo)記為二進(jìn)制避免Git嘗試文本合并 package-lock.json mergeours # 合并時(shí)總是保留本地的版本7. 在 IDE 中執(zhí)行合并以 VS Code 和 IntelliJ IDEA 為例圖形化界面GUI能極大簡(jiǎn)化合并操作尤其是在解決沖突時(shí)。VS Code:在源代碼管理視圖CtrlShiftG你可以看到當(dāng)前分支并有一個(gè)“...”更多操作菜單。點(diǎn)擊“...”選擇“分支” - “合并分支...”然后從列表中選擇要合并進(jìn)來(lái)的分支。如果發(fā)生沖突VS Code 會(huì)在文件列表中標(biāo)記出沖突文件。點(diǎn)擊文件它會(huì)打開一個(gè)并排對(duì)比視圖清晰地顯示“當(dāng)前更改”HEAD和“傳入的更改”要合并的分支。你可以直接點(diǎn)擊按鈕選擇接受其中一個(gè)或者手動(dòng)編輯中間的結(jié)果面板。解決完所有沖突后點(diǎn)擊“暫存”按鈕或使用git add然后像平常一樣提交。IntelliJ IDEA:在右下角的 Git 分支小部件點(diǎn)擊會(huì)彈出分支列表。找到你想合并過(guò)來(lái)的分支右鍵選擇“Merge into Current”。IDEA 會(huì)執(zhí)行合并。如有沖突會(huì)彈出“Merge Revisions”對(duì)話框提供三窗格視圖本地、合并結(jié)果、遠(yuǎn)程。你可以清晰地對(duì)比和編輯。IDEA 還提供了非常強(qiáng)大的“Resolve Conflict”工具可以按塊Block解決沖突甚至智能分析代碼差異。解決后點(diǎn)擊“Apply”然后提交。個(gè)人體會(huì)對(duì)于簡(jiǎn)單的合并命令行高效直接。但對(duì)于復(fù)雜的、涉及多文件的沖突我強(qiáng)烈建議使用 IDE 的圖形化工具。它們的可視化對(duì)比和塊級(jí)操作能力能節(jié)省大量時(shí)間和腦力減少人為錯(cuò)誤。不過(guò)理解命令行背后的原理能讓你在使用 GUI 時(shí)更清楚每一步操作的意義。git merge遠(yuǎn)不止一個(gè)簡(jiǎn)單的命令它是 Git 分布式協(xié)作模型的基石。從理解三方合并的原理到區(qū)分快進(jìn)與非快進(jìn)策略再到系統(tǒng)化地解決沖突每一步都考驗(yàn)著我們對(duì)版本控制的理解。我最深刻的教訓(xùn)是不要害怕合并更不要回避沖突。把它們視為一種正常的、可管理的開發(fā)流程。通過(guò)頻繁地合并主干到特性分支保持分支短小精悍并善用工具合并可以變得平滑而可控。最終清晰、線性的提交歷史不是靠魔法變出來(lái)的而是靠每個(gè)團(tuán)隊(duì)成員對(duì)merge等命令的恰當(dāng)使用和共識(shí)達(dá)成的。