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) 签署工作


谢谢

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