Git合并的顺序重要吗? [英] Does the order of Git merging matter?

查看:103
本文介绍了Git合并的顺序重要吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有两个分支,AB.下列属性是否成立?

Suppose I have two branches, A and B. Do the following properties hold?

  • A合并到B中时发生冲突,并且仅当将B合并到A中时发生冲突.
  • A合并到B之后文件的内容与将B合并到A之后文件的内容相同.
  • Merging A into B conflicts if and only if merging B into A conflicts.
  • The contents of my files after merging A into B is the same as the contents of my files after merging B into A.

推荐答案

cmaster的答案是正确的,但有警告.首先,请注意以下项目/假设:

cmaster's answer is correct, with caveats. Let's start by noting these items / assumptions:

  • 始终存在单个合并基础提交.我们将此提交称为 B ,以作为基础.
  • 其他两个输入也是单次提交.我们称它们为 L 表示左侧/本地(--ours),而 R 表示右侧/远程(--theirs).
  • There is always a single merge base commit. Let's call this commit B, for base.
  • The other two inputs are also single commits. Let's call them L for left / local (--ours) and R for right / remote (--theirs).

第一个假设不一定正确.如果有多个合并基础候选者,则合并策略要对此进行处理.两种标准的两头合并策略是recursiveresolve. resolve策略只是(随机)随机选择一个. recursive策略一次将两个合并基础合并,然后将生成的提交用作合并基础. resolve 选择的一个可能受git merge-base以及因此git merge的参数顺序影响,所以这里就是一个警告.由于递归策略 可以进行多个合并,因此这里有第二个警告尚难以描述,但仅在两个以上合并基础时才适用.

The first assumption is not necessarily true. If there are multiple merge base candidates, it is up to the merge strategy to do something about this. The two standard two-head merge strategies are recursive and resolve. The resolve strategy simply picks one at (apparent) random. The recursive strategy merges the merge bases two at a time, and then uses the resulting commit as the merge base. The one chosen by resolve can be affected by the order of arguments to git merge-base and hence to git merge, so that's one caveat right there. Because the recursive strategy can do more than one merge, there's a second caveat here that is difficult to describe yet, but it applies only if there are more than two merge bases.

第二个假设更为正确,但请注意,合并代码 可以在部分修改的工作树上运行.在这种情况下,所有下注都不可用,因为工作树与 L R 不匹配.但是,标准的git merge会告诉您必须首先提交,因此通常这不是问题.

The second assumption is much more true, but note that the merge code can run on a partially-modified work-tree. In this case all bets are off, since the work-tree does not match either L or R. A standard git merge will tell you that you must commit first, though, so normally this is not a problem.

我们已经注意到存在多个合并基础的问题.我们也假设是两头合并.

We already noted the issue with multiple merge bases. We're assuming a two-head merge as well.

章鱼合并可以处理多个头部.这也改变了合并基础的计算,但是一般来说,章鱼合并不适用于合并问题复杂的情况,并且只会拒绝在顺序可能重要的地方运行.不过,我不会努力去做.这是对称规则可能失败的另一种情况.

Octopus merges can deal with multiple heads. This also change the merge base computation, but in general octopus merge won't work with cases that have complicated merge issues and will just refuse to run where the order might matter. I would not push hard on it though; this is another case where the symmetry rule is likely to fail.

-s ours合并策略完全忽略了所有其他提交,因此合并顺序在这里显然至关重要:结果始终为 L . (我很确定-s ours甚至不必费心计算合并基础 B .)

The -s ours merge strategy completely ignores all other commits so merge order is obviously crucial here: the result is always L. (I am fairly sure that -s ours does not even bother computing a merge base B.)

您可以编写自己的策略并做任何您想做的事情.在这里,您可以像在-s ours一样处理订单事宜.

You can write your own strategy and do whatever you want. Here, you can make the order matter, as it does with -s ours.

Git现在实际上从这三个快照中计算出两个变更集:

Git now computes, in effect, two change-sets from these three snapshots:

  • L-B git diff --find-renames B L
  • R-B git diff --find-renames B R

