我已将一个大型SVN存储库迁移到GIT,并且有大量连续提交具有同一作者的相同消息。
现在我想自动将提交修复为一次提交。
思想?
答案 0 :(得分:2)
我终于能够解决问题,像往常一样,这是一个人为错误。
我在这里提供的脚本是为bash编写的,但正如我从实验中看到的那样,这不应该是一个问题。
这是脚本,我将在下面详细解释:
#!/bin/bash
author="$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>"
msg="$(cat)"
tree="$1"
parents=()
shift
while getopts ":p:" opt; do
case $opt in
p)
parents=(${parents[*]} $OPTARG)
;;
?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
create_parents_option() {
parentsstring=""
for parent in $@; do
parentsstring+="-p $parent "
done
echo "$parentsstring"
}
get_msg() {
git log -1 --format="%B" $1
}
get_author() {
git log -1 --format="%aN <%aE>" $1
}
squash_commit() {
if [ "x$author" == "x$(get_author $1)" ] && [ "x$msg" == "x$(get_msg $1)" ]; then
git read-tree -m --aggressive ${1}^{tree} $tree >/dev/null
tree=$(git write-tree)
parents=($(git log --format=%P -1 $1))
fi
}
if [[ ${#parents[@]} == 1 ]]; then
squash_commit ${parents[0]}
fi
git commit-tree $tree $(create_parents_option ${parents[@]}) -m "$msg"
您可以通过以下方式执行脚本:
git filter-branch --commit-filter "$(cat /path/to/the/script)"
该脚本将检查当前提交是否由同一作者提交并使用与之前相同的消息。
如果是这种情况,它会在使用合并给定树的git read-tree -m
并将结果写入索引之前将当前提交的树与提交的更改合并。
之后git write-tree
用于从索引上的合并结果生成新树。
然后脚本继续将当前提交的父级设置为合并提交的父级的父级,实际上&#34;删除&#34;这是历史的承诺。
如果您有任何其他问题,我很乐意提供帮助。这很有趣!
编辑:我已在Windows 7上使用msysgit版本1.9.2测试了此脚本。
答案 1 :(得分:1)
我还没有足够的声誉发表评论。
@Zeeker 我试过你的剧本,但我收到了这个错误:
Rewrite 34eb354e01107e111ee0f0a332bc78455bb41bba (1/5)git commit-tree: 52: Syntax error: "(" unexpected
could not write rewritten commit
像你说的那样运行它:
git filter-branch --commit-filter "$(cat filter.sh)"
运行git 1.7.10.4
有什么想法吗?
debian / bin / sh上的编辑:指向破折号而不是bash。使用破折号执行提交过滤器,这会导致这些错误。 Dash无法处理此数组语法。我找到的唯一解决方案是将/ bin / sh的符号链接从破折号更改为bash。
答案 2 :(得分:0)
这是我的第一个版本,但它需要更多测试,我确信它可以更智能:
#!/bin/bash
git filter-branch --force --prune-empty --tree-filter 'GIT_PARANT=$(git log -n 2 $GIT_COMMIT --pretty=format:"%h" | wc -l); if [ "$GIT_PARANT" = "1" ]; then GIT_SUBJECT=$(git log -n 1 $GIT_COMMIT --pretty=format:"%s" | sed -e "s/ \\+/ /g" -e "s/^ *//" -e "s/ *\$//"); GIT_LAST_SUBJECT=$(git log -n 1 $GIT_COMMIT~1 --pretty=format:"%s" | sed -e "s/ \\+/ /g" -e "s/^ *//" -e "s/ *\$//"); GIT_LAST_AUTHOR=$(git log -n 1 $GIT_COMMIT~1 --pretty=format:"%an <%ae>"); GIT_LAST_COMMITTER=$(git log -n 1 $GIT_COMMIT~1 --pretty=format:"%cn <%ce>"); echo "\n - test subject: $GIT_SUBJECT <-> $GIT_LAST_SUBJECT\n - test author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> <-> $GIT_LAST_AUTHOR\n - test committer: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> <-> $GIT_LAST_COMMITTER"; if [ "$GIT_SUBJECT" = "$GIT_LAST_SUBJECT" ] && [ "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" = "$GIT_LAST_AUTHOR" ] && [ "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = "$GIT_LAST_COMMITTER" ]; then git reset $GIT_COMMIT~1; else echo "...pick"; fi; else echo "\n...pick"; fi' --tag-name-filter cat -- --all
@Zeeker我已经开始实验并且不知道你的方式/状态 - 这个答案还没有被接受!