## 在合并過程中得到解決沖突的協助
git會把所有可以自動合并的修改加入到索引中去, 所以[git diff](http://www.kernel.org/pub/software/scm/git/docs/git-diff.html)只會顯示有沖突的部分. 它使用了一種不常見的語法:
~~~
$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,5 @@@
++<<<<<<< HEAD:file.txt
+Hello world
++=======
+ Goodbye
++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
~~~
回憶一下, 在我們解決沖突之后, 得到的提交會有兩個而不是一個父提交: 一個父提交會成為HEAD, 也就是當前分支的tip; 另外一個父提交會成為另一分支的tip, 被暫時存在MERGE_HEAD。
在合并過程中, 索引中保存著每個文件的三個版本. 三個"文件暫存(file stage)"中的每一個都代表了文件的不同版本:
~~~
$ git show :1:file.txt # 兩個分支共同祖先中的版本.
$ git show :2:file.txt # HEAD中的版本.
$ git show :3:file.txt # MERGE_HEAD中的版本.
~~~
當你使用[git diff](http://www.kernel.org/pub/software/scm/git/docs/git-diff.html)去顯示沖突時, 它在工作樹(work tree), 暫存2(stage 2)和暫存3(stage 3)之間執行三路diff操作, 只顯示那些兩方都有的塊(換句話說, 當一個塊的合并結果只從暫存2中得到時, 是不會被顯示出來的; 對于暫存3來說也是一樣)。
上面的diff結果顯示了file.txt在工作樹, 暫存2和暫存3中的差異. git不在每行前面加上單個'+'或者'-', 相反地, 它使用兩欄去顯示差異: 第一欄用于顯示第一個父提交與工作目錄文件拷貝的差異, 第二欄用于顯示第二個父提交與工作文件拷貝的差異. (參見[git diff-files](http://www.kernel.org/pub/software/scm/git/docs/git-diff-files.html)中的"COMBINED DIFF FORMAT"取得此格式詳細信息.)
在用直觀的方法解決沖突之后(但是在更新索引之前), diff輸出會變成下面的樣子:
~~~
$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,1 @@@
- Hello world
-Goodbye
++Goodbye world
~~~
上面的輸出顯示了解決沖突后的版本刪除了第一個父版本提供的"Hello world"和第二個父版本提供的"Goodbye", 然后加入了兩個父版本中都沒有的"Goodbye world"。
一些特別diff選項允許你對比工作目錄和三個暫存中任何一個的差異:
~~~
$ git diff -1 file.txt # 與暫存1進行比較
$ git diff --base file.txt # 與上相同
$ git diff -2 file.txt # 與暫存2進行比較
$ git diff --ours file.txt # 與上相同
$ git diff -3 file.txt # 與暫存3進行比較
$ git diff --theirs file.txt # 與上相同.
~~~
[git log](http://www.kernel.org/pub/software/scm/git/docs/git-log.html)和[gitk](http://www.kernel.org/pub/software/scm/git/docs/gitk.html)命令也為合并操作提供了特別的協助:
~~~
$ git log --merge
$ gitk --merge
~~~
這會顯示所有那些只在HEAD或者只在MERGE_HEAD中存在的提交, 還有那些更新(touch)了未合并文件的提交.
你也可以使用[git mergetool](http://www.kernel.org/pub/software/scm/git/docs/git-mergetool.html), 它允許你使用外部工具如emacs或kdiff3去合并文件。
每次你解決沖突之后, 應該更新索引:
~~~
$ git add file.txt
~~~
完成索引更新之后, git-diff(缺省地)不再顯示那個文件的差異, 所以那個文件的不同暫存版本會被"折疊"起來。
## 多路合并
你可以一次合并多個頭, 只需簡單地把它們作為[git merge](http://www.kernel.org/pub/software/scm/git/docs/git-merge.html)的參數列出. 例如,
~~~
$ git merge scott/master rick/master tom/master
~~~
相當于:
~~~
$ git merge scott/master
$ git merge rick/master
$ git merge tom/master
~~~
## 子樹
有時會出現你想在自己項目中引入其他獨立開發項目的內容的情況. 在沒有路徑沖突的前提下, 你只需要簡單地從其他項目拉取內容即可。
如果有沖突的文件, 那么就會出現問題. 可能的例子包括Makefile和其他一些標準文件名. 你可以選擇合并這些沖突的文件, 但是更多的情況是你不愿意把它們合并. 一個更好解決方案是把外部項目作為一個子目錄進行合并. 這種情況不被遞歸合并策略所支持, 所以簡單的拉取是無用的。
在這種情況下, 你需要的是子樹合并策略。
這下面例子中, 我們設定你有一個倉庫位于/path/to/B (如果你需要的話, 也可以是一個URL). 你想要合并那個倉庫的master分支到你當前倉庫的dir-B子目錄下。
下面就是你所需要的命令序列:
~~~
$ git remote add -f Bproject /path/to/B (1)
$ git merge -s ours --no-commit Bproject/master (2)
$ git read-tree --prefix=dir-B/ -u Bproject/master (3)
$ git commit -m "Merge B project as our subdirectory" (4)
$ git pull -s subtree Bproject master (5)
~~~
子樹合并的好處就是它并沒有給你倉庫的用戶增加太多的管理負擔. 它兼容于較老(版本號小于1.5.2)的客戶端, 克隆完成之后馬上可以得到代碼。
然而, 如果你使用子模塊(submodule), 你可以選擇不傳輸這些子模塊對象. 這可能在子樹合并過程中造成問題。
> 譯者注: submodule是Git的另一種將別的倉庫嵌入到本地倉庫方法.
另外, 若你需要修改內嵌外部項目的內容, 使用子模塊方式可以更容易地提交你的修改.
- 1. 介紹
- 歡迎使用Git
- GIT對象模型
- Git目錄 與 工作目錄
- Git索引
- 2. 第一步
- 安裝Git
- 安裝與初始化
- 3. 基本用法
- 獲得一個Git倉庫
- 正常的工作流程
- 分支與合并@基礎
- 查看歷史 -Git日志
- 比較提交 - Git Diff
- 分布式的工作流程
- Git標簽
- 4. 中級技能
- 忽略某些文件
- rebase
- 交互式rebase
- 交互式添加
- 儲藏
- Git樹名
- 追蹤分支
- 使用Git Grep進行搜索
- Git的撤消操作 - 重置, 簽出 和 撤消
- 維護Git
- 建立一個公共倉庫
- 建立一個私有倉庫
- 5. 高級技能
- 創建新的空分支
- 修改你的歷史
- 高級分支與合并
- 查找問題的利器 - Git Bisect
- 查找問題的利器 - Git Blame
- Git和Email
- 定制Git
- Git Hooks
- 找回丟失的對象
- 子模塊
- 6. Git生態體系
- Git 與之 Windows
- 使用Git進行系統部署
- 與 Subversion 集成
- 從其他代碼管理工具遷移到Git
- 圖形化的Git
- Git倉庫托管
- Git的其它用法
- Git的腳本支持
- Git 與編輯器
- 7. 原理解析
- Git是如何存儲對象的
- 查看Git對象
- Git引用
- Git索引
- 打包文件
- 更底層的Git
- 傳輸協議
- 術語表