PetSerAl的评论提供了一个(或者至少是)你可以使用的命令来实现你想要的。唯一缺少的是关于为什么的解释:
git reset --soft $(git log --format=%B -n 1 | \
git commit-tree HEAD^{tree} -p HEAD^ -p other_branch)
工作。稍后我会详细解释,但首先...
一个直接的回答
然而,根据您提出的问题,直接回答是“不可以”。由于主题行可以被编辑掉,让我引用一下:
有没有一种方法可以在不产生新提交的情况下与“ours”策略合并?
在Git中,“合并”这个词可以指两件事情:合并的行为或以前合并行为的结果。第一个是动词,“合并”;第二个是形容词,“合并提交”,甚至是名词,“合并”。合并提交是具有两个父级(或更多,但我们只关心两个)的提交。
运行
git merge --squash
告诉Git执行合并即动词部分,但在最后创建一个普通的非合并提交即取消合并作为形容词的效果。
1这样做的目的是当您有一系列提交时,例如以“分支”方式存在,即不仅仅是“指向特定提交的名称”(请参见
我们所说的“分支”究竟是什么?)。
运行
git merge -s ours
告诉Git执行虚假合并操作,即
假装执行合并即动词,但实际上不会做任何事情,从而产生合并作为形容词/名词的提交。由于动词形式消失了,唯一剩下的就是后效。这就是在此处使用
--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
过程将执行一堆步骤,就好像它们都一次性完成一样,所以要么它们全部正确完成,要么什么也不会发生(或者至少看起来是这样)。这些步骤包括:
- 获取当前提交的哈希值(
git rev-parse HEAD
)。将其称为P:现有的HEAD
将成为新提交的父级。(如果您正在结束合并,则MERGE_HEAD
也将存在,并且git commit
会读取它以获取其他父级。不过这只是用于完成合并。)
- 将当前索引写入以创建一个
tree
对象(git write-tree
)。将其称为T,即树的哈希值。(这可能与先前某个提交的树相同,也可能不同。)
- 获取您的姓名、电子邮件地址和当前时间作为提交者。通常,将其用作作者(您可以覆盖它们)。
- 获取提交消息。
- 使用所有这些信息(
tree
T、parent
P、在步骤3中获取的author
和committer
,以及在步骤4中获取的提交消息)编写一个新的提交。结果是一个新的提交哈希值C。
- 将新哈希C写入当前分支名称,以便
git rev-parse HEAD
现在产生C。
使用
git commit --amend
会改变步骤1的程序:Git不再将
HEAD
作为父提交,而是
读取当前提交的父哈希值(如果您正在
--amend
合并,则可能有多个),并在步骤5中使用它们。
其效果是将当前提交推到一旁。
...--o--o--* <-- master (HEAD)
变成:
* [the commit that was HEAD before]
/
...--o--o--@ <-- master (HEAD)
你想让Git做的事情有点不同。
为什么(以及如何)shell命令起作用
Git的
commit-tree
命令会生成新的提交对象。就像上面六步提交序列中的第5步。但它没有创建树状结构,并且还没有预先计算好的父提交哈希值,因此需要将其作为命令行参数传递:
git commit-tree tree-hash -p parent-hash-1 -p parent-hash-2
。
在这种情况下,我们想要的
tree-hash
与
git merge -s ours
相同,即当前提交所具有的相同树。我们可以使用
HEAD^{tree}
来命名该树,
gitrevisions文档中有相关描述。我们想要的两个父哈希值始于
当前提交的父级。 (我们可以假设只有一个这样的父级。)再次,gitrevisions语法为我们提供了一种编写此内容的方法:我们可以使用
parent^1
或
parent~1
,或从这些表达式中省略
1
。我们想要的另一个父哈希是指向
other_branch
的提交,因此我们只需命名即可。 这给了我们:
git commit-tree HEAD^{tree} -p HEAD^ -p other_branch
这个命令从标准输入中读取提交的信息。如果我们想保留当前提交的消息,可以使用
git log
提取它:
--format=%B
告诉
git log
将每个提交作为文本打印出来,并且
-n 1
告诉
git log
只显示一个提交。默认情况下,
git log
显示的第一个提交是
HEAD
提交。因此,这给了我们:
git log --format=%B -n 1 |
部分——我们将git log
的标准输出管道传输到git commit-tree
的标准输入。
git commit-tree
在创建提交后会将其哈希ID打印到自己的标准输出中。因此,如果我们仅运行此管道本身,我们将看到新的提交哈希打印出来,但我们不会将其存储在任何地方。我们需要做的是更改当前分支名称(无论它是什么)以指向新提交;git reset --soft commit-hash
将完成此操作,因此:
git reset --soft $(...)
$(...)
结构是最后一部分:shell 将其解释为“运行给定的命令,捕获其标准输出,然后将该标准输出文本视为
git reset --soft
命令的参数。”由于只有一个输出词——新提交的哈希值——因此会在新的提交 ID 上运行
git reset --soft
。
git reset --soft $(git log --format=%B -n 1 | git commit-tree HEAD^{tree} -p HEAD^ -p other_branch)
- user4003407