没有内置的东西可以做到这一点。您将不得不编写自己的脚本。
为此,请从git rev-list
or开始git log
(两者基本相同,但命令行选项略有不同)遍历您要扫描的所有提交。您的目标是将提交复制但压缩到一个新的临时分支上。例如,假设所有提交都在分支上进行feature
并且要合并到分支target
中,您可以获取要检查的提交列表:
git rev-list --reverse --topo-order target..feature > /tmp/list
此处的输出是提交 SHA-1 ID 的列表。对于每个提交,您都需要找到作者,可能还有提交消息:
while read sha1; do
author_name=$(git log -n 1 --format=%an $sha1)
...
done < /tmp/list
如果当前作者名与之前的作者名相同,则只需要累积此提交,但如果不同,则需要发出提交 ID。由于$author_name
最初未设置它将是空字符串,因此第一次提交不会匹配以前的作者,但您必须特别处理这个(连同最终提交 ID),因为您总是想累积第一次提交并采取行动在最后一次提交之后或作者更改之后。因此,该...
部分有点复杂。我们还需要一些设置工作来创建并进入一个临时分支,它的第一次提交指向分支的尖端target
。我们将在这里使用匿名分支,而不是使用命名的临时分支。
最后,进行压缩提交的方法特别棘手:最简单的方法是使用管道命令,git commit-tree
然后推进临时分支。
将所有这些放在一起,我们得到以下完全未经测试的代码:
# add new squash-style commit using commit $1
make_squash_commit() {
local sha1=$1 tree new_sha1
tree=$(git rev-parse $sha1^{tree})
new_sha1=$(git commit-tree $tree -p HEAD)
git update-ref -m "squash $sha1" HEAD $new_sha1
}
set -e
git rev-list --reverse --topo-order target..feature > /tmp/list
git checkout --detach target
: > /tmp/accum_log
prev_sha1=""
while read sha1; do
author_name=$(git log -n 1 --format=%an $sha1)
if [ "$author_name" != "$prev_name" -a -n "$prev_sha1" ]; then
make_squash_commit $prev_sha1 < /tmp/accum_log
: > /tmp/accum_log
fi
prev_name="$author_name"
prev_sha1=$sha1
git log -n 1 --format="%B" $sha1 >> /tmp/accum_log
done < /tmp/list
if [ -z "$prev_sha1" ]; then
echo "Warning: no commits found to squash!"
sha1=$(git rev-parse target)
fi
# squash final commit, then give anonymous branch a name
make_squash_commit $prev_sha1 < /tmp/accum_log
git checkout -b temp_branch
rm /tmp/list /tmp/accum_log
这个脚本有一个故意的缺陷:它使用当前日期和时间以您作为每个提交者的作者和提交者进行所有新提交(这是适当的,因为您将多个作者的提交弄得一团糟,挤压不管他们是否同意)。您可以通过在步骤中设置所有适当的环境变量来“修复”此问题git commit-tree
(请参阅其文档)。