git rebase squash 获取第二条消息(如修复)

git rebase squash take second message (like fixup)

假设我有一些提交:

<sha1> bug due to function1
<sha2> bug due to function2
... other commits

并且我想将提交 1 和提交 2 压缩在一起,仅保留 second 提交的消息,然后我将使用 git rebase -i,编辑为:

pick <sha1> bug due to function1
squash <sha2> bug due to function2
... other commits

而且我总是需要编辑合并的消息并删除第一条消息。

我知道我可以重新安排提交并像这样使用 fixup

pick <sha2> bug due to function2
fixup <sha1> bug due to function1
pick <sha3> other commit

但是我有这样的风险,颠倒两个提交的顺序,可能会有一些冲突。

我怎样才能以更少的操作获得相同的结果,尤其是避免编辑合并的消息。请注意,在提交 1 之前和提交 2 之后可能有很多提交。

首先执行 git log 查看最后四次提交的哈希值。重置为您要压缩的两次提交之前的最后一次提交:

git reset --hard <sha0>

...您要压缩的两个提交之前的最后一个提交在哪里。

然后,依次尝试以下命令:

git cherry-pick -n <sha1>
git cherry-pick -n <sha2>
git commit
git cherry-pick    <sha3>

前两个命令将在您的本地暂存沙箱中组合 <sha1><sha2>。当您不带参数执行 git commit 时,它将使用上次提交的提交消息提交更改。您需要做的就是在查看提交消息后退出编辑器。最后的 cherry-pick 应用最后一次提交,未更改。

全自动版本。假设一个 git 历史是这样的:

... (as many commits as you like)
6acdc6f - commit message 3
46c9468 - commit message 2
9b28fd5 - commit message 1

然后变基以将提交 1 和 2 压缩在一起并保留 提交消息 2:

git rebase -i 9b28fd5~

然后像这样编辑:

pick 9b28fd5 commit message 1
pick 46c9468 commit message 2
exec HH=$(git rev-parse HEAD); git reset --soft HEAD~2; git commit -C $HH
pick 6acdc6f commit message 3

shell 命令的简要说明:

HH=$(git rev-parse HEAD) # store the HEAD sha1 in a variable; since the next call will change HEAD to HEAD~2
git reset --soft HEAD~2  # destroy the last two commits keeping the changes staged.
git commit -C $HH        # now commit all changes reusing the commit message from commit2 (using the sha1 that we saved in the variable)

由于 OP 的问题,请注意交互式变基的行为确实发生了变化(现在已修复,2021 年第一季度)。

当“git rebase -i"(man)处​​理fixup insn时,没有理由清理commit log消息,但是我们做了通常的stripspace处理。
Git 2.31(2021 年第一季度)已更正此问题。

参见 commit f7d42ce (28 Jan 2021) by Johannes Schindelin (dscho)
(由 Junio C Hamano -- gitster -- in commit 7e94720 合并,2021 年 2 月 10 日)

rebase -i: do leave commit message intact in fixup! chains

Reported-by: Vojtěch Knyttl
Helped-by: Martin Ågren
Signed-off-by: Johannes Schindelin

In 6e98de7 ("sequencer (rebase -i): add support for the 'fixup' and 'squash' commands", 2017-01-02, Git v2.12.0-rc0 -- merge listed in batch #8), this developer introduced a change of behavior by mistake: when encountering a fixup! commit (or multiple fixup! commits) without any squash! commit thrown in, the final git commit(man) was invoked with --cleanup=strip.
Prior to that commit, the commit command had been called without that --cleanup option.

Since we explicitly read the original commit message from a file in that case, there is really no sense in forcing that clean-up.

We actually need to actively suppress that clean-up lest a configured commit.cleanup may interfere with what we want to do: leave the commit message unchanged.