我知道我在这里确实提出了一个 XY 问题。
让我们想象一下,最后 4 次提交代表了一个更改,我想要创建一个
no-ff
合并到原始状态,以保留各个提交,解释为什么他们自己做了一个很好的更改,但让合并提交消息描述该功能是什么以及它为用户提供了什么好处。
我可以执行以下操作:
git reset HEAD~4 --hard # Reset HEAD to the commit before the feature
git merge --no-ff HEAD@{1} # HEAD@{1} is not the prev. commit, thus my features.
第一个命令更改了我的工作树,但最终状态与我开始的状态相同。工作树的更改可能会产生一些不幸的后果,例如
tsserver
,如果移动/删除文件,TypeScript/JavaScript 语言服务器有时会进入 100% CPU。它还可能触发自动测试运行,这可能会产生我不想要的副作用。
所以我想看看是否可以在不更改工作文件夹的情况下执行该操作。
阅读了True merge 的文档,并检查执行 --no-ff
合并时的中间状态(在等待提交消息时),我尝试了:
# reset to the commit I want to merge _to_, but keep working dir
git reset HEAD~4
git update-ref MERGE_HEAD HEAD@{1} # Gathered from merge documentation
git update-ref ORIG_HEAD HEAD # Not in doc, but `no-ff` updates this ref
echo "no-ff" > .git/MERGE_MODE # Not in doc, but `no-ff` creates this file
git add . # Gathered from merge documentation
git merge --continue
虽然它创建提交,但该提交只有一个父级,而不是 2 个。
我还注意到no-ff
添加了一个
AUTO_MERGE
文件指向提交根树对象。我尝试添加(仅使用在这种特殊情况下有效的硬编码树对象 ID)。
git update-ref AUTO_MERGE f9e3c4cb... # just for this particular case
但这并没有任何效果。新提交仍然只有一个父级,而不是两个。如何手动创建两个父级的提交?
在检查中间状态时我无法检测到的
git commit --no-ff <ref>
幕后发生了什么?
git diff HEAD~4..HEAD
为空(并且它应该,因为你断言第一个命令不会更改工作树),你可以通过凭空创建提交来获得相同的结果,而无需移动:
git merge $( git commit-tree -p HEAD~4 -p HEAD -m "The comment for the merge commit" HEAD^{tree} )