Git筆記(33) Rerere


1. 重用記錄的解決方案

git rerere 功能是一個隱藏的功能
正如它的名字“重用記錄的解決方案(reuse recorded resolution)”所示
它允許讓 Git 記住解決一個塊衝突的方法, 這樣在下一次看到相同衝突時,Git 可以爲你自動地解決它

要啓用 rerere 功能,只需運行以下配置選項即可:

$ git config --global rerere.enabled true

你也可以通過在特定的倉庫中創建 .git/rr-cache 目錄來開啓它
但是設置選項更乾淨並且可以應用到全局


2. 乾淨的合併和變基

想要保證一個長期分支會乾淨地合併
但是又不想要一串中間的合併提交弄亂你的提交歷史

rerere 功能開啓後,可以試着偶爾合併,解決衝突,然後退出合併
如果你持續這樣做,那麼最終的合併會很容易,因爲 rerere 可以爲自動做所有的事情

可以將同樣的策略用在維持一個變基的分支時
這樣就不用每次解決同樣的變基衝突了

或者你將一個分支合併並修復了一堆衝突後想要用變基來替代合併
可能並不想要再次解決相同的衝突

當偶爾將一堆正在改進的主題分支合併到一個可測試的頭時
如果測試失敗,可以倒回合併之前
然後在去除導致測試失敗的那個主題分支後重做合併
而不用再次重新解決所有的衝突


3. 舉例

假設有一個名爲 hello.rb 的文件如下:

#! /usr/bin/env ruby

def hello
  puts 'hello world'
end

在一個分支中修改單詞 “hello” 爲 “hola
然後在另一個分支中修改 “world” 爲 “mundo
在這裏插入圖片描述
當合並兩個分支到一起時,我們將會得到一個合併衝突:

$ 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

同樣可以使用 git 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' 來解決它
可以再次運行 git 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 add hello.rb
$ git commit
Recorded resolution for 'hello.rb'.
[master 68e16e5] Merge branch 'i18n'

可以看到它 “Recorded resolution for FILE
在這裏插入圖片描述

現在如果想撤消那個合併然後將它變基到 master 分支頂部來替代它
可以通過使用之前在 Git筆記(31) 重置揭密 看到的 git 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 這行
如果看這個文件,會發現它已經被解決了,而且在它裏面沒有合併衝突標記

#! /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

在這裏插入圖片描述
可以通過 git checkout 命令重新恢復到衝突時候的文件狀態:

$ git checkout --conflict=merge hello.rb
$ cat hello.rb
#! /usr/bin/env ruby

def hello
  puts 'hello mundo'
end

Git筆記(32) 高級合併 中已看過這個例子
然而現在,通過運行 git 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 功能可以幫助你的工作


參考: git
以上內容,均根據git官網介紹刪減、添加和修改組成


相關推薦:

Git筆記(32) 高級合併
Git筆記(31) 重置揭密
Git筆記(30) 重寫歷史
Git筆記(29) 搜索
Git筆記(28) 簽署工作


謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章