--- /dev/null
+#!/bin/sh
+
+git rev-list --first-parent "stable..HEAD" | while read commit ; do
+ number_of_parents=$(git rev-list -n 1 --parents $commit | sed 's/ /\n/g' | grep -v $commit | wc -l)
+ if [[ $number_of_parents > 1 ]] ; then
+ # For each parent
+ git rev-list --no-walk --parents $commit | sed 's/ /\n/g' | grep -v $commit | while read parent ; do
+ echo "merge=$commit parent=$parent"
+ # Does this parent have any children besides us?
+ #
+ # List the parents of all branch commits (after stable/parent), find
+ # those that include our parent, get their sha1, remove our merge
+ git rev-list --parents --branches ^stable "^$parent" | grep $parent | gawk '{print $1}' | grep -v $commit | while read child ; do
+ echo "child $child"
+ git rev-list "$child" "^$commit"
+ done
+ # Find any commits in the parent (and another branch) but not us--that means we need it
+ # number_missing=$(git rev-list "$parent" --branches "^HEAD" | wc -l)
+ # if [[ $number_missing > 0 ]] ; then
+ # git rev-list "$parent" --branches "^HEAD" | xargs git name-rev
+ # fi
+ done
+ fi
+done
+
--- /dev/null
+#!/bin/sh
+
+#
+# When tagging the candidate branch, and commit "A"
+# has gone onto a topic branch that we previously
+# merged, reject the tag.
+#
+# * -- * -- * -- A topic A
+# \ \
+# * -- * topic B
+# \ \
+# * candidate
+#
+# We want to detect A when trying to tag candidate.
+#
+# This also implements candidate protection, e.g.
+# only merges are allowed on candidate branches.
+
+# Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+echo "updating $refname $oldrev $newrev"
+fail=0
+
+case "$refname" in
+ refs/tags/*)
+ # First make sure stable hasn't moved on us
+ baserev=$(git merge-base stable $newrev)
+ stablerev=$(git rev-parse stable)
+ if [ "$baserev" != "$stablerev" ] ; then
+ echo "----------------------------------------------------"
+ echo
+ echo " Rejecting $refname because you need to merge:"
+ echo
+ git rev-list "$baserev..$stablerev" | xargs git name-rev
+ echo
+ echo "----------------------------------------------------"
+ fail=1
+ else
+
+ # Now go back to stable and iterate through looking for commits on
+ # our merge parents that are not within us
+ git rev-list --first-parent "stable..$newrev" | while read commit ; do
+ number_of_parents=$(git rev-list -n 1 --parents $commit | sed 's/ /\n/g' | grep -v $commit | wc -l)
+ if [[ $number_of_parents > 1 ]] ; then
+ # For each parent
+ git rev-list --no-walk --parents $commit | sed 's/ /\n/g' | grep -v $commit | while read parent ; do
+ # Does this parent have any children besides us?
+ #
+ # List the parents of all branch commits (after stable/parent), find
+ # those that include our parent, get their sha1, remove our merge
+ git rev-list --parents --branches ^stable "^$parent" | grep $parent | gawk '{print $1}' | grep -v $commit | while read child ; do
+ number_missing=$(git rev-list "$child" "^$newrev" | wc -l)
+ if [[ $number_missing > 0 ]] ; then
+ echo "----------------------------------------------------"
+ echo
+ echo " Rejecting $refname because you need to merge:"
+ echo
+ git rev-list "$child" "^$newrev" | xargs git name-rev
+ echo
+ echo "----------------------------------------------------"
+ fail=1
+ fi
+ done
+
+ # Find any commits in the parent (and another branch) but not us--that means we need it
+ # number_missing=$(git rev-list "$parent" --all "^$newrev" | wc -l)
+ # if [[ $number_missing > 0 ]] ; then
+ # echo "----------------------------------------------------"
+ # echo
+ # echo " Rejecting $refname because you need to merge:"
+ # echo
+ # git rev-list "$parent" --all "^$newrev" | xargs git name-rev
+ # echo
+ # echo "----------------------------------------------------"
+ # exit 1
+ # fi
+ done
+ fi
+ done
+
+ fi
+ ;;
+
+ refs/heads/candidate*)
+ # Look at commits between stable and us--ignoring those brought in by merges
+ git rev-list --first-parent "$newrev" ^stable | while read commit ; do
+ number_of_parents=$(git rev-list -n 1 --parents $commit | sed 's/ /\n/g' | grep -v $commit | wc -l)
+ if [[ $number_of_parents == 1 ]] ; then
+ echo "----------------------------------------------------"
+ echo
+ echo " Candidate branches must be only merges"
+ echo
+ echo "----------------------------------------------------"
+ fail=1
+ fi
+ done
+ ;;
+
+ *)
+ ;;
+esac
+
+exit "$fail"
+
--- /dev/null
+#!/bin/sh
+
+test_description='server update ensure merged'
+
+. ./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 &&
+ git checkout -b stable &&
+ git push origin stable
+'
+
+install_server_hook 'update-ensure-merged' 'update'
+
+test_expect_success 'pushing just topic is okay' '
+ git checkout -b topic &&
+ echo "$test_name" >a &&
+ git commit -a -m "$test_name on topic" &&
+ git push origin topic
+'
+
+test_expect_success 'if topic moves on, tagging candidate requires a merge' '
+ git checkout -b candidate stable &&
+ git merge topic --no-ff &&
+ git push &&
+
+ git checkout topic &&
+ echo "$test_name on topic" >a &&
+ git commit -a -m "$test_name on topic" &&
+ git push &&
+
+ git checkout candidate &&
+ git tag -a -m "Tagging candidate" deployment-1 &&
+ ! git push --tags 2>push.err &&
+ cat push.err | grep "Rejecting refs/tags/deployment-1 because you need to merge" &&
+ cat push.err | grep "topic" &&
+
+ git merge topic &&
+ git tag -d deployment-1 &&
+ git tag -a -m "Tagging candidate" deployment-1 &&
+ git push --tags
+'
+
+test_expect_success 'if stable moves on, tagging candidate requires a merge' '
+ git checkout stable &&
+ echo "$test_name on stable" >a.stable &&
+ git add a.stable &&
+ git commit -a -m "$test_name on stable" &&
+ git push &&
+
+ git checkout candidate &&
+ git tag -a -m "Tagging candidate" deployment-2 &&
+ ! git push --tags 2>push.err &&
+ cat push.err | grep "Rejecting refs/tags/deployment-2 because you need to merge" &&
+ cat push.err | grep "stable" &&
+
+ git merge stable &&
+ git tag -d deployment-2 &&
+ git tag -a -m "Tagging candidate" deployment-2 &&
+ git push --tags
+'
+
+test_expect_success 'when creating a candidate, it must be a merge' '
+ git checkout -b topic2 stable &&
+ echo "$test_name on topic2" >a &&
+ git commit -a -m "$test_name on topic2" &&
+ git push origin topic2 &&
+
+ git checkout -b candidate2 stable &&
+ git merge topic2 &&
+ ! git push origin candidate2 2>push.err &&
+ cat push.err | grep "Candidate branches must be only merges" &&
+
+ git reset --hard HEAD^ &&
+ git merge --no-ff topic2 &&
+ git push origin candidate2
+'
+
+test_done
+