# 處理合并沖突
對于很多人來說,合并時出現沖突是非常可怕的事,這就好像一不小心格式化了自己的硬盤一樣。在這一章節里我將為你消除這種恐懼。
## 你不會把事情搞砸
首先你應該記住,你總是可以撤銷一個合并操作,并且返回到沖突發生之前的狀態。也就是說,你永遠有機會放棄并重新開始。
如果你已經掌握了一些關于其它的版本控制系統的使用經驗,例如 Subversion ,你可能會很難過。因為在 Subversion 中處理沖突是被大家公認極為復雜而繁瑣的。這也就是為什么我們要使用 Git 的原因。簡單地說,它在這方面的工作原理是完全不同于 Subversion 的。Git 能夠在合并過程中顧及到很多方方面面的東西,從而為你創造一個比較簡單的方案來解決可能出現的沖突。
當然,沖突只會妨礙你自己的工作,它是不會涉及到整個團隊的項目倉庫。這是因為在 Git 中,沖突只可能發生在開發人員的本地計算機上,而不是在遠程服務器上。
## 什么是一個合并沖突
在 Git 中,“合并(merging)” 是在形式上整合別的分支到你當前的工作分支的操作。你需要得到在另外一個上下文背景下的改動(這就也就是我們所提到過的,一個有效的分支應該是建立在一個上下文工作背景上的),并且合并它們到你的當前的工作文件中來。
作為你的版本管理系統,Git 所帶來的最偉大的改善就是它讓合并操作變得非常輕松簡單。在大多數情況下,Git 會自己弄清楚該如何整合這些新來的變化。
當然,也存在極少數的情況,你必須自己手動地告訴 Git 該怎么做。最為常見的就是大家都改動了同一個文件。即便在這種情況下,Git 還是有可能自動地發現并解決掉這些沖突。但是,如果兩個人同時更改了同一個文件的同一行代碼,或者一個人改動了那些被另一個人刪除了的代碼,Git 就不能簡單地確定到底誰的改動才是正確的。這時 Git 會把這些地方標記為一個沖突,你必須首先解決掉這些沖突,然后再繼續你的工作。
## 如何解決合并沖突
當面對一個合并沖突時,我們首先要搞明白發生了什么。例如是不是你和你的同事都同時編輯了同一個文件的同一行代碼呢?是不是他刪除了一個你正在編輯的文件呢?是不是你們同時添加了一個相同文件名的文件呢?
當你使用 “git status” 時, Git 會告訴你存在一個 “未合并的路徑(unmerged paths)”,這只是用另外一個方式告訴你,存在一個或多個沖突:
```
$ git status
# On branch contact-form
# You have unmerged paths.
# (fix conflicts and run "git commit")
#
# Unmerged paths:
# (use "git add <file>..." to mark resolution)
#
# both modified: contact.html
#
no changes added to commit (use "git add" and/or "git commit -a")
```
就讓我們來深入地探討一下,如何去解決這些最常見的沖突。
當兩個改動發生在同一個文件的同一些行上,我們就要看看發生沖突的文件的內容了。Git 會非常友好地把文件中那些有問題的區域在 “<<<<<<< HEAD” 和 “>>>>>>> [other/branch/name]” 之間標記出來。

第一個標記后的內容源于當前分支。在尖括號之后,Git 會告訴我們這些改動是從哪里(哪個分支)來的。然后有沖突的改動會被 “=======” 分割起來。
現在我們的工作是要清理這些問題行。當我們完成這些清理后,這個文件應該看起來和我們預期的完全一樣。在過程中你也可能需要咨詢一下那個和你的代碼發生沖突的同事,從而更好地決定哪些改動才是最終正確的,哪些改動是需要被放棄掉的。可能是你的改動,也可能是他的,或者可能是你們兩個改動的組合。
打開一個比較原始的文件編輯器來清理這些沖突看起來是可行的,但是這樣并不簡單。使用一個專門的合并工具可以使這個操作變得更容易(如果你已經安裝了一個在你的本地計算機上……)。你可以通過 “git config” 命令來設置這個合并工具給 Git。更詳細的內容你就要查看這個工具的文檔說明了。
之后當發生合并沖突時,你可以使用 “git mergetool” 命令來調用這個工具。
例如,我在 Mac 上使用 “Kaleidoscope.app”:

在左邊和右邊的窗口會標記出那些改動的沖突。比起那些用符號 “<<<<<<<” 和 “>>>>>>>” 來標記沖突的方法來說,這是一個更加優雅的可視化環境。你可以非常方便地選擇哪個改動是需要被保留的。位于中間的窗口會顯示出處理后的結果,并且你也可以進一步手動編輯它。
現在,當清理文件并得到最終代碼后,所有剩下的工作就是將這個結果保存起來,并且馬上退出這個合并工具。這樣 Git 就會知道你已經完成了這個操作。Git 會在后臺對那個文件自動地執行 “git add” 命令。這也標志著沖突已經解決了。如果你_不_使用合并工具,而是手動在文本編輯器中清理這些沖突,你必須手動地將文件標記為已解決狀態(通過執行命令 “git add <filename>”)。
最終,當所有的沖突被解決后,你必須通過一個正常的提交操作來完成這個清理合并沖突的工作。
## 如何撤銷一個合并
你應該始終牢記,你可以在任何時間執行撤銷操作,并返回到你開始合并之前的狀態。要對自己有信心,你不會破壞項目中的任何東西。只要在命令行界面中鍵入 “git merge --abort” 命令,你的合并操作就會被安全的撤銷。
當你解決完沖突,并且在合并完成后發現一個錯誤,你仍然還是有機會來簡單地撤銷它。你只須要鍵入 “git reset --hard <commit-hash>” 命令,系統就會回滾到那個合并開始前的狀態,然后重新開始吧!</commit-hash>
- Learn Version Control with Git 中文版
- 前言
- Part 1 - 基礎知識
- 什么是版本控制?
- 為什么要使用版本控制系統?
- 準備工作
- 版本控制的基本工作流程
- 從一個未被納入版本控制的項目開始
- 從一個已被納入版本控制的項目開始
- 工作在你的項目上
- Part 2 - 分支與合并
- 分支可以改變你的生命
- 在分支上工作
- 暫時保存更改
- 切換一個本地分支
- 合并改動
- 分支的工作流程
- Part 3 - 遠程倉庫
- 關于遠程倉庫
- 連接一個遠程倉庫
- 查看遠程數據
- 整合遠程的改動
- 發布一個本地分支
- 刪除分支
- Part 4 - 高級應用
- 撤銷操作
- 用 diff 來檢查改動
- 處理合并沖突
- Rebase 代替合并
- 子模塊
- git-flow 的工作流程
- 使用 SSH 公鑰驗證
- Part 5 - 工具與服務
- 桌面應用程序
- 比較和整合工具
- 代碼托管服務
- 更多學習資源
- 附錄
- 版本控制的最佳實踐
- 命令 101
- 從 Subversion 過渡到 Git
- 為什么選擇 Git