## Rerere
`git rerere`?功能是一個隱藏的功能。 正如它的名字 “reuse recorded resolution” 所指,它允許你讓 Git 記住解決一個塊沖突的方法,這樣在下一次看到相同沖突時,Git 可以為你自動地解決它。
有幾種情形下這個功能會非常有用。 在文檔中提到的一個例子是如果你想要保證一個長期分支會干凈地合并,但是又不想要一串中間的合并提交。 將?`rerere`?功能打開后偶爾合并,解決沖突,然后返回到合并前。 如果你持續這樣做,那么最終的合并會很容易,因為?`rerere`?可以為你自動做所有的事情。
可以將同樣的策略用在維持一個變基的分支時,這樣就不用每次解決同樣的變基沖突了。 或者你將一個分支合并并修復了一堆沖突后想要用變基來替代合并 - 你可能并不想要再次解決相同的沖突。
另一個情形是當你偶爾將一堆正在改進的特性分支合并到一個可測試的頭時,就像 Git 項目自身經常做的。 如果測試失敗,你可以倒回合并之前然后在去除導致測試失敗的那個特性分支后重做合并,而不用再次重新解決所有的沖突。
為了啟用?`rerere`?功能,僅僅需要運行這個配置選項:
~~~
$ git diff-tree -p rack_remote/master
~~~
也通過在特定的倉庫中創建?`.git/rr-cache`?目錄來開啟它,但是設置選項更干凈并且可以應用到全局。
現在我們看一個簡單的例子,類似之前的那個。 假設有一個像這樣的文件:
~~~
#! /usr/bin/env ruby
def hello
puts 'hello world'
end
~~~
在一個分支中修改單詞 “hello” 為 “hola”,然后在另一個分支中修改 “world” 為 “mundo”,就像之前一樣。

Figure 7-25.
當合并兩個分支到一起時,我們將會得到一個合并沖突:
~~~
$ git merge i18n-world
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Recorded preimage for 'hello.rb'
Automatic merge failed; fix conflicts and then commit the result.
~~~
你會注意到那個新行?`Recorded preimage for FILE`。 除此之外它應該看起來就像一個普通的合并沖突。 在這個時候,`rerere`?可以告訴我們幾件事。 和往常一樣,在這個時候你可以運行`git status`?來查看所有沖突的內容:
~~~
$ git status
# On branch master
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add <file>..." to mark resolution)
#
# both modified: hello.rb
#
~~~
然而,`git rerere`?也會通過?`git rerere status`?告訴你它記錄的合并前狀態。
~~~
$ git rerere status
hello.rb
~~~
并且?`git rerere diff`?將會顯示解決方案的當前狀態 - 開始解決前與解決后的樣子。
~~~
$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,11 @@
#! /usr/bin/env ruby
def hello
-<<<<<<<
- puts 'hello mundo'
-=======
+<<<<<<< HEAD
puts 'hola world'
->>>>>>>
+=======
+ puts 'hello mundo'
+>>>>>>> i18n-world
end
~~~
同樣(這并不是真的與?`rerere`?有關系),可以使用?`ls-files -u`?來查看沖突文件的之前、左邊與右邊版本:
~~~
$ git ls-files -u
100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1 hello.rb
100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2 hello.rb
100644 54336ba847c3758ab604876419607e9443848474 3 hello.rb
~~~
現在可以通過改為?`puts 'hola mundo'`?來解決它,可以再次運行?`rerere diff`?命令來查看 rerere 將會記住的內容:
~~~
$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,7 @@
#! /usr/bin/env ruby
def hello
-<<<<<<<
- puts 'hello mundo'
-=======
- puts 'hola world'
->>>>>>>
+ puts 'hola mundo'
end
~~~
所以從本質上說,當 Git 看到一個?`hello.rb`?文件的一個塊沖突中有 “hello mundo” 在一邊與 “hola world” 在另一邊,它會將其解決為 “hola mundo”。
現在我們可以將它標記為已解決并提交它:
~~~
$ git add hello.rb
$ git commit
Recorded resolution for 'hello.rb'.
[master 68e16e5] Merge branch 'i18n'
~~~
可以看到它 "Recorded resolution for FILE"。

