2

我已经将一个大型 SVN 存储库迁移到 GIT,并且有大量的连续提交具有同一作者的相同消息。

现在我想自动将提交修复为一个提交。

想法?

4

2 回答 2

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用于从索引上的合并结果生成新树。

然后脚本继续通过将当前提交的父级设置为合并提交的父级的父级,有效地从历史中“删除”这个提交。

如果您还有任何问题,我很乐意提供帮助。做这件事很有趣!

编辑:我已经在 Windows 7 上使用 msysgit 版本 1.9.2 测试了这个脚本。

于 2014-10-14T06:57:27.440 回答
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 我已经开始实验并且不知道你的方式/状态 - 这个答案还没有被接受!

于 2014-10-13T14:54:53.957 回答