举一个简单的例子,假设我们有一个repo,其中一个不正确的行被添加到文件a
。
$ git init
Initialized empty Git repository in /someplace/.git/
$ echo "a1\nb2" > a
$ echo b1 > b
$ git add .
$ git commit -m init
[master (root-commit) 917f3b9] init
2 files changed, 3 insertions(+)
create mode 100644 a
create mode 100644 b
分支master
然后分叉新的分支f
。
$ git branch f
在master
中,我们然后在不正确的文件中添加另一行。
$ echo b3 >> a
$ git commit -am 'added b3 to a'
[master e44bc9f] added b3 to a
1 file changed, 1 insertion(+)
在分支f
,我们做一些无关的事情,提交它,
$ git checkout f
$ echo c1 > c
$ git add .
$ git commit -m "added c"
1 label onto
[f 7cb63b2] added c
1 file changed, 1 insertion(+)
create mode 100644 c
然后我们将错误放置在文件a中的行移动到文件b。
$ echo b2 >> b
$ sed -i '/b2/d' a
$ git commit -am "moved b2 to where it really belongs"
[f 32cee76] moved b2 to where it really belongs
2 files changed, 1 insertion(+), 1 deletion(-)
然后我们尝试将master
合并到f
,但遇到了master
添加了另一条不正确的线而f
删除了第一条线的冲突。
$ git merge master
Auto-merging a
CONFLICT (content): Merge conflict in a
Automatic merge failed; fix conflicts and then commit the result.
$ cat a
a1
<<<<<<< HEAD
=======
b2
b3
>>>>>>> master
要解决冲突,我们将第二行不正确的行移动到正确的文件,然后提交合并。
$ echo b3 >> b
$ sed -i '/HEAD/,$d' a
$ git commit -a --no-edit
[f 28d8543] Merge branch 'master' into f
然后,我们意识到关于c
的提交不应该在这个分支中,所以我们用rebase -ir
“删除”它并遇到与之前相同的冲突。
$ git rebase -ir $(git merge-base master @) # remove the commit about c
Auto-merging a
CONFLICT (content): Merge conflict in a
Could not apply 28d8543... onto # Merge branch 'master' into f
$ cat a
a1
<<<<<<< HEAD
=======
b2
b3
>>>>>>> refs/rewritten/onto
那么,考虑到在冲突中没有出现的b
添加的内容,这里最简单,最有效和最通用的解决方案是重做以前的解决方案吗?
有一个git实用程序用于记录冲突的解决方式,以便您以后可以重放。它被称为git rerere
。
它真的意味着特定的用例 - 并且由于该用例是基于我不同意的假设,我几乎从不使用git rerere
。
我还要说,如果没有一个良好的工作流程,你所描述的场景应该是不常见的,所以我个人不会开始使用rerere
记录每一个冲突解决方案,只是因为我以后再次需要它。
但是......因为那是你提出的问题,git rerere
可能是“答案”。
到目前为止想到的最佳解决方案(实际上并不是那么好)是放弃git并制作合并冲突解决方案的补丁:
$ cp -a . ../$(basename $PWD)~
$ echo b3 >> b
$ sed -i '/HEAD/,$d' a
$ diff -ru ../$(basename $PWD)~ .
diff -ru ../t~/a ./a
--- ../t~/a 2019-03-08 12:54:34.701197573 -0800
+++ ./a 2019-03-08 12:54:56.761197321 -0800
@@ -1,6 +1 @@
a1
-<<<<<<< HEAD
-=======
-b2
-b3
->>>>>>> refs/rewritten/onto
diff -ru ../t~/b ./b
--- ../t~/b 2019-03-08 12:54:34.701197573 -0800
+++ ./b 2019-03-08 12:54:54.044530686 -0800
@@ -1,2 +1,3 @@
b1
b2
+b3
我可以在某处保存该补丁,并在需要再次解决此问题时应用它。这样做的问题是管理这些补丁,并且当我需要重做冲突解决方案或者总是对每个冲突解决方案进行修补时,要么预测这个补丁。
不过,我更喜欢使用git来管理它的解决方案。
git rerere
或制作refs/rewritten/onto
补丁是必要的,因为,在Git 2.23中,refs/rewritten/onto
将在git rebase -ir
中止的特定情况下被移除。
“git rebase --abort
”在结束“refs/rewritten/
”时曾经离开过git rebase -r
,这已被纠正。
参见commit d559f50的commit 37e9ee5,commit d3fce47,commit 7372eae,Phillip Wood (phillipwood
)(2019年5月14日)。
(Junio C Hamano -- gitster
--在commit 88f95e4合并,2019年7月9日)
rebase --abort/--quit
:清理refs/rewritten
当
rebase -r
完成时,它会移除它创建的refs/rewritten
下的任何引用。 但是,如果用户中止或退出,则不删除rebase引用。这可能会导致未来的退货问题。例如,我最近想将主题分支的更新版本合并到集成分支中,因此运行
rebase -ir
并从todo列表中删除主题分支的选择和标签,以便merge -C <old-merge> topic
会拿起新版主题。 不幸的是,
refs/rewritten/topic
已经存在于先前已被中止的rebase中,因此rebase只使用了旧主题,而不是新主题。非交互式退出案例的逻辑已更改,以确保始终释放
buf
。