Figure 7-26.
現在,讓我們撤消那個合并然后將它變基到 master 分支頂部來替代它。可以通過使用之前在?[重置揭密](http://git-scm.com/book/zh/v2/ch00/_git_reset)?看到的?`reset`?來回滾分支。
~~~
$ git reset --hard HEAD^
HEAD is now at ad63f15 i18n the hello
~~~
我們的合并被撤消了。 現在讓我們變基特性分支。
~~~
$ git checkout i18n-world
Switched to branch 'i18n-world'
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: i18n one word
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Resolved 'hello.rb' using previous resolution.
Failed to merge in the changes.
Patch failed at 0001 i18n one word
~~~
現在,正像我們期望的一樣,得到了相同的合并沖突,但是看一下?`Resolved FILE using previous resolution`?這行。 如果我們看這個文件,會發現它已經被解決了,而且在它里面沒有合并沖突標記。
~~~
$ cat hello.rb
#! /usr/bin/env ruby
def hello
puts 'hola mundo'
end
~~~
同樣,`git diff`?將會顯示出它是如何自動地重新解決的:
~~~
$ git diff
diff --cc hello.rb
index a440db6,54336ba..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
#! /usr/bin/env ruby
def hello
- puts 'hola world'
- puts 'hello mundo'
++ puts 'hola mundo'
end
~~~

Figure 7-27.
也可以通過?`checkout`?命令重新恢復到沖突時候的文件狀態:
~~~
$ git checkout --conflict=merge hello.rb
$ cat hello.rb
#! /usr/bin/env ruby
def hello
<<<<<<< ours
puts 'hola world'
=======
puts 'hello mundo'
>>>>>>> theirs
end
~~~
我們將會在?[高級合并](http://git-scm.com/book/zh/v2/ch00/_advanced_merging)?中看到這個的一個例子。 然而現在,讓我們通過運行?`rerere`?來重新解決它:
~~~
$ git rerere
Resolved 'hello.rb' using previous resolution.
$ cat hello.rb
#! /usr/bin/env ruby
def hello
puts 'hola mundo'
end
~~~
我們通過?`rerere`?緩存的解決方案來自動重新解決了文件沖突。 現在可以添加并繼續變基來完成它。
~~~
$ git add hello.rb
$ git rebase --continue
Applying: i18n one word
~~~
所以,如果做了很多次重新合并,或者想要一個特性分支始終與你的 master 分支保持最新但卻不想要一大堆合并,或者經常變基,打開?`rerere`?功能可以幫助你的生活變得更美好。
- 前言
- Scott Chacon 序
- Ben Straub 序
- 獻辭
- 貢獻者
- 引言
- 1. 起步
- 1.1 關于版本控制
- 1.2 Git 簡史
- 1.3 Git 基礎
- 1.4 命令行
- 1.5 安裝 Git
- 1.6 初次運行 Git 前的配置
- 1.7 獲取幫助
- 1.8 總結
- 2. Git 基礎
- 2.1 獲取 Git 倉庫
- 2.2 記錄每次更新到倉庫
- 2.3 查看提交歷史
- 2.4 撤消操作
- 2.5 遠程倉庫的使用
- 2.6 打標簽
- 2.7 Git 別名
- 2.8 總結
- 3. Git 分支
- 3.1 分支簡介
- 3.2 分支的新建與合并
- 3.3 分支管理
- 3.4 分支開發工作流
- 3.5 遠程分支
- 3.6 變基
- 3.7 總結
- 4. 服務器上的 Git
- 4.1 協議
- 4.2 在服務器上搭建 Git
- 4.3 生成 SSH 公鑰
- 4.4 配置服務器
- 4.5 Git 守護進程
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管的選擇
- 4.10 總結
- 5. 分布式 Git
- 5.1 分布式工作流程
- 5.2 向一個項目貢獻
- 5.3 維護項目
- 5.4 總結
- 6. GitHub
- 6.1 賬戶的創建和配置
- 6.2 對項目做出貢獻
- 6.3 維護項目
- 6.4 管理組織
- 6.5 腳本 GitHub
- 6.6 總結
- 7. Git 工具
- 7.1 選擇修訂版本
- 7.2 交互式暫存
- 7.3 儲藏與清理
- 7.4 簽署工作
- 7.5 搜索
- 7.6 重寫歷史
- 7.7 重置揭密
- 7.8 高級合并
- 7.9 Rerere
- 7.10 使用 Git 調試
- 7.11 子模塊
- 7.12 打包
- 7.13 替換
- 7.14 憑證存儲
- 7.15 總結
- 8. 自定義 Git
- 8.1 配置 Git
- 8.2 Git 屬性
- 8.3 Git 鉤子
- 8.4 使用強制策略的一個例子
- 8.5 總結
- 9. Git 與其他系統
- 9.1 作為客戶端的 Git
- 9.2 遷移到 Git
- 9.3 總結
- 10. Git 內部原理
- 10.1 底層命令和高層命令
- 10.2 Git 對象
- 10.3 Git 引用
- 10.4 包文件
- 10.5 引用規格
- 10.6 傳輸協議
- 10.7 維護與數據恢復
- 10.8 環境變量
- 10.9 總結
- A. 其它環境中的 Git
- A1.1 圖形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Eclipse 中的 Git
- A1.4 Bash 中的 Git
- A1.5 Zsh 中的 Git
- A1.6 Powershell 中的 Git
- A1.7 總結
- B. 將 Git 嵌入你的應用
- A2.1 命令行 Git 方式
- A2.2 Libgit2
- A2.3 JGit
- C. Git 命令
- A3.1 設置與配置
- A3.2 獲取與創建項目
- A3.3 快照基礎
- A3.4 分支與合并
- A3.5 項目分享與更新
- A3.6 檢查與比較
- A3.7 調試
- A3.8 補丁
- A3.9 郵件
- A3.10 外部系統
- A3.11 管理
- A3.12 底層命令