1  版权声明
2 起步
2.1  关于版本控制
2.2  Git 简史
2.3  Git 基础
2.4  命令行
2.5  安装 Git
2.6  初次运行Git前的配置
2.7  获取帮助
2.8  总结
3 Git 基础
3.1  获取 Git 仓库
3.2  记录每次更新到仓库
3.3  查看提交历史
3.4  撤消操作
3.5  远程仓库的使用
3.6  打标签
3.7  Git 别名
3.8  总结
4 Git 分支
4.1  分支简介
4.2  分支的新建与合并
4.3  分支管理
4.4  分支开发工作流
4.5  远程分支
4.6  变基
4.7  总结
5 服务器上的 Git
5.1  协议
5.2  在服务器上搭建 Git
5.3  生成 SSH 公钥
5.4  配置服务器
5.5  Git 守护进程
5.6  Smart HTTP
5.7  GitWeb
5.8  GitLab
5.9  第三方托管的选择
5.10  总结
6 分布式 Git
6.1  分布式工作流程
6.2  向一个项目贡献
6.3  维护项目
6.4  总结
7 GitHub
7.1  账户的创建和配置
7.2  对项目做出贡献
7.3  维护项目
7.4  管理组织
7.5  脚本 GitHub
7.6  总结
8 Git工具
8.1  选择修订版本
8.2  交互式暂存
8.3  储藏与清理
8.4  签署工作
8.5  搜索
8.6  重写历史
8.7  重置揭密
8.8  高级合并
8.9  Rerere
8.10  使用 Git 调试
8.11  子模块
8.12  打包
8.13  替换
8.14  凭证存储
8.15  总结
9 自定义Git
9.1  配置 Git
9.2  Git 属性
9.3  Git 钩子
9.4  使用强制策略的一个例子
10 Git与其他系统
10.1  作为客户端的 Git
10.2  迁移到 Git
10.3  总结
11 Git内部原理
11.1  底层命令和高层命令
11.2  Git 对象
11.3  Git 引用
11.4  包文件
11.5  引用规格
11.6  传输协议
11.7  维护与数据恢复
11.8  环境变量
11.9  总结

Rerere

2018-06-21 10:19:37
git
1463
最后编辑:GavinHsueh 于 2018-06-21 10:34:38

git rerere 功能是一个隐藏的功能。 正如它的名字 “reuse recorded resolution” 所指,它允许你让 Git 记住解决一个块冲突的方法,这样在下一次看到相同冲突时,Git 可以为你自动地解决它。

有几种情形下这个功能会非常有用。 在文档中提到的一个例子是如果你想要保证一个长期分支会干净地合并,但是又不想要一串中间的合并提交。 将 rerere 功能打开后偶尔合并,解决冲突,然后返回到合并前。 如果你持续这样做,那么最终的合并会很容易,因为 rerere 可以为你自动做所有的事情。

可以将同样的策略用在维持一个变基的分支时,这样就不用每次解决同样的变基冲突了。 或者你将一个分支合并并修复了一堆冲突后想要用变基来替代合并 - 你可能并不想要再次解决相同的冲突。

另一个情形是当你偶尔将一堆正在改进的特性分支合并到一个可测试的头时,就像 Git 项目自身经常做的。 如果测试失败,你可以倒回合并之前然后在去除导致测试失败的那个特性分支后重做合并,而不用再次重新解决所有的冲突。

为了启用 rerere 功能,仅仅需要运行这个配置选项:

$ git config --global rerere.enabled true

也通过在特定的仓库中创建 .git/rr-cache 目录来开启它,但是设置选项更干净并且可以应用到全局。

现在我们看一个简单的例子,类似之前的那个。 假设有一个像这样的文件:

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

同样(这并不是真的与 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"。

现在,让我们撤消那个合并然后将它变基到 master 分支顶部来替代它。可以通过使用之前在  重置揭密 看到的 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

也可以通过 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

我们将会在  高级合并 中看到这个的一个例子。 然而现在,让我们通过运行 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 功能可以帮助你的生活变得更美好。

发表评论
评论通过审核后显示。