没有内置的东西可以做到这一点;您必须编写自己的脚本。
为此,请从 git rev-list 或 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 步骤中设置所有适当的环境变量来“修复”此问题(请参阅其文档)。