使用git和meld进行交互式重新绑定的3路合并中的三个文件是什么? [英] What are the three files in a 3-way merge for interactive rebasing using git and meld?

查看:164
本文介绍了使用git和meld进行交互式重新绑定的3路合并中的三个文件是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我使用 git rebase -i 进行交互式转化。如果出现一些冲突,我可能会遇到合并冲突并要求进行三方合并。使用 meld ,我会看到三个窗口: LOCAL (左),(中)和 REMOTE (右)。这里 ??? 我的意思是, meld 不会提供一些特殊的名字来追加到文件中。 / p>

在正常合并过程中,这是有道理的,因为中间是共同的祖先,并且您将本地和远程更改合并到该祖先。然而,在交互式rebase期间,这似乎并不是这种情况 - 目前还不清楚每个文件代表什么。



3路合并中的这些文件分别代表互动式底线?在编辑这些文件时,我的目标是什么?



更新:根据我看到的评论和实验:




  • 左侧( LOCAL ):您在本次执行的重放序列中的文件本地版本。

  • 正确( REMOTE ):最初应用当前提交后的文件状态。 $ b 中间:原始提交序列中右侧的父代。



我的任务是确定从从中间到右侧,然后将此增量应用于左侧。在新的提交序列中应用当前提交增量后,中间应该反映文件的状态。



请注意,此配置似乎特定于meld,至少在一定程度上。对于其他编辑器,Git的3路合并行为可能会有所不同。

解决方案

中间版本是合并基础,就像 git merge



(名称other可能比remote更合适,因为没有要求合并的另一边是远程的,因为Mercurial一直使用other这个名字,而不是Git需要与Mercurial相匹配,但是一些一致性可能会很好。请注意,Git使用名称我们和他们的 这里也是如此,所以我们永远不会从Git获得100%的一致性:-))



但是等等,怎么会有一个合并基础?



总是有一个合并基础。



通常我们甚至不需要找到它当作为补丁处理时(不尝试三路合并),每个补丁都可以干净地应用。但是有时候这个补丁不能很好地应用,我们必须回退到三路合并。



(顺便提一下,你可以禁用此后备功能,请参阅 - 3way - no-3way am .threeWay git-虽然这里链接的页面已经过期,因为这些控件最近发生了变化。)

  $ git rebase -i 
pick aaaaaaa first commit
pick bbbbbbb second commit
pick ccccccc third commit

让我们也画出提交图,这样我们就可以看到我们正在重定位的内容,并且:

  A  -  B  -  C < - 分行
/
... - o - *
\
G - H < - 原产地/分行

我们会挑选提交 A B C A = aaaaaaa 等),这样我们就可以得到结果如下:

  A  -  B  -  C [弃用] 
/
.. - - - A' - B' - C'< - 分支
\ /
G - H < - 原产地/分支

让我们仔细看看 A 的第一个选择。



比较(差异) A 与它的父母,它是提交 * ,并尝试将结果差异应用于提交 H



提交 H 已经从提交 * 有些偏离。事实上,我们可以在 A H 之间找到合并基础,它是... commit * 。这实际上是一个相当不错的合并基础,但最好是Git可以按原样应用修补程序,而不必回退到三路合并代码。



<因此,在选择 A H时,提交 * / code>。合并完成后,我们得到新的提交 A'。 (例如,它的新SHA-1 ID可能是 aaaaaa1 ,可能不是,我们只是称它为 A'。)



现在我们将选择 B 。这不同于 B 与它的父元素不同,它是 A ,并试图将diff应用于 A'



提交 A' code> B 。事实上,我们可以在 B A'之间找到合并基础,那就是... commit * 。不幸的是,这是一个可怜的合并基地。幸运的是,如果补丁无法按原样应用,并且通常可以,Git只会回退它。但如果它不能, Git会比较 * vs B * vs A'并尝试合并这两个差异。请注意, * vs B 合并了我们在 A ,但 * vs A'也包含所有这些相同的 A 更改,所以如果我们幸运的话,Git会注意到已经包含的更改并且不会重复它们。 编辑 Git秘技。 (尽管总体策略保持不变,但这个代码最近在2.6版中已经发生了变化。)



考虑 git diff 用于仅显示从提交 A 到提交 B 的更改。这包括索引行:

  diff --git a / foo b / foo 
index f0b98f8..0ea3286 100644

左边的值是文件 foo 在提交 A 中的版本的缩写)。右边的值是提交 B 中文件版本的哈希。



Git只是假装来自左侧散列的合并基础。换句话说,提交 A 中的文件版本将成为伪造的合并库。 (Git将 - build-fake-ancestor 传递给 git apply ,这就要求特定的文件blob对象在但是它们是在commit A 中的,因为对于通过电子邮件发布的补丁,Git使用相同的代码,但blob可能存在也可能不存在。)



请注意,当挑选提交 A 时,Git实际上也会这样做,但这次合并基本文件是版本从提交 * ,这真的是合并基础。

最后,我们樱桃 - 选择 C 。这个区别在于 B vs C ,就像我们区分 A vs B 上次。如果我们可以按原样应用该补丁,那么可以;如果不是,我们再次使用commit * 作为合并基础。这又是一个非常糟糕的合并基地。与以前一样,假装 B 中的版本是常见的基础。



