Это результат того, что кто-то обновил свой проект, используя 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