git merge将优先级分配给较旧的分支

时间:2019-04-03 04:54:28

标签: git merge

几周前,我的搭档“乔恩”(Jon)开设了一个新分支,从事新功能。同时,团队的其余成员在许多新功能中进行了大量工作,我们创建了新分支并将其合并,并且始终将工作合并到branch-update分支中。但是“ Jon”尚未完成,他从未从我们更新的工作中更新过他的分支。现在,与“乔恩”合并的时刻到了。问题是,当我尝试与乔恩合并时,他的工作优先于我的工作。

我有branch-updatebranch-jon,如果HEAD指向branch-update并且我说git merge branch-jon,则结果是branch-jon优先于许多已经存在的文件更新,乔恩从未触及他的分支。如果HEAD指向branch-jon,而我说git merge branch-update,则结果仍然相同。

所以,我的问题是,是否有一种方法可以通知git branch-update优先于branch-jon,并且合并应该只为我带来jon更新的工作?

1 个答案:

答案 0 :(得分:5)

在合并中没有“优先级”之类的东西。好吧,可能是 ,但是为其分配了;您一会儿就会明白我的意思。

合并是关于合并的工作。但是,要合并工作,无论您是谁,无论他们是谁,都必须有一个共同的起点。由于Git是关于 commits 的全部内容-文件只是嵌入在commits中,因此,这个共同的起点是 shared commit

例如,尝试使用git log --graph --decorate --online branch-update branch-jon。您将看到的可能很复杂;我要画一些简单的东西。 Git用最近的提交到顶部,下面的提交来绘制图表。出于空间原因,我倾向于水平绘制图形。

请记住,每个提交都是所有文件的完整快照,并在时间上冻结(不可变),并包含其前任或 parent 提交的哈希ID。这意味着我们(或Git)从最近提交(由某个分支名称标识)开始,可以向后进行操作,一次提交一次:

             I--J   <-- branch-update (HEAD)
            /
...--F--G--H
            \
             K--L   <-- branch-jon

在这个(非常简化,以至于不切实际的简单点)示例中,您和Jon自共同起点以来分别进行了两次提交。实际的共同出发点是提交HH实际上是一些丑陋的哈希ID,git log --oneline将以缩写形式打印)。通过从您的最新提交J开始,然后向后依次到IH,并从Jon的最新提交L开始,并向后工作到K,然后H,Git将自动 找到该最佳共享的常见提交。 Git将此最佳共享提交称为合并操作的合并基础。 (请注意,提交GF等也被共享;它们不是 best 。)

(Git可能将此图形绘制为:

* abcdef0 (HEAD -> branch-update) your commit subject
* c9f1233 your previous commit subject
| * 5bf3149 (branch-jon) his commit subject
| * 78983fc his previous commit subject
|/
* 371820d shared commit subject line
[snip]

尽管方向发生了变化,但这两张图代表的是同一张图。)

您的实际图形将更加混乱。合并基础在哪里可能不清楚。您可以让Git告诉您哪个提交是(或可能但不太可能是提交)合并基础:

git merge-base --all branch-update branch-jon

如果这会产生多个合并基数,则情况会稍微复杂一些,但是通常您只会看到一个提交哈希ID作为输出。

无论如何,在确定了合并基础后,就像我的图形中那样称其为H,Git现在必须将合并基础H中的快照与进行比较最新快照J,以了解您所做的更改。它还必须将H中的快照与其他最新快照进行比较,以查看Jon所做的更改。因此它运行两个git diff命令:

  • git diff --find-renames hash-of-H hash-of-J:您所做的更改
  • git diff --find-renames hash-of-H hash-of-L:乔恩做了什么

现在,Git实际上合并了更改。它从共同的起点开始,从提交H中提取所有文件。对于这些文件,它将应用您的更改。它还适用于乔恩的更改。在某些情况下,应用很容易:例如,如果您更改了文件F1F2,而Jon没有触摸这些文件,则结果为您的 {{1} }和您的 F1。如果乔恩(Jon)碰过F2而您没有碰,那么结果就是他的F3F3中的许多其他文件在HJ中可能完全相同,因此您甚至无法确定Git是否使用L的{​​{1} }或H。但是对于一些文件,例如J两者,您 Jon进行了一些更改。

这是L真正必须努力的地方。在所有其他情况下,F4仅仅从git mergegit mergeH中提取了一些文件。对于这些文件,您都接触过,Git确实必须结合更改。

如果您更改了J的第10至15行,则Git将对这些行进行您的更改。如果Jon将第20行更改为第25行,则Git将 his 更改为这些行。不需要 作为优先级:Git只需进行 更改。

如果您都是将第30行更改为第35行怎么办?好吧,如果您都对这些行进行了相同更改,则Git只会获取这些更改的一个副本,并将该一个副本应用于L中的文件。

但是,如果你们俩都将第30行更改为第35行,并且进行了不同的更改,那么现在就出现了问题。 这是不存在的优先级不存在的地方。默认情况下,Git仅声明合并冲突,并放弃完成合并。这让您一团糟:您的工作树中有一个文件,其中包含冲突的一组更改周围的冲突标记。 Git还会在索引中留下所有三个输入文件。修复Git机器的麻烦,这就是您的工作。

这也是可以选择的“优先级”出现的地方。如果您使用标准的F4合并策略,则可以使用H或{ {1}}自变量-我称为扩展自变量,尽管Git将其称为策略自变量-告诉Git:在发生冲突的情况下,请选择我的更改(recursive或他的更改(-X ours)。

摘要

大多数人在想到-X theirs时首先犯的错误是认为只有两个输入并且正在运行:

-X ours

告诉您将会发生什么。那是完全错误的!每次合并有三个输入:当前(-X theirs)提交及其文件,您提供的另一个提交(git merge)和合并基础< / em>。 Git从提交图本身找到第三输入(在重要意义上实际上是第一输入)。该图在这里非常重要。该图确定了合并基础,与两个分支技巧相比,合并基础确定了合并的结果。

合并过程本身(Git到达将要进入新合并的内容的路径)是对称的。哪个git diff branch-update branch-jon 首先运行并不重要。但是,当您说HEADbranch-jon时,就破坏了对称性:发生冲突时,您选择一侧或另一侧为准。

最后,当Git从合并的文件进行提交时,它也是不对称的。新提交的 first 父级基于git diff:无论连接到哪个分支-X ours,该分支的尖端提交都是新合并提交的第一父级。新提交的 second 父级是另一个提交。当然,新提交的哈希ID通过-X theirs进入当前分支,因此当我们获得新的合并提交HEAD时, current 分支就是获取它:

HEAD

请注意,此合并提交及其两个父级的存在会在以后的合并中更改图结构。现在,HEADM之间最好的共享公用提交是提交 I--J / \ ...--F--G--H M <-- branch-update (HEAD) \ / K--L <-- branch-jon

branch-update

如果您现在运行branch-jon,Git将比较L I--J / \ ...--F--G--H M--N--O <-- branch-update (HEAD) \ / K--L--------P <-- branch-jon 来找到我们所做的更改,以及git merge branch-jonL来查找它们的更改