顺便说一句,这也解释了为什么你倾向于一次又一次地看到相同的合并冲突:我们每次都使用相同的合并基础。 (启用 git rerere 可以提供帮助。)


Let's say I do an interactive rebase with git rebase -i. If some conflict arises I might be presented with a merge conflict and asked to do a 3-way merge. Using meld, I am presented with three windows: LOCAL (left), ??? (middle), and REMOTE (right). Here by ??? I mean simply that meld doesn't provide some special name to append to the file.

During a normal merge this makes sense, since the middle is the common ancestor and you are merging the local and remote changes to that ancestor. However this does not seem to be the case during an interactive rebase - it's unclear what each file represents.

What do these files in the 3-way merge each represent during an interactive rebase? And when editing these files, what is my goal?

Update: Based on the comments and experiments I'm seeing:

  • Left (LOCAL): Your local version of the file at this point in the commit replay sequence.
  • Right (REMOTE): The state of the file just after the current commit was originally applied.
  • Middle: The parent of the right in the original commit sequence.

My task is thus to determine the delta from Middle to Right, and then apply this delta to the Left. The Middle should reflect the state of the file after the current commit delta is applied in the new commit sequence.

Note that this configuration appears to be specific to meld, at least to some degree. Git's 3-way merge behavior may differ for other editors.

解决方案

The middle version is the merge base, just as with a git merge.

(The name "other" might be more appropriate than "remote" since there is no requirement that the other side of a merge be a remote, and since Mercurial consistently uses the name "other" for it, not that Git needs to match Mercurial, but some consistency might be nice. Note that Git uses the names "ours" and "theirs" here as well, so we will never get 100% consistency from Git. :-) )

But wait, how is there a merge base?

There is always a merge base.

Usually we don't even have to find it as each patch applies cleanly when treated as a patch (without attempting a three-way merge). But sometimes the patch won't apply cleanly, and we do have to fall back to the three-way merge.

(Incidentally, you can disable this fallback. See --3way, --no-3way, and am.threeWay in the git-am documentation, though the page linked here is already out of date since these controls changed recently.)

$ git rebase -i
pick aaaaaaa first commit
pick bbbbbbb second commit
pick ccccccc third commit

Let's draw the commit graph, too, so we can see what we are rebasing from and to:

              A - B - C   <-- branch
            /
... - o - *
            \
              G - H       <-- origin/branch

We'll be cherry-picking commits A, B, and C (A = aaaaaaa, etc) so that we get this result, in the end:

              A - B - C   [abandoned]
            /
... - o - *           A' - B' - C'   <-- branch
            \       /
              G - H       <-- origin/branch

Let's look closely at the first cherry-pick, of A.

This compares (diffs) A against its parent, which is commit *, and attempts to apply the resulting diff to commit H.

Commit H, however, has drifted somewhat from commit *. In fact, we can find a merge base between A and H, and it is ... commit *. This is actually a pretty decent merge-base, though it's best if Git can just apply the patch as-is, without having to fall back to the three-way merge code.

So, commit * is the merge base when cherry-picking A onto H. When the merge is done we get new commit A'. (Its new SHA-1 ID might be aaaaaa1 for instance. Probably not; let's just call it A'.)

Now we'll cherry-pick B. This diffs B versus its parent, which is A, and attempts to apply the diff to A'.

Commit A', however, has drifted somewhat from commit B. In fact, we can find a merge base between B and A', and that is ... commit * again. Unfortunately, this is a wretched merge base. Fortunately, Git only falls back on it if the patch cannot be applied as-is, and usually it can. But if it can't, Git will diff * vs B and * vs A' and try to merge those two diffs. Note that * vs B incorporates all of the changes we made in A, but * vs A' also incorporates all of those same A changes, so if we are lucky, Git notices the already-incorporated changes and does not duplicate them. edit Git cheats. (This code has changed recently in version 2.6, although the overall strategy remains the same.)

Consider the actual output of git diff when used to show just the change from commit A to commit B. This includes an index line:

diff --git a/foo b/foo
index f0b98f8..0ea3286 100644

The value on the left is the (abbreviated) hash for the version of file foo in commit A. The value on the right is the hash for the version of the file in commit B.

Git simply fakes up a merge base from the left side hash. In other words, the file version in commit A becomes the faked merge-base. (Git passes --build-fake-ancestor to git apply. This requires that the particular file blob objects be in the repository, but they are since they are in commit A. For emailed patches, Git uses this same code, but the blob may or may not be present.)

Note that Git actually does this when cherry-picking commit A as well, but this time the merge base file is the version from commit *, which really is the merge base.

Finally, we cherry-pick C. This diffs B vs C, just as we diffed A vs B last time. If we can apply the patch as is, good; if not, we fall back to using commit * as the merge base again. It is once again a pretty wretched merge base. the same way as before, pretending that the version in B was the common base.

This also explains, incidentally, why you tend to see the same merge conflicts over and over again for these rebases: we're using the same merge-base each time. (Enabling git rerere can help.)

这篇关于使用git和meld进行交互式重新绑定的3路合并中的三个文件是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