`git rebase--on--preserve merges`复制我在合并中已经解决的冲突

`git rebase--on--preserve merges`复制我在合并中已经解决的冲突,git,merge,rebase,git-rebase,Git,Merge,Rebase,Git Rebase,我有一个类似于下面的git历史记录,它试图让master的一个旧功能保持最新 * (origin/master, origin/HEAD, master) commit B' * commit Y * commit X | * (HEAD -> feature) commit D | * commit C | |\ | | * commit B | |/ |/| * | commit A * commit OLD Master指的是commit A,我不得不添加一

我有一个类似于下面的git历史记录,它试图让master的一个旧功能保持最新

* (origin/master, origin/HEAD, master) commit B'
* commit Y
* commit X
| * (HEAD -> feature) commit D
| *   commit C
| |\  
| | * commit B
| |/  
|/|   
* | commit A 
  * commit OLD
Master指的是commit A,我不得不添加一个大型修复程序,这会给旧的未集成的特性带来很多麻烦。我将该修复(一开始并不担心该功能)提交到提交B中。然后我将master合并到旧功能中,需要解决许多冲突。我在
功能
分支中再次提交,因为我稍微更新了该功能。所有的工作和建设,所以我把承诺B的主人。然而(通过gerrit),该提交在同时推送的两个新提交(X和Y)上被重定基础,并给出“重复的”提交B

我尝试使用命令在提交B'上重新设置合并的基础

git rebase --onto master B feature --preserve-merges
然而,我仍然存在着与合并a和提交C时相同的冲突。这是一个相当长的过程,我不希望重做这项工作。我该怎么做才能让我的历史看起来像这样

  * (HEAD -> feature) commit D'
  *   commit C'
 /|
* | (origin/master, origin/HEAD, master) commit B'
* | commit Y
* | commit X
* | commit A 
  * old commit OLD

谢谢你的帮助

这是很自然的,因为
git-rebase
必须复制提交,并且没有复制合并的常规方法(相对于
git-cherry-pick
复制非合并提交的常规方法)。因此,保留合并的git-rebase实际上会重新执行合并

如果您可以保证原始合并的三个输入树,即
B
OLD
,以及显示日志底部的合并基提交,我们无法看到它们与新合并的三个输入树相同,它们是
B'
OLD
,可能是相同的合并基数,但我们不能确定合并结果是否相同

然而,似乎
B'
的树与
B
的树不同,因为
B'
在其历史中也有
X
Y
B'
B
在它们的历史中都有
A
,因此如果
B
A
的进化,
B'
可能是
Y
的进化,这可能是
X
的进化,也可能是
A
的进化,制作
B'
时,其中包含
X
Y
的更改。因此,最终合并结果可能与原始合并结果不同。事实上,它可能或多或少应该是,“C中的是什么,但通过应用X和Y中的演化,就像通过cherry pick一样进行演化。”(最后一点是您可以在这里使用的一种策略的线索。)

不过,冲突很可能完全相同,合并每个冲突的结果很可能与上次完全相同。如果是这样的话,
gitrere
会做你想做的事情

这里有两条可用的路径 我们刚刚提到Git可以做两件事来帮助您。不过,出于不同的原因,两者都有点棘手

最好的可能是
git-rere
。这个内置于Git中的重用记录的解决方案可以在
Git合并
失败时保存冲突,然后当您修复冲突和
Git添加
已解决的版本和
Git提交
结果时,它也会保存它们的解决方案。(这两个步骤实际上发生在失败的合并之后,在最后的
git提交
期间)然后,在随后的
git合并
上,例如
git rebase--preserve
调用的具有冲突的新
git合并
,git会查看这些冲突是否已保存,记录的分辨率。如果是这样,Git将这些解决方案应用于这些冲突,只留下新的冲突需要手工解决

在这个特殊的软膏中,当冲突发生时,必须启用
git rere
,以便记录冲突,并且在添加和提交解决方案时仍然启用冲突。很明显,您当时没有打开此选项,否则您将不会提出此问题。:-)

我有一个想法,我从来没有测试过,但在逻辑上应该工作,为处理这个问题。或者,我们可以使用第二条路径,我现在将描述它。然后我将回到路径1,描述这个想法,您可以测试它

路径2:使用之前的结果,然后选择樱桃 避免重新执行所有冲突解决方案的第二种方法是从commit
B
获取树。假设您已经运行了
git-rebase-p
并且现在处于合并冲突状态,那么如果您运行
git-status
,您将看到一些未合并的文件和一些成功合并的文件。您需要解决冲突并提交,因此让我们通过删除每个文件立即解决所有冲突:

git rm -r -- .         # assumes you are in your top level dir
相当激烈,嗯?:-)但是现在,在不提交的情况下,让我们按照提交
C
中的格式提取每个文件:

git checkout <hash-of-C> -- .
现在,在索引和工作树中,有一个与
C
匹配的树,除了它还添加了
X
Y
的更改。我们可以
git-diff-HEAD
这样做,以确保它看起来不错,然后
git-commit
结果

路径1:
git-rere
git Reere的问题是,它在原始合并期间没有打开,因此git没有记录冲突及其解决方案。但请稍等片刻:在上面,我们刚刚看到了一种方法,通过这种方法,我们完全像以前一样重新进行了合并。我们所要做的就是重复原始合并并提交其原始结果

因此,我们首先通过结束进行中、失败的重新基准来结束进行中、失败的合并:

git rebase --abort
我们现在有了一个干净的索引和工作树,甚至还没有开始重基,这很好,因为
git-rebase-p
稍后将重复所有这些。我们哈
git rebase --abort
git checkout feature^^
git config rerere.enabled true
git merge feature^^2
git read-tree --reset -u feature^
git commit -m 'dummy merge for rerere'
git checkout feature
git rebase --onto master --preserve-merges B
git config rererere.enabled true
git checkout --detach <parent1>
git merge <parent2>
git read-tree --reset -u <merge>
git commit -m dummy
git checkout <where we were>
git checkout B
git checkout -b non-merge
git merge OLD
git add -u .
git commit -m 'Temporary commit to delete'
git diff non-merge..C > my_merge_modifs.patch
git checkout feature
git reset --hard OLD
git merge master
patch -p1 < my_merge_modifs.patch
git add -u .
git commit -m "This is commit C'"
git checkout D
git rebase C'
git branch -D non-merge