我需要做的是关闭一个分支并使另一个分支的提示(最后一次提交)看起来像与该分支的合并而不实际更改其内容。我试过了
git merge -s ours other_branch --squash
但没有发生任何事情(在我阅读what squash actually does之后这是有意义的)
即。在命令之前
* other_branch
/
*---* HEAD
命令后的预期结果
* other_branch
/ \
*---* HEAD
注意:这里的内容我指的是提交的东西:元数据会随着我想要实现的操作实际上添加一个父提交而改变;我知道这正在改变历史,至少会影响shasum。
答案 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
。
--amend
,但它并没有让我们在那里图表显示了您想要的内容 - 例如,如果Git支持它,可能拼写为git commit --amend --add-parent
,但不会产生关键洞察力,事实上git commit --amend
是一个小的,或者说是巨大的谎言,因为它没有更改提交,它只是使用不常见的父哈希进行新的提交。
正常的git commit
进程执行一系列步骤,就像它们一次完成一样,这样它们都可以正确完成,或者没有任何反应(或者似乎至少)。步骤是:
git rev-parse HEAD
)。调用此 P :现有的HEAD
将是新提交的父级。 (如果您要完成合并,MERGE_HEAD
也会存在,git commit
会将其读取以获得更多父母。但这只是为了完成合并。)tree
对象(git write-tree
)。将此 T 称为树哈希ID。 (这可能与先前提交的树相同或不同。)tree
T ,parent
P ,author
和{{ 1)}在步骤3中获得,并在步骤4中获取提交消息。结果是新的提交哈希 C 。committer
现在生成 C 。使用git rev-parse HEAD
在步骤1更改过程:Git 不是将git commit --amend
作为父提交,而是读取当前提交的父哈希(那里)如果您正在HEAD
进行合并,则可能不止一个,并在步骤5中使用这些内容。
效果是将当前的提交推到一边:
--amend
变为:
...--o--o--* <-- master (HEAD)
你想让Git做的事情有点不同。
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上运行$(...)
。