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

时间:2018-03-08 10:43:19

标签: git git-commit cherry-pick

我有一些问题找到适合该工作的git命令。

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

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

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

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

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

Cherry-Pick Strategy

我一直在寻找,几个小时没有找到正确的方法来做这件事。因此,提示可以指向正确的方向会很好。提前谢谢。

1 个答案:

答案 0 :(得分:3)

TL; DR

我想你只想要git merge -s ours

描述

Cherry挑选一些现有的提交会使没有链接回原始提交的新提交(新提交具有单个父提交;此提交是HEAD提交时你跑了git cherry-pick)。您所做的绘图显示了#em>两个父项的#2提交,就像您的樱桃选择是合并一样。因此,我不确定我是否正确了解情况本身。

短语空提交是Git本身使用的一个,但我发现它非常具有误导性:提交通常没有空的对象(参见Is git's semi-secret empty tree object reliable, and why is there not a symbolic name for it? );使用git commit --allow-empty进行提交的空白是该提交与其父级之间的差异

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

(请注意,由于合并提交具有两个父项,因此使用短语“empty commit”来讨论此合并的含义甚至比讨论单父提交时更少。对于合并提交的两个父母,有两个差异。如果一个为空,另一个则不是。)

运行git merge时,您可以提供合并策略名称。 Git有四个内置策略,名为recursiveresolveoctopusours。人们在正常合并期间遇到的正常问题是recursiveresolve策略是-s recursive的简化变体,几乎是相同的(两者仅在与多个合并基础合并的相对罕见的情况下不同)。 octopus策略是Git在与 more 进行真正合并时使用的策略,而不是两个父母,我们在此不再讨论。留下-s ours。在我们开始之前,让我们花一点时间来回顾正常(递归/解析)方法:

  1. 您查看了一些特定的分支:git checkout mainline
  2. 您运行git merge sidebranch
  3. 此时,Git在mainline的提示提交之间计算合并基础 - 分支名称指向的提交,我将其称为L / local / --ours - 以及sidebranch的提示,我将R称为右/远程/ --theirs

    ...--o--*--o--L   <-- mainline
             \
              o----R   <-- sidebranch
    

    合并基础是两个分支联合的点:这里,它是提交*。从本质上讲,Git计算两个git diff s:

    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
    

    Git然后尝试组合这两组更改,使这些组合更改适用于合并基础*的内容,如果一切顺利,Git会创建一个新的< em> merge commit M,其中L为其第一个父级,R为其第二个父级:

    ...--o--*--o--L--M   <-- mainline
             \      /
              o----R   <-- sidebranch
    

    比较(差异)LM将显示此合并从* - vs - R获得的更改,这些更改尚未在*中-vs - L。也就是说,我们开始了他们的工作。比较RM将显示此合并从* - vs - L获得的更改,这些更改尚未在* - vs - {{1 }}。也就是说,我们保持我们的工作。

    R策略

    -s ours做的很简单。 Git只保留-s ours的树,而不是烦扰所有diff。 Git使用L作为其第一个父项进行新的合并提交,并将L作为其第二个父项,因此看起来完全相同:

    R

    但现在,如果我们运行...--o--*--o--L--M <-- mainline \ / o----R <-- sidebranch 来比较git diff mainline^1 mainlineL,则输出为空。如果我们运行M来比较git diff sidebranch mainlineR,它们可能完全不同。 Git 完全忽略了提交M的内容:它使用R的内容M

    如果你将空差异称为“空提交”,那么L vs L就是这样一个空提交。但是,作为此合并提交的一部分,仍然存在M vs R,因此如上所述,“空提交”这一短语在这里并不合理。

    长期搁置

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

    M

    随着时间的推移变成:

      ...--o--M--o--<arbitrarily complex work>--I   <-- integration
             /
    ...--o--*   <-- tvfc
    

    在这种情况下,添加到 ...--o--M--o--<arbitrarily complex work>--I <-- integration / ...--o--*--o--<arbitrarily complex work>-----X <-- tvfc 的所有新提交都可以简单地合并到tvfcintegration作为git checkout integration; git merge tvfc和{{1}的合并基础现在提交integration。 Git会将tvfc*区分开来,看看他们发生了什么变化,并将其与* vs X结合起来,看看你发生了什么变化。在组合这些更改后,Git将进行新的合并提交*,并且将来新的合并基础将是I本身:

    N

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