Странная проблема с git merge - PullRequest
4 голосов
/ 18 февраля 2010

Вот так выглядит gitk в одном из наших проектов:

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

Это, судя по всему, из всего, что мы можем сказать, произошло после того, как было выполнено одно "git merge" с удаленной веткой - мы не уверены, почему или что происходит. Есть идеи, что здесь произошло?

Что еще важнее, как лучше это исправить? Эти коммиты слияния пусты, но при выполнении git rebase -i коммиты слияния обычно не появляются.

Самое главное, я бы предпочел не делать историю несовместимой с другими клонами, то есть они все равно должны иметь возможность тянуть / толкать / сливаться с ней. Это вообще возможно?

1 Ответ

2 голосов
/ 18 февраля 2010

Это результат того, что кто-то обновил свой проект, используя git pull (или, что эквивалентно, git fetch и git merge). Представьте, что ваш репозиторий выглядит так:

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

То есть вы сделали коммиты A, B и C поверх того, что было в исходном репо.

В то же время некоторые другие вносят изменения и отправляют их в общий репозиторий. Если вы затем запустите 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 отличается от F и C, поэтому его следует рассматривать по-разному.

Теперь, если вы нажмете это изменение, у вас будет это:

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]

Теперь, если вы продолжите делать это (и если многие продолжат делать это), вы получите дерево, подобное изображенному на вашей картинке. Это не «неправильно», но многим это не нравится, потому что это очень усложняет историю развития.

Решение этой проблемы - научить людей делать перебаз. Перебазировка (как вы заметили) удалит бесполезные коммиты слияния и даст вам гораздо более чистую историю. В приведенном выше случае вы получите линейную историю развития. Это намного легче следовать. Однако вам нужно знать, что после перебазирования вам нужно пересобрать и повторно протестировать свой код ... просто потому, что код легко объединяется, не означает, что результат верный.

Если вы используете центральный репозиторий для обмена официальной линией разработки, вы можете реализовать ловушку предварительного получения, которая обнаруживает эти автоматические («глупые» / «бесполезные») коммиты слияния и отклоняет принудительное принудительное действие пользователя либо перебазировать. На самом деле, вы хотите, чтобы хук искал сообщение о фиксации слияния по умолчанию ... поэтому, если вы действительно хотите сохранить коммит слияния (иногда это имеет смысл), вы должны по крайней мере написать интеллектуальное сообщение о коммите.

Вот пример крючка. Я снял кучу лишних вещей, поэтому не смог их протестировать.

#!/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
...