在历史中间挤压两个 Git 提交,无需交互式变基

Squash two Git commits in the middle of history without interactive rebase

我正在将旧的 SVN 存储库转换为 Git,其中包括尝试在所有正确的位置获取所有 branches/tags。那部分进展顺利,但有时我想在我的脚本中添加一个历史提交,稍后我想在下一次提交时压缩。问题是我不是一个一个地抓取提交,而是作为一个大组抓取,所以我无法在将它们从 SVN 存储库中拉出时压缩它们。最后,我的存储库如下所示:

* (branch_2, HEAD) commit 5
* commit 4
* commit 3
* SQUASH ME!
* (branch_1) commit 2
* commit 1

我希望能够用 SQUASH ME! 压缩 commit 3,使用交互式变基显然很容易,但在脚本中更具挑战性。我似乎遇到的主要问题是,虽然很容易签出 branch_1 或之前的任何提交,但很难以编程方式请求 after 提交,这对我来说很难预测我需要从 branch_2 返回多少提交。我真的很想能够做这样的事情:

git checkout branch_1+2

有什么指点吗?

你所说的不是 squash,而是 fixup,因为 squash 会要求你交互提交信息,而 修复程序使用来自 HEAD 提交的提交消息

这是一个无需干预即可执行此操作的脚本。

脚本:/usr/bin/git-fixup

#/bin/bash
# This command fixesup one commit onto his parent
set -e

# We need a commit from the first argument of that command
commit=${1:?No commit given as first argument}
startingbranch=$(git rev-parse --abbrev-ref HEAD)

# Checkout the parent of the asked commit
git checkout "$commit"
git checkout HEAD~

# Merge the commit into it's parent, keeping the commit message of the parent
git merge --squash "$commit"
git add .
git add --update
git commit --amend --no-edit

# Store the current commit
newcommit=$(git rev-parse HEAD)

# Rebase the starting branch onto the new commit
git checkout "$startingbranch"
git rebase "$newcommit"

一起使用
git fixup <commit-id>

例如,如果您的历史是:

ce0e2fd (master, HEAD) commit 4
72ab3c4 commit 3
8150939 commit 2
301c1e1 commit 1

您可以执行 git fixup 72ab3c4,这将合并 "commit 3" 和 "commit 2" 作为消息 "commit 2" 的提交,并将您放回主分支。

来自git rebase --help

--autosquash

When the commit log message begins with "squash! ..." (or "fixup! ..."), and there is a commit whose title begins with the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified, and change the action of the moved commit from pick to squash (or fixup).

This option is only valid when --interactive option is used.

如果输入的形式正确,看起来它会完成你想要的一半。

另一半阻止它启动交互式编辑器。幸运的是,编辑器是可配置的,所以我们可以将它设置为无害的东西。

试试这个:

env EDITOR=true git rebase -i --autosquash <from-here>

将编辑器设置为 true(一个简单地成功退出的工具)足以说服 git 继续默认的变基设置,自动压缩应该设置为有用的东西.


或者,如果 --autosquash 不符合您的要求,您可以将 EDITOR 设置为您喜欢的任何脚本:

env EDITOR=mysquasher.sh git rebase -i <from-here>

该脚本可以执行您需要的任何操作,但在您的情况下,它只需要找到包含 "SQUASHME!" 的每一行,然后将下一行的 "pick" 更改为 "fixup" .这可能最容易用 awk 实现:

#!/bin/bash -e

awk -- 'BEGIN {dosquash=0}
        dosquash==1 {gsub(/^pick/, "fixup"); dosquash=0}
        /SQUASHME/ {dosquash=1}
        {print}' "" > /tmp/tmp.$$

mv /tmp/tmp.$$ ""