这里的重命名检测器是独立的,这意味着两者都不会相互影响.两者都使用相同的规则.这里的主要问题是,有可能在 B 变更集中将 B 中的同一文件检测为重命名,在这种情况下,我们得到了所谓的高级冲突,特别是重命名/重命名冲突. (在重命名/删除和其他几种情况下,我们也会发生高层冲突.)对于重命名/重命名冲突,Git选择的 final 名称是 L 中的名称. ,而不是 R 中的名称.因此,这里的顺序取决于最终的文件名.这不会影响工作树合并的 content .

The rename detectors here are independent—by this I mean neither affects the other; both use the same rules though. The main issue here is that it's possible for the same file in B to be detected as renamed in both change-sets, in which case we get what I call a high level conflict, specifically a rename/rename conflict. (We can also get high level conflicts with rename/delete and several other cases.) For a rename/rename conflict, the final name that Git chooses is the name in L, not the name in R. So here, the order matters in terms of final file name. This does not affect the work-tree merged content.

在这一点上,我们应该对Git的内部进行一些浏览.现在,我们已经配对了 B -vs- L B -vs- R 中的文件,即我们知道三个提交中的每个文件中的相同"文件.但是,Git存储文件和提交的方式很有趣.从逻辑的角度来看,Git 没有增量::每次提交都是所有文件的完整快照.但是,每个文件都是一对实体:路径名 P 和哈希ID H .

At this point we should take a small tour of Git's internals. We have now paired up files in B-vs-L and in B-vs-R, i.e., we know which files are "the same" files in each of the three commits. However, the way Git stores files and commits is interesting. From a logical point of view, Git has no deltas: each commit is a complete snapshot of all files. Each file, however, is just a pair of entities: a path name P and a hash ID H.

