Proactive refollow.
authorStephen Haberman <stephen@exigencecorp.com>
Mon, 25 Aug 2008 05:33:01 +0000 (00:33 -0500)
committerStephen Haberman <stephen@exigencecorp.com>
Mon, 25 Aug 2008 05:33:01 +0000 (00:33 -0500)
scripts/gc-refollow [new file with mode: 0644]
tests/t5400-script-refollow.sh [new file with mode: 0644]

diff --git a/scripts/gc-refollow b/scripts/gc-refollow
new file mode 100644 (file)
index 0000000..35a6369
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+#
+# Goes through the ensure-follows branches and makes sure
+# the non-excused branches have it merged.
+#
+
+die() {
+       echo >&2 "$@"
+       exit 1
+}
+
+attempt_merge() {
+       source_branch=$1
+       remote_branch=$2
+
+       if [ "$source_branch" == "$remote_branch" ] ; then
+               return
+       fi
+       if [[ $excused =~ " $remote_branch " ]] ; then
+               return
+       fi
+       git branch -r --contains "$source_branch" | grep --quiet "origin/$remote_branch"
+       if [ $? -eq 0 ] ; then
+               return
+       fi
+
+       echo -n "Merging $source_branch into $remote_branch..."
+
+       baserev=$(git merge-base "$source_branch" "origin/$remote_branch")
+       git read-tree -m --trivial $baserev "origin/$remote_branch" "$source_branch"
+       if [ $? -ne 0 ] ; then
+               echo "failed merge"
+               return
+       fi
+
+       new_tree=$(git write-tree)
+       new_commit=$(git commit-tree $new_tree -p "origin/$remote_branch" -p "$source_branch" <<FOO
+Merge $source_branch into $remote_branch
+FOO)
+       if [ "$new_commit" == "" ] ; then
+               echo "failed commit"
+               return
+       fi
+
+       git push origin "$new_commit:$remote_branch" >/dev/null 2>/dev/null
+       if [ $? -ne 0 ] ; then
+               echo "failed push"
+               return
+       fi
+
+       echo "succeeded"
+}
+
+gitconfig=$(git rev-parse origin/gitconfig)
+if [ $? -ne 0 ] ; then
+       echo "gitconfig branch not found"
+       exit 1
+fi
+
+# The source branches
+config_hash=$(git ls-tree $gitconfig | grep config | grep -oP '\w{40}')
+branches=$(git cat-file blob "$config_hash" | grep hooks.update-ensure-follows.branches)
+branches=("${branches#*=}")
+excused=$(git cat-file blob "$config_hash" | grep hooks.update-ensure-follows.excused)
+excused=" ${excused#*=} "
+
+# We're going to merge stuff into the index, so make sure it's okay
+git diff-index --cached --quiet HEAD -- || die "refusing to refollow--your index is not clean"
+
+# Get the latest remote refs
+git fetch
+
+# So we can put the index back after we screw with it
+original_head=$(git rev-parse HEAD)
+
+for source_branch in ${branches[@]} ; do
+       git branch -r | grep -v HEAD | while read line ; do
+               if [[ "$line" =~ origin/(.*) ]] ; then
+                       attempt_merge $source_branch ${BASH_REMATCH[1]}
+               fi
+       done
+done
+
+# Put the index back
+git read-tree "$original_head"
+
diff --git a/tests/t5400-script-refollow.sh b/tests/t5400-script-refollow.sh
new file mode 100644 (file)
index 0000000..911f177
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='script refollow'
+
+. ./test-lib.sh
+
+export PATH=$PATH:../../scripts
+
+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 checkout -b stable &&
+       git push origin stable
+'
+
+test_expect_success 'setup gitconfig' '
+       make-gitconfig-branch &&
+       git checkout gitconfig &&
+       echo "hooks.update-ensure-follows.branches=stable" >>config &&
+       echo "hooks.update-ensure-follows.excused=master gitconfig" >>config &&
+       git commit -a -m "enable update-ensure-follows" &&
+       git push origin gitconfig
+'
+
+test_expect_success 'make topic1 then move stable' '
+       git checkout -b topic1 stable &&
+       echo "$test_name" >a.topic1 &&
+       git add a.topic1 &&
+       git commit -m "$test_name on topic1" &&
+       git push origin topic1 &&
+
+       git checkout stable &&
+       echo "$test_name" >a &&
+       git commit -a -m "$test_name on stable" &&
+       git push
+'
+
+test_expect_success 'refollow fails with dirty index' '
+       echo "$test_name" >a &&
+       git add a &&
+       ! gc-refollow 2>refollow.err &&
+       cat refollow.err | grep "refusing to refollow--your index is not clean" &&
+       ! git reset a
+'
+
+test_expect_success 'refollow topic1 onto stable' '
+       echo "$test_name" >a &&
+       git commit -a -m "move stable" &&
+       git push origin stable &&
+       gc-refollow >refollow.out &&
+       cat refollow.out | grep "Merging stable into topic1...succeeded"
+
+       git checkout topic1 &&
+       git pull origin topic1 &&
+       cat a | grep "$test_name"
+'
+
+test_expect_success 'refollow does not double tap' '
+       # Still on topic1
+       head=$(git rev-parse HEAD) &&
+       gc-refollow &&
+       git pull origin topic1 &&
+       git rev-parse HEAD | grep $head
+'
+
+test_expect_success 'refollow respects excused' '
+       git checkout gitconfig &&
+       head=$(git rev-parse HEAD) &&
+
+       git checkout stable &&
+       echo "$test_name" >a &&
+       git commit -a -m "move stable" &&
+       git push origin stable &&
+
+       gc-refollow &&
+
+       git checkout gitconfig &&
+       git pull origin gitconfig &&
+       git rev-parse HEAD | grep $head
+'
+
+test_expect_success 'refollow continues on conflict' '
+       git checkout -b topic2 stable &&
+       echo "$test_name" >a &&
+       git commit -a -m "create topic2" &&
+       git push origin topic2 &&
+
+       git checkout stable &&
+       echo "$test_name" >a &&
+       git commit -a -m "move stable" &&
+       git push origin stable &&
+
+       gc-refollow > refollow.out &&
+       cat refollow.out | grep "Merging stable into topic1...succeeded"
+       cat refollow.out | grep "Merging stable into topic2...failed merge"
+'
+
+test_done
+