Git如何与两个父母进行空提交

Hit*_*SPB 1 git cherry-pick git-commit

我在为工作找到正确的 git 命令时遇到了一些问题。

我工作的地方目前正在切换源代码控制(TFVC -> Git)。但是,TFVC 中的旧版本仍然需要不时修复错误。

我通过使用一些 PowerShell 脚本创建构建来处理这个问题,以确保我的“tfvc-merge”分支包含这些更改。

所以这就是我之后所做的。

  • 从集成分支结帐
  • 我从“tfvc-merge”分支中挑选提交到我的“mergerhandler”分支
  • (我的问题)现在我想做一个有 2 个父母的空提交。第一个父项是我在“MergeHandler”分支上所做的最后一次提交,下一个父项是“tfvc-merge”分支的提交。

我试着把它画出来。所以基本上,拉取请求中的空提交将在我的绘图中具有 1 和 2 的父项。

樱桃挑选策略

我一直在四处寻找,几个小时没有找到正确的方法来做到这一点。因此,如果有一个提示可以为我指明正确的方向,那就太好了。提前致谢。

tor*_*rek 5

TL; 博士

我想你只是想要git merge -s ours

描述

Cherry 选择一些现有提交进行新提交,该提交不会链接回原始提交(新提交只有一个父提交;该父提交是HEAD您运行时的提交git cherry-pick)。您绘制的图显示了与两个父项的提交 #2 ,就好像您的选择是合并一样。因此,我不确定我是否正确理解了情况本身。

短语空提交是 Git 本身使用的,但我发现它非常具有误导性:提交通常没有空对象(请参阅git 的半秘密空树对象是否可靠,为什么没有符号名称?) ; 提交的空白在于该提交与其父提交之间git commit --allow-empty差异

有几种方法可以进行这样的提交,显然包括git commit --allow-empty,但第一种方法不会进行合并提交。进行合并提交的唯一面向用户的命令是git merge. 这给您留下了“合并而不合并”的问题,如果这句话甚至意味着什么的话。有时它会:git merge -s ours.

(请注意,由于合并提交有两个父项,因此使用短语“空提交”来谈论此合并比谈论单亲提交时更没有意义,因为有两个差异要针对合并提交的两个父母。很可能如果一个是空的,另一个不是。)

运行时git merge,您可以提供合并策略名称。Git有四个内置的策略,命名recursiveresolveoctopus,和ours。人们在正常合并期间遇到的正常情况是recursive. 该resolve策略是-s recursive几乎相同的简化变体(两者仅在具有多个合并基础的合并的相对罕见的情况下不同)。该octopus战略是一个Git使用当你与一个真正的合并超过两个的父母,我们不会在所有在这里讨论。那离开-s ours。在我们开始之前,让我们花点时间回顾一下正常的(递归/解析)方法:

  1. 你检查了一些特定的分支:git checkout mainline.
  2. 你跑git merge sidebranch

此时,Git 会计算——分支名称指向的提交的提示提交,我将调用left/local/ ——和 的提示提交之间的合并基础,我将调用右/远程/ :mainlineL--ourssidebranchR--theirs

...--o--*--o--L   <-- mainline
         \
          o----R   <-- sidebranch
Run Code Online (Sandbox Code Playgroud)

合并基础是两个分支连接的点:在这里,它是 commit *。Git 然后,本质上,计算两个git diffs:

git diff --find-renames <hash-of-base> <hash-of-L>   # what we did
git diff --find-renames <hash-of-base> <hash-of-R>   # what they did
Run Code Online (Sandbox Code Playgroud)

然后 Git 尝试组合这两组更改,使这些组合的更改应用于合并 base 的内容*,如果一切顺利,Git 将创建一个新的合并提交 ML作为它的第一个父级和R第二个父级:

...--o--*--o--L--M   <-- mainline
         \      /
          o----R   <-- sidebranch
Run Code Online (Sandbox Code Playgroud)

比较(差异化)LvsM将显示此合并从*-vs-获得的更改,而这些更改R尚未在*-vs- 中L。也就是说,我们接手了他们的工作。比较RvsM将显示此合并从*-vs-获得的更改,而这些更改L尚未在*-vs- 中R。也就是说,我们也保留了我们的工作。

-s ours策略

做什么-s ours很简单。diffGit不会打扰所有的-ing,而是将树从L. Git 将新的合并提交L作为其第一个父级和R第二个父级,因此该看起来完全相同:

...--o--*--o--L--M   <-- mainline
         \      /
          o----R   <-- sidebranch
Run Code Online (Sandbox Code Playgroud)

但是现在如果我们运行git diff mainline^1 mainline比较Land M,输出是空的。如果我们运行git diff sidebranch mainline比较RM,它们可能完全不同。Git完全忽略了 commit 的内容R:它M使用了L.

如果您将空差异称为“空提交”,那么LvsM就是这样一个空提交。但是仍然有RvsM作为这个合并提交的一部分,所以正如我们上面提到的,“空提交”这个短语在这里没有意义。

长篇大论

如果存在从旧 tvfc 分支到新分支的现有合并提交,整个事情会更有意义:

  ...--o--M--o--<arbitrarily complex work>--I   <-- integration
         /
...--o--*   <-- tvfc
Run Code Online (Sandbox Code Playgroud)

随着时间的推移,它变成:

  ...--o--M--o--<arbitrarily complex work>--I   <-- integration
         /
...--o--*--o--<arbitrarily complex work>-----X   <-- tvfc
Run Code Online (Sandbox Code Playgroud)

在这种情况下,任何和所有加入到新的提交tvfc本身可以简单地合并,integrationgit checkout integration; git merge tvfc作为合并基础integrationtvfc现在提交*。Git 将比较*vsX以查看它们更改了什么,并将其与*vs结合I以查看您更改了什么。合并这些更改后,Git 将进行新的合并提交N,未来新的合并基础将是X它自己:

  ...--o--M--o--<arbitrarily complex work>--I--N   <-- integration
         /                                    /
...--o--*--o--<arbitrarily complex work>-----X   <-- tvfc
Run Code Online (Sandbox Code Playgroud)

但这似乎不是你在做什么。我想知道您现在是否只是在设置这种情况。如果是这样,git merge -s ours上面的命令可能就是你想要的。