奇怪的git合并问题

时间:2010-02-18 13:19:44

标签: git git-rebase git-merge

这就是gitk目前在我们的一个项目中的表现:

https://dl.dropbox.com/u/2582508/gitk.png

显而易见,我们可以说,在一个“git merge”与远程分支完成之后发生了 - 我们不确定为什么或者发生了什么。知道这里发生了什么吗?

更重要的是,解决这个问题的最佳方法是什么?那些合并提交是空的,但是当做“git rebase -i”时,合并提交通常似乎没有出现。

最重要的是,我宁愿不让历史与其他克隆不兼容,即它们仍然应该能够拉/推/合并它。这甚至可能吗?

1 个答案:

答案 0 :(得分:2)

这是有人使用git pull(或等效地,git fetchgit merge)更新项目的结果。想象一下,你的存储库看起来像这样:

o---o---o [origin/master]
         \
          A---B---C [master]

也就是说,您已完成了对原始回购中的提交ABC的提交。

与此同时,其他一些人会进行更改并将其推送到共享存储库。如果然后运行git fetch,您的存储库将如下所示:

o---o---o---D---E---F [origin/master]
         \
          A---B---C [master]

现在,如果您运行git merge(请记住:git pull仅为git fetch,后跟git merge)。你会有这个:

o---o---o---D---E---F [origin/master]
         \           \
          A---B---C---G [master]

假设一切顺利,G可能只是一个“愚蠢的”合并提交;从技术上讲,G的状态与FC不同,因此必须采用不同的方式。

现在,如果您推动此更改,您将拥有以下内容:

o---o---o---D---E---F 
         \           \
          A---B---C---G [origin/master]

如果你继续开发,你会得到这个:

o---o---o---D---E---F 
         \           \
          A---B---C---G [origin/master]
                       \
                        H---I---J [master]

现在,如果你继续这样做(如果很多人继续这样做),你最终会得到一张像你图片中那棵树的树。这不是“错误的”,但许多人不喜欢它,因为它使开发历史很难遵循。

解决这个问题的方法是教人们改变。 Rebasing将(如你所述)远程无用的合并提交并给你一个更清晰的历史记录。在上面的例子中,您将得到一个线性发展历史。这更容易遵循。但是,您需要知道在重新绑定后需要重新构建并重新测试代码...仅仅因为代码合并很容易并不意味着结果是正确的。

如果您正在使用中央存储库来共享官方开发线,则可以实现预接收挂钩,以检测这些自动(“愚蠢”/“无用”)合并提交并拒绝推送 - 强制用户要么变坏。实际上,您希望钩子查找默认的合并提交消息...因此,如果您确实希望保持合并提交(有时这是有意义的),您必须至少编写一个智能提交消息。

这是一个示例钩子。我剥掉了一堆额外的东西,所以我没有去测试它。

#!/bin/bash

# This script is based on Gnome's pre-receive-check-policy hook.
# This script *only* checks for extraneous merge commits.

# Used in some of the messages
server=git.wherever.com

GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)

in_import() {
    test -e "$GIT_DIR/pending"
}

forced() {
    test -n "$GNOME_GIT_FORCE"
}

check_commit() {
    commit=$1

    subject="$(git log $commit -1 --pretty=format:%s)"
    if expr "$subject" : ".*Merge branch.*of.*\(git\|ssh\):" > /dev/null 2>&1; then
          if ! in_import && ! forced ; then
                cat &2
---
The commit:

EOF
        git log $commit -1 >&2
        cat &2

Looks like it was produced by typing 'git pull' without the --rebase
option when you had local changes. Running 'git  pull --rebase' now
will fix the problem. Then please try, 'git push' again. Please see:

  http://live.gnome.org/Git/Help/ExtraMergeCommits
---
EOF
        exit 1
          fi
    fi
}

check_ref_update() {
    oldrev=$1
    newrev=$2
    refname=$3

    change_type=update
    if expr $oldrev : "^0\+$" > /dev/null 2>&1; then
    change_type=create
    fi

    if expr $newrev : "^0\+$" > /dev/null 2>&1; then
          if [ x$change_type = xcreate ] ; then
             # Deleting an invalid ref, allow
                return 0
          fi
          change_type=delete
    fi

    case $refname in
     refs/heads/*)
        # Branch update
        branchname=${refname#refs/heads/}

        range=
        # For new commits introduced with this branch update, we want to
        # run some checks to catch common mistakes.
        #
        # Expression here is same as in post-receive-notify-cia; we take 
        # all the branches in the repo, as "^/ref/heads/branchname", other
       # than the branch we are actualy committing to, and exclude commits
       # already on those branches from the list of commits between
       # $oldrev and $newrev.

        if [ -n "$range" ] ; then
        for merged in $(git rev-parse --symbolic-full-name --not --branches | \
                    egrep -v "^\^$refname$" | \
            git rev-list --reverse --stdin "$range"); do
            check_commit $merged
        done
        fi
        ;;
    esac

    return 0
}

if [ $# = 3 ] ; then
    check_ref_update $@
else
    while read oldrev newrev refname; do
    check_ref_update $oldrev $newrev $refname
    done
fi

exit 0