换句话说,此时,无需遍历从 B L R 的所有提交.我们知道我们有一些文件 F ,该文件最多由三个单独的路径名标识(如上所述,Git在大多数情况下将使用 L 路径,但使用 R 路径(如果合并的 B -vs- R 端只有一个重命名).可通过直接查找获得所有三个文件的完整内容: H B 表示基本文件内容, H L 代表左侧文件, H R 代表右侧文件.

In other words, at this point, there is no need to walk through all the commits leading from B to either L or R. We know that we have some file F, identified by up to three separate path names (and as noted above, Git will use the L path in most cases, but use the R path if there is only one rename in the B-vs-R side of the merge). The complete contents of all three files are available by direct lookup: HB represents the base file content, HL represents the left-side file, and HR represents the right-side file.

两个文件仅在其哈希匹配时才完全匹配. 1 因此,此时Git只是比较哈希ID.如果三个都匹配,则合并的文件与左右文件和基础文件相同:没有任何作用.如果L和R匹配,则合并的文件是L或R的内容;该基数无关紧要,因为双方都进行了相同的更改.如果B匹配L或R而不匹配另一个,则合并的文件是不匹配的哈希.如果存在用于低级合并冲突的潜力,则Git仅需要执行低级合并.

Two files match exactly if and only if their hashes match.1 So at this point Git just compares the hash IDs. If all three match, the merged file is the same as the left and right and base files: there is no work. If L and R match, the merged file is the L or R content; the base is irrelevant as both sides made the same change. If B matches either L or R but not the other, the merged file is the non-matching hash. Git only has to do the low-level merge if there is a potential for a low-level merge conflict.

所以 now ,Git提取了这三个内容并进行了合并.这是逐行进行的(更改多条相邻的行时,这些行会组合在一起):

So now, Git extracts the three contents and does the merge. This works on a line-by-line basis (with lines grouped together when multiple adjacent lines are changed):

  • 如果左右两侧仅触摸不同源代码行,则Git将同时进行这两个更改.这显然是对称的.

  • If both left and right sides touched only different source lines, Git will take both changes. This is clearly symmetric.

如果左右触摸相同的源代码行,Git将检查更改本身是否也相同.如果是这样,Git将获取一份更改.这显然也是对称的.

If left and right touched the same source lines, Git will check whether the change itself is also the same. If so, Git will take one copy of the change. This, too, is clearly symmetric.

如果左右触碰相同的行,但是进行了不同的更改,则Git将声明合并冲突.工作树内容将取决于更改的顺序,因为工作树内容具有<<<<<<< HEAD ... ||||||| base ... ======= ... other >>>>>>>标记(base部分是可选的,如果选择diff3样式,则会出现).

If left and right touched the same lines, but made different changes, Git will declare a merge conflict. The work-tree content will depend on the order of the changes, since the work-tree content has <<<<<<< HEAD ... ||||||| base ... ======= ... other >>>>>>> markers (the base section is optional, appearing if you choose diff3 style).

同一行的定义有些棘手.这确实取决于diff算法(可以选择),因为某些文件的某些部分可能会重复.但是,Git始终使用单个算法来计算L和R,因此 order 在这里无关紧要.

The definition of the same lines is a little tricky. This does depend on the diff algorithm (which you may select), since some sections of some files may repeat. However, Git always uses a single algorithm for computing both L and R, so the order does not matter here.

1 换句话说,如果您设法生成Doppelgänger文件,则该文件与有所不同内容与某些现有文件中的 hash 相同,但是Git只是拒绝将该文件放入存储库中. shattered.it PDF并不是这样的文件,因为Git在文件的数据前加上单词blob和文件的大小,但是该原理适用. 请注意,将此类文件放入SVN会破坏SVN,好吧,.

1To put this another way, if you manage to produce a Doppelgänger file—one that has different content from, but the same hash as, some existing file, Git simply refuses to put that file into the repository. The shattered.it PDF is not such a file, because Git prefixes the file's data with the word blob and the size of the file, but the principle applies. Note that putting such a file into SVN breaks SVN—well, sort of.

您可以使用-X ours-X theirs覆盖合并冲突投诉.这些直接引导Git解决冲突的方法,分别有利于 L R 更改.

You can override merge conflict complaints using -X ours or -X theirs. These direct Git to resolve conflicts in favor of the L or R change respectively.

即使有上述警告,这种对称原理也适用于单个合并.但是一旦完成合并,运行的 next 合并将使用修改后的提交图来计算新的合并基础.如果您打算进行两次合并,则可以按照以下步骤进行合并:

This symmetry principle, even with the above caveats, is fine for a single merge. But once you have made a merge, the next merge you run will use the modified commit graph to compute the new merge base. If you have two merges that you intend to do, and you do them as:

git merge one    (and fix conflicts and commit if needed)
git merge two    (fix conflicts and commit if needed)

然后,即使每次合并中的所有内容都是对称的,这也不意味着您一定会获得与运行时相同的结果:

then even if everything is symmetric in each merge, that does not mean that you will necessarily get the same result as if you run:

git merge two
git merge one

首先运行哪个合并,您将获得一个 merge提交,然后第二次合并将找到一个不同的合并基础.

Whichever merge runs first, you get a merge commit, and the second merge now finds a different merge base.

如果您确实有冲突,必须先解决该冲突,然后再进行合并,这尤其重要,因为这也会影响到第二个git merge命令的 L 输入.对于它的三个输入中的两个,它将使用第一个合并的快照作为 L ,将新的(可能有所不同)合并基础作为 B .

This is particularly important if you do have conflicts that you must fix before finishing whichever merge goes first, since that also affects the L input to the second git merge command. It will use the first merge's snapshot as L, and the new (maybe different) merge base as B, for two of its three inputs.

这就是为什么我提到-s recursive在使用多个合并库时可能存在顺序差异的原因.假设有三个合并基础. Git将合并前两个(以它们从合并基础计算中弹出的顺序),提交结果(即使存在合并冲突,它也会提交冲突) (在这种情况下),然后将该提交与第三个提交合并并提交结果.然后,这里的最后提交是输入 B .只有此过程的所有部分是对称的,最终的 B 结果才是顺序不敏感的. 大多数合并是对称的,但是我们看到了上面的所有警告.

This is why I mentioned that -s recursive has potential order differences when working with multiple merge bases. Suppose there are three merge bases. Git will merge the first two (in whatever order they pop out of the merge base computation), commit the result (even if there are merge conflicts—it just commits the conflicts in this case), and then merge that commit with the third commit and commit the result. The final commit here is then the input B. Only if all parts of this process are symmetric will that final B result be order-insensitive. Most merges are symmetric, but we saw all the caveats above.

这篇关于Git合并的顺序重要吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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