From 8767da729516725120027f37f65436e26137e45f Mon Sep 17 00:00:00 2001
From: Simo Sorce <idra@samba.org>
Date: Sat, 23 Mar 2013 14:40:00 -0400
Subject: [PATCH] Add post-merge tool to update git tree

In some cases it is not possible to add hooks to the master
tree however it is possible to easily pull from it into the
patchwork server.

This post-merge hook allows to mark patch status on pull with
a merge hook instead of on a push with post update hook.

Extends utils.py so it is callable from the hook and exposes
an utility to set the patch status by patch hash
---
 apps/patchwork/utils.py | 52 +++++++++++++++++++++++++++++++++-
 tools/post-merge.hook   | 75 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+), 1 deletion(-)
 create mode 100755 tools/post-merge.hook

diff --git a/apps/patchwork/utils.py b/apps/patchwork/utils.py
index 1771167..3b48613 100644
--- a/apps/patchwork/utils.py
+++ b/apps/patchwork/utils.py
@@ -29,7 +29,7 @@ from django.db.models import Max
 from django.db.utils import IntegrityError
 from patchwork.forms import MultiplePatchForm
 from patchwork.models import Bundle, Project, BundlePatch, UserProfile, \
-        PatchChangeNotification, EmailOptout
+        PatchChangeNotification, EmailOptout, Patch, State
 
 def get_patch_ids(d, prefix = 'patch_id'):
     ids = []
@@ -206,3 +206,53 @@ def send_notifications():
         delete_notifications()
 
     return errors
+
+def patch_set_state(patch_hash, state_name):
+    """Update patch state: Inputs are patch hash and state name"""
+    try:
+        patch = Patch.objects.get(hash = patch_hash)
+        patch.state = State.objects.get(name = state_name)
+        patch.save()
+        return True
+    except:	 
+        raise
+
+def main(args):
+    from optparse import OptionParser
+
+    usage = "usage: %prog [options] command"
+    parser = OptionParser(usage=usage)
+    parser.add_option('-#', '--hash',
+                      dest = 'patch_hash', help = 'Patch Hash')
+    parser.add_option('-s', '--state',
+                      dest = 'state_name', help = 'State Name')
+
+    (options, args) = parser.parse_args()
+
+    if len(args) != 1:
+        parser.print_help()
+        sys.exit(-1)
+
+    if args[0] == "set":
+        if not options.patch_hash:
+            print "Missing Patch Hash\n"
+            parser.print_help()
+            sys.exit(-1)
+
+        if not options.state_name:
+            print "Missing State Name\n"
+            parser.print_help()
+            sys.exit(-1)
+
+        try:
+            patch_set_state(options.patch_hash, options.state_name)
+        except:
+            print "E: Failed to update patch!\n"
+
+    else:
+        parser.print_help()
+        sys.exit(-1)
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(main(sys.argv))
diff --git a/tools/post-merge.hook b/tools/post-merge.hook
new file mode 100755
index 0000000..7cba308
--- /dev/null
+++ b/tools/post-merge.hook
@@ -0,0 +1,75 @@
+#!/bin/bash
+#
+# Git post-merge hook to update Patchwork patches after Git pulls
+#
+# Copyright © 203 Simo Sorce <simo@samba.org>
+#
+# Based on code from the post-receive hoof from:
+# Copyright © 2010 martin f. krafft <madduck@madduck.net>
+# Released under the GNU General Public License v2 or later.
+set -eu
+
+STATE_MAP="refs/heads/master:Accepted"
+PWDIR=/home/patchwork/patchwork/apps
+
+BASE_DIR=$(git rev-parse --show-toplevel)
+STATE_DIR=${BASE_DIR}/.git/patchwork
+
+do_exit=0
+trap "do_exit=1" INT
+
+get_patchwork_hash()
+{
+  local hash
+  hash=$(git show $1 | python $PWDIR/patchwork/parser.py --hash)
+  echo $hash
+  test -n "$hash"
+}
+
+set_patch_state()
+{
+    PYTHONPATH=${PWDIR} DJANGO_SETTINGS_MODULE=settings python ${PWDIR}/patchwork/utils.py -# ${1} -s ${2} set
+}
+
+update_patches()
+{
+  local cnt; cnt=0
+  for rev in $(git rev-list --no-merges --reverse ${1}..${2}); do
+    if [ "$do_exit" = 1 ]; then
+      echo "I: exiting..." >&2
+      break
+    fi
+    hash=$(get_patchwork_hash $rev) \
+      || { echo "E: failed to hash rev $rev." >&2; continue; }
+    reason="$(set_patch_state $hash $3)" \
+      || { echo "E: failed to update patch ${reason:+: $reason}." >&2; continue; }
+    echo "I: patch $hash updated using rev $rev." >&2
+    cnt=$(($cnt + 1))
+  done
+  echo "I: $cnt patch(es) updated to state $3." >&2
+}
+
+if [ ! -d ${STATE_DIR} ]; then
+    mkdir -p ${STATE_DIR}
+fi
+
+CUR_REFS=$(git show-ref --heads |cut -d ' ' -f 2)
+
+for refname in $CUR_REFS; do
+    BRANCH=$(echo ${refname} | cut -d '/' -f 3)
+    newrev=$(git rev-parse ${refname})
+    if [ ! -f ${STATE_DIR}/${BRANCH} ]; then
+        echo ${newrev} > ${STATE_DIR}/${BRANCH}
+        continue
+    fi
+    oldrev=$(cat ${STATE_DIR}/${BRANCH})
+    echo ${newrev} > ${STATE_DIR}/${BRANCH}
+
+    for i in $STATE_MAP; do
+        key="${i%:*}"
+        if [ "${key}" = "${refname}" ]; then
+            update_patches ${oldrev} ${newrev} ${i#*:}
+            break
+        fi
+    done
+done
-- 
1.7.12.1

