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

adr*_*edo 1 git merge

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

我有branch-updateand branch-jon,如果HEAD指向branch-update并且我说git merge branch-jon结果是branch-jon优先于许多已经更新的文件,并且jon从未在他的分支中碰过。如果HEAD指向branch-jon,我说git merge branch-update结果还是一样。

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

tor*_*rek 7

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

合并是关于合并工作。但是,要合并工作,无论您是谁,无论他们是谁,都必须有一个共同的起点。由于Git完全是关于提交的-文件只是嵌入在提交中-因此,这个共同的出发点是共享提交

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

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

             I--J   <-- branch-update (HEAD)
            /
...--F--G--H
            \
             K--L   <-- branch-jon
Run Code Online (Sandbox Code Playgroud)

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

(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]
Run Code Online (Sandbox Code Playgroud)

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

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

git merge-base --all branch-update branch-jon
Run Code Online (Sandbox Code Playgroud)

如果这产生多个合并基础,则情况会稍微复杂一些,但是通常您只会看到一个提交哈希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从共同的起点从提交中提取所有文件。对于这些文件,它将应用您的更改。它还适用于乔恩的更改。在某些情况下,申请过程非常简单:例如,如果你改变了文件F1,并F2和Jon没有触及那些文件,结果是 F1你的 F2。如果乔恩(Jon)碰了碰F3而您没有碰,结果就是他F3。在许多其他的文件H可能是在完全相同的JL,所以你甚至不能让Git是否正在使用H的,J的,或L的。但是,对于一些文件,比如F4两者 乔恩做了一些更改。

这是git merge真正必须努力的地方。所有其他情况下,git merge逃脱了刚刚服用一些文件HJL。对于这些文件(你们俩都曾碰过的文件),Git确实必须合并更改。

如果通过的15改线10 F4,Git的需要改变这些行。如果Jon将第20行更改为第25行,则Git 会将更改为这些行。有没有必要优先:混帐只需要两个变化。

如果您更改了第30至35行怎么办?好吧,如果你都做了同样的变化对这些线路,Git的只是需要一个拷贝的这些变化,将一个拷贝到该文件H

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

这也是“优先”那可以选择进来如果你使用的是标准。recursive合并策略,您可以使用-X ours-X theirs论据,我称之为扩展参数,虽然混帐称他们的策略参数 -to让Git:在如果发生冲突,请选择我的更改(-X ours)或他的更改(-X theirs)。

摘要

大多数人一开始想到的错误git merge就是认为只有两个输入,然后运行:

git diff branch-update branch-jon
Run Code Online (Sandbox Code Playgroud)

告诉你会发生什么。那是完全错误的!每次合并有三个输入:当前(HEAD)提交及其文件,您提供的另一个提交(branch-jon)和合并基础。Git会从提交图中自行找到第三个输入(在重要意义上,它实际上是第一个输入)。该图在这里非常重要。该图确定了合并基础,并且与两个分支技巧相比,合并基础确定了合并的结果。

合并过程本身(Git到达将要进入新合并的内容的路径)是对称的。哪个git diff先运行并不重要。但是,当您说-X ours或时-X theirs,这破坏了对称性:发生冲突时,您选择一侧或另一侧占优势。

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

             I--J
            /    \
...--F--G--H      M   <-- branch-update (HEAD)
            \    /
             K--L   <-- branch-jon
Run Code Online (Sandbox Code Playgroud)

请注意,此合并提交及其两个父级的存在会在以后的merge中更改图结构。现在,branch-update和之间最好的共享通用提交branch-jon是commit L

             I--J
            /    \
...--F--G--H      M--N--O   <-- branch-update (HEAD)
            \    /
             K--L--------P   <-- branch-jon
Run Code Online (Sandbox Code Playgroud)

如果您现在运行git merge branch-jon,则Git将比较Lvs O来找到我们更改的内容,以及Lvs P来找到它们更改的内容。

  • 非常清楚的解释。TL;DR:您可以使用 `-X ours` 或 `-X Theirs` 参数:) (2认同)