有没有办法与Strategy"我们的"没有产生新的提交?

时间:2018-02-01 10:21:38

标签: git merge

我需要做的是关闭一个分支并使另一个分支的提示(最后一次提交)看起来像与该分支的合并而不实际更改其内容。我试过了

git merge -s ours other_branch --squash

但没有发生任何事情(在我阅读what squash actually does之后这是有意义的)

即。在命令之前

  * other_branch
 / 
*---* HEAD

命令后的预期结果

  * other_branch
 / \
*---* HEAD

注意:这里的内容我指的是提交的东西:元数据会随着我想要实现的操作实际上添加一个父提交而改变;我知道这正在改变历史,至少会影响shasum。

1 个答案:

答案 0 :(得分:1)

PetSerAl's comment有(或者" a",至少)命令可以用来实现你想要的。唯一缺少的是关于为什么:

的解释
git reset --soft $(git log --format=%B -n 1 | \
    git commit-tree HEAD^{tree} -p HEAD^ -p other_branch)

的工作原理。我稍后会谈到这一点,但首先......

字面答案

然而,问题的字面答案是" no"。由于可以编辑主题行,我可以引用它:

  

有没有办法与策略“我们的”合并而不会产生新的提交?

Git中的单词 merge 可以指两件事:合并行为,或前一个合并行为的结果。第一个是动词,要合并; 第二个是形容词,合并提交,甚至是名词,合并。合并提交是一个有两个父母的提交(或更多,但我们只关心两个)。

运行git merge --squash告诉Git执行合并 - 即,执行动词部分 - 但最后,在进行新提交时,进行普通的非合并提交,即禁止合并为 - 目标效应。 1 这一点是,当你有一系列提交时,例如,"分支"在某种意义上,只是"一个指向特定提交的名称" (见What exactly do we mean by "branch"?

运行git merge -s ours告诉Git执行合并的假冒行为 - 也就是说, prentend 执行合并动作,但不是真的做任何事情 - 导致合并作为形容词/名词提交。由于动词形式消失了,唯一剩下的就是后遗症。这就是为什么在这里使用--squash是没用的:你建议删除动词名词,并且没有任何东西。

1 由于没有特别好的理由,git merge --squash也会产生设置--no-commit参数的副作用,因此您必须手动运行git commit在末尾。请注意,真正的合并(进行合并提交)也可以与--no-commit一起运行,这样您也可以手动运行git commit;或者它可以因冲突而停止,您必须解决这些冲突,从而手动完成提交。最新版本的Git添加了git merge --continue以使其感觉不那么尴尬,但它只运行git commit

Git有--amend,但它并没有让我们在那里

图表显示了您想要的内容 - 例如,如果Git支持它,可能拼写为git commit --amend --add-parent,但不会产生关键洞察力,事实上git commit --amend是一个小的,或者说是巨大的谎言,因为它没有更改提交,它只是使用不常见的父哈希进行新的提交。

正常的git commit进程执行一系列步骤,就像它们一次完成一样,这样它们都可以正确完成,或者没有任何反应(或者似乎至少)。步骤是:

  1. 获取当前提交的哈希ID(git rev-parse HEAD)。调用此 P :现有的HEAD将是新提交的父级。 (如果您要完成合并,MERGE_HEAD也会存在,git commit会将其读取以获得更多父母。但这只是为了完成合并。)
  2. 编写当前索引以生成tree对象(git write-tree)。将此 T 称为树哈希ID。 (这可能与先前提交的树相同或不同。)
  3. 获取您的姓名和电子邮件地址以及作为提交者的当前时间。通常,也可以将它们用作作者(您可以覆盖它们)。
  4. 获取提交消息。
  5. 写出包含所有这些信息的新提交(tree T parent P author和{{ 1)}在步骤3中获得,并在步骤4中获取提交消息。结果是新的提交哈希 C
  6. new 哈希 C 写入当前分支名称,以便committer现在生成 C
  7. 使用git rev-parse HEAD在步骤1更改过程:Git 不是将git commit --amend作为父提交,而是读取当前提交的父哈希(那里)如果您正在HEAD进行合并,则可能不止一个,并在步骤5中使用这些内容。

    效果是将当前的提交推到一边:

    --amend

    变为:

    ...--o--o--*   <-- master (HEAD)
    

    你想让Git做的事情有点不同。

    为什么(以及如何)shell命令工作

    Git&#39; * [the commit that was HEAD before] / ...--o--o--@ <-- master (HEAD) 命令生成新的提交对象。它类似于上面六步提交序列的第5步。但它还没有成为一棵树,并且它没有准备好的预计算父提交哈希值,所以它将这些作为命令行参数:

    commit-tree

    在这种情况下。我们想要的 git commit-tree tree-hash -p parent-hash-1 -p parent-hash-2 tree-hash一样,是当前提交所具有的相同的树。我们可以使用git merge -s ours命名该树,the gitrevisions documentation中对此进行了描述。我们想要的两个父哈希以当前提交的父级开头。 (我们可以假设只有一个这样的父级。)同样,gitrevisions语法为我们提供了一种写入方式:我们可以使用HEAD^{tree}parent^1,或省略{{1来自其中任何一个表达式。我们想要的另一个父哈希是parent~1点的提交,所以我们可以命名。这给了我们:

    1

    此命令从其标准输入中读取commit 消息。如果我们想要保留当前提交的提交消息,我们可以使用other_branch提取它:git commit-tree HEAD^{tree} -p HEAD^ -p other_branch 告诉git log通过打印其主题来显示每个提交-and-body作为文本,--format=%B告诉git log仅显示一个提交。默认情况下,-n 1显示的第一个提交是git log提交。所以这给了我们:

    git log

    部分 - 我们将此HEAD的标准输出传输到git log --format=%B -n 1 | 的标准输入。

    提交后git log执行的操作是将其哈希ID打印到自己的标准输出。因此,如果我们自己运行此管道,我们会看到打印的新提交哈希,但我们不会存储在任何地方。我们需要做的是更改当前分支名称 - 无论是什么 - 指向新提交;并且git commit-tree会这样做,因此:

    git commit-tree

    git reset --soft commit-hash构造是最后一位:shell将此视为运行给定命令,捕获其标准输出,然后将该标准输出文本视为git reset --soft $(...) 的命令参数。 由于只有一个输出字 - 新提交的哈希 - 它在新提交ID上运行$(...)