--- /dev/null
+#!/bin/sh
+
+while read oldrev newrev refname ; do
+ if expr "$oldrev" : '0*$' >/dev/null ; then
+ exit 0
+ fi
+
+ # If the new ref is a merge, it'll have spaces in the parents log (ugly, yes)
+ git log -n 1 --pretty=format:%p $newrev | grep " "
+ if [ $? -ne 0 ] ; then
+ exit 0
+ fi
+
+ # If they are introducing non-merge commits /and/ merge commits, it could
+ # look like:
+ #
+ # * --- * --- * --- O
+ # \ \
+ # N --- N --- M
+ #
+ # They should have done a rebase.
+ #
+ # But if it looks like:
+ #
+ # * --- * --- * --- O
+ # \ \
+ # S --- N --- M
+ #
+ # Then they were correct in doing a merge as S was already shared.
+
+ baserev=$(git merge-base $oldrev $newrev)
+ base_to_old=$(git rev-list $baserev..$oldrev | wc -l)
+ base_to_new=$(git rev-list $baserev..$newrev | wc -l)
+ # includes the base_to_new includes both legs, so subtract to get new leg count
+ new_leg=$(expr $base_to_new - $base_to_old)
+ all_new=$(git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --stdin $oldrev..$newrev | wc -l)
+
+ # echo "new_leg=$new_leg"
+ # echo "all_new=$all_new"
+ if [ $all_new -eq $new_leg ] ; then
+ echo "----------------------------------------------------"
+ echo
+ echo "It looks like you should rebase"
+ echo
+ echo "----------------------------------------------------"
+ exit 1
+ fi
+done
+
--- /dev/null
+#!/bin/sh
+
+test_description='server pre-receive prefer rebase'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo "setup" >a &&
+ git add a &&
+ git commit -m "setup" &&
+ git clone ./. server &&
+ rm -fr server/.git/hooks &&
+ git remote add origin ./server &&
+ git config --add branch.master.remote origin &&
+ git config --add branch.master.merge refs/heads/master &&
+ git fetch
+'
+
+install_server_hook 'pre-receive-prefer-rebase' 'pre-receive'
+
+test_expect_success 'all local changes do not need a merge' '
+ # server is on "setup"
+
+ # make an outstanding change for us--but do not push
+ echo "$test_name" >a.client1 &&
+ git add a.client1 &&
+ git commit -m "$test_name on client1" &&
+
+ # have another client commit (in this case, it is the server, but close enough)
+ cd server &&
+ echo "$test_name" >a.client2 &&
+ git add a.client2 &&
+ git commit -m "$test_name on client2" &&
+
+ # go back to our client and it will merge in our changes
+ cd .. &&
+ git pull &&
+
+ ! git push 2>push.err &&
+ cat push.err | grep "It looks like you should rebase" &&
+ git reset --hard origin/master
+'
+
+test_expect_success 'already shared topic changes do warrant a merge' '
+ # server is on "setup"
+
+ # make a change on topic for us and share it
+ git checkout -b topic master &&
+ echo "$test_name" >a.client1 &&
+ git add a.client1 &&
+ git commit -m "$test_name on client1 and topic" &&
+ git push origin topic &&
+
+ # make an outstanding change that we will have to merge later
+ echo "$test_name again" >>a.client1 &&
+ git commit -a -m "$test_name on client1 and topic again" &&
+
+ # have another client commit to master (in this case, it is the server, but close enough)
+ cd server &&
+ echo "$test_name" >a.client2 &&
+ git add a.client2 &&
+ git commit -m "$test_name on client2" &&
+
+ # go back to our client and it will merge in our changes
+ cd .. &&
+ git checkout master &&
+ git pull &&
+ git merge topic &&
+
+ git push
+'
+
+test_expect_success 'simple commit' '
+ # go back to topic and make a simple commit/push as a sanity check
+ git checkout topic &&
+ echo "$test_name" >>a.client1 &&
+ git commit -a -m "$test_name on client1 and topic" &&
+ git push
+'
+
+test_done
+