On Tue, 06 Mar 2012 01:13:26 -0400, David Bremner <[email protected]> wrote:
> On Mon, 05 Mar 2012 22:10:48 -0400, David Bremner <[email protected]> wrote:
> > 
> > One thing I think I got wrong in round 1 is the blanket use of
> > --ancestor-path. Consider the following branch structure, where 
> > upstream has been merged into master several times.
> 
> Here is a revised version. Let's pretend there were all kinds of useful
> discussions in some other medium that led to this.

Here is another revision.  Two convenience commands are edited

     list - list available marked commits
     edit - edit the metadata for a commit.

The latter is more a recovery tool in case e.g. the user screws up a
conflict resolution. The former I expect to be the primary interface for
people working interactively with the tool.

There is (unfortunately) a bunch of new code to make it more robust
dealing with conflicts and badly formatted notes.

The docs could still be improved with some example workflows, and there
is no builtin help.

>From 904161f8780e342a4042806ef37a5fb2531e1800 Mon Sep 17 00:00:00 2001
From: David Bremner <[email protected]>
Date: Mon, 5 Mar 2012 12:17:46 -0400
Subject: [PATCH] git-debpatch: select commits to be exported as quilt patches

This script allows the user to mark commits for export as
quilt-patches in debian/patches. gitpkg support is provided via
hooks/quilt-patches-deb-export-hook.
---
 debian/rules                        |    4 +-
 git-debpatch                        |  160 +++++++++++++++++++++++++++++++++++
 git-debpatch.1                      |   82 ++++++++++++++++++
 gitpkg.1                            |    3 +-
 hooks/quilt-patches-deb-export-hook |    3 +
 5 files changed, 249 insertions(+), 3 deletions(-)
 create mode 100755 git-debpatch
 create mode 100644 git-debpatch.1

diff --git a/debian/rules b/debian/rules
index 7afba92..f5c4ae5 100755
--- a/debian/rules
+++ b/debian/rules
@@ -18,9 +18,9 @@ install-indep:
 	dh_testdir
 	dh_testroot
 	dh_clean -k -i 
-	dh_install -i gitpkg git-debimport usr/bin
+	dh_install -i gitpkg git-debimport git-debpatch usr/bin
 	dh_install -i hooks usr/share/gitpkg
-	dh_installman -i gitpkg.1 git-debimport.1
+	dh_installman -i gitpkg.1 git-debimport.1 git-debpatch.1
 
 binary-common:
 	dh_testdir
diff --git a/git-debpatch b/git-debpatch
new file mode 100755
index 0000000..28834b0
--- /dev/null
+++ b/git-debpatch
@@ -0,0 +1,160 @@
+#!/bin/bash
+
+set -e
+
+GN="git notes --ref debpatch"
+patch_dir=debian/patches
+
+# get_field ref Field
+#
+function get_field() {
+    shorthash=$(git rev-parse --short $1)
+
+    mapfile -t lines < \
+       <(${GN} show $1 2>/dev/null | sed -n "s/^$2: \(.*\)$/\1/p")
+
+    if [ ${#lines[*]} -gt 1 ]; then 
+	echo "Commit $shorthash has multiple values for $2" 1>&2
+	printf "Please fix with e.g.\n\t$ git debpatch edit $shorthash\n" 1>&2
+	exit 1
+    fi
+    echo "${lines[0]}"
+}
+
+# set_field ref Field Val
+function set_field() {
+    other_fields=$(${GN} show $1 2>/dev/null | grep -v "^$2:")
+    printf "$2: $3\n$other_fields" | ${GN} add -f -F- $1 2>/dev/null
+}
+
+# remove_field ref Field
+function remove_field() {
+    other_fields=$(${GN} show $1 2>/dev/null | grep -v "^$2:")
+    if [ -n "$other_fields" ] ; then
+	printf "$other_fields" | ${GN} add -f -F- $1 2>/dev/null
+    else
+	$GN remove $1 2>/dev/null
+    fi
+}
+
+function is_exportable() {
+    if result="$(get_field $1 Export)" ; then
+	[ "$result"x = "truex" ]
+    else
+	exit 1
+    fi
+}
+
+function export_one_patch() {
+    if [ -f $patch_dir/series ]; then
+	count=$(wc -l "$patch_dir/series" | cut -f1 -d' ')
+    else
+	mkdir -p "$patch_dir" || exit 1
+	echo "# exported from git by git-debpatch" > "$patch_dir/series"
+	count=1
+    fi
+
+    name=$(git format-patch --start-number $count -1 -o "$patch_dir" $1)
+    echo "$name" | sed -e "s,$patch_dir/,,g" -e 's, ,\n,g' >> "$patch_dir/series"
+}
+
+
+function insist_commits() {
+    if [ $(git rev-parse $1 | wc -l) != $2 ]; then
+	echo "$1 does not expand to exactly $2 commits";
+	exit 1;
+	
+    fi;
+}
+
+function add_tag() {
+    insist_commits $1 1
+    field=${2^}
+    value=${3-true}
+    set_field $ref $field $value
+}
+
+function remove_tag() {
+    insist_commits $1 1
+    field=${2^}
+    value=${3-true}
+    remove_field $1 $field
+}
+
+function do_merge() {
+	if ${GN} merge -q refs/notes/remotes/$remote/debpatch 1>/dev/null 2>&1; then
+	    echo merge complete
+	else
+	    # this is mainly to work around a bug in git-notes
+	    # git notes merge --commit silently fails if inside
+	    # .git/NOTES_MERGE_WORKTREE
+	    echo "DEBPATCH: Starting subshell to resolve conflicts"
+	    echo "DEBPATCH: exit to complete merge"
+	    echo "DEBPATCH: exit 1 to abort";
+	    origdir=$(pwd)
+	    reporoot=$(git rev-parse --show-toplevel)
+	    if cd $reporoot/.git/NOTES_MERGE_WORKTREE && ${SHELL-sh} < /dev/tty ; then
+		cd $reporoot && ${GN} merge --commit
+	    else
+		cd $reporoot && ${GN} merge --abort
+	    fi
+	    cd $origdir
+	fi
+}
+
+function all_notes() {
+    ${GN} list | cut -f2 -d' '
+}
+
+case $1 in
+    edit)
+	${GN} edit $2
+	;;
+    fetch)
+	remote=${2-origin}
+	git fetch $remote refs/notes/debpatch:refs/notes/remotes/$remote/debpatch
+	exit 0;
+	;;
+    list)
+	all_notes | \
+	    while read hash; do
+	       GIT_NOTES_REF=refs/notes/debpatch git show --stat --pretty=format:"%n%h %s%n%N" $hash
+	    done | ${PAGER-less}
+	exit 0
+	;;
+
+    merge)
+	remote=${2-origin}
+	do_merge $remote
+	exit 0
+	;;
+    pull)
+	git debpatch fetch $2 && git debpatch merge $2
+	exit $?
+	;;
+    push)
+	remote=${2-origin}
+	git push $remote refs/notes/debpatch:refs/notes/debpatch
+	exit 0;
+	;;
+    +*)
+	ref=${2-HEAD}
+	add_tag $ref ${1#+} $3
+	;;
+    -*) 
+	ref=${2-HEAD}
+	remove_tag $ref ${1#-} $3
+	;;
+    toquilt)
+	ref=${2-HEAD}
+	git rev-list --reverse $ref | \
+	    while read hash ; do 
+	    if is_exportable $hash; then
+		export_one_patch $hash
+	    fi
+	done
+	;;
+
+    *)
+	GIT_NOTES_REF=refs/notes/debpatch exec git $*
+esac
diff --git a/git-debpatch.1 b/git-debpatch.1
new file mode 100644
index 0000000..34fe71d
--- /dev/null
+++ b/git-debpatch.1
@@ -0,0 +1,82 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH GIT-DEBPATCH 1 "March 5, 2012"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+git\-debpatch \- annotate and optionally export commits
+.SH SYNOPSIS
+.B git\-debpatch
+.RI [ command ]  
+.RI [ params ] 
+
+.SH DESCRIPTION
+This program uses the \fBgit-notes\fP(1) facility to track certain
+metadata about git commits, and optionally export them as quilt patches.
+
+.SH COMMANDS
+
+The following commands are available:
+
+.TP
+.RI + field " [" commitish "] [" value ]
+Add the given field to the notes for commitish (defaulting to HEAD),
+with a default value of "true". The most useful example is "+export"
+
+.TP
+.RI - field " [" commitish ]
+Remove the given field from commitish
+
+
+.TP
+.BR edit " [hash]"
+Edit debpatch notes for given commit (default HEAD).
+This is an "expert" command, be careful.
+
+.TP
+.BR fetch " [remote]"
+Fetch debpatch specific notes from remote (default origin)
+
+.TP
+.BR list
+List known debpatch notes.
+
+.TP
+.BR merge " [remote]"
+Merge debpatch specific notes from remote (default origin).
+
+.TP
+.BR pull " [remote]"
+Fetch and then merge.
+
+
+.TP 
+.BR toquilt " ref-or-range"
+Export the any "+export" patches in the range to debian/patches.  The
+default range is everything reachable from HEAD.  
+\fBNB\fP
+If
+debian/patches/series exists, new patches are appended.
+
+.RE
+Other commands are passed though to git with an appropriate set of
+GIT_NOTES_REF. Useful examples are "git debpatch log" and 
+"git debpatch show".
+
+.SH SEE ALSO
+.BR gitpkg (1),
+.BR git (1)
+.SH AUTHOR
+git-debpatch was written by David Bremner <[email protected]>.
diff --git a/gitpkg.1 b/gitpkg.1
index 1ba10e1..6e0b7cd 100644
--- a/gitpkg.1
+++ b/gitpkg.1
@@ -515,7 +515,8 @@ most cases.  For example:
 .fi
 .hy
 
-
+If the range of commits is prefixed with :debpatch:, then it will be passed to 
+\fBgit-debpatch\fP(1) for export, rather than \fBgit-format-patch\fP.
 
 .SS Hook Library Helpers
 These are even more trivial snippets, for operations which may be shared by
diff --git a/hooks/quilt-patches-deb-export-hook b/hooks/quilt-patches-deb-export-hook
index 98b2aa7..b158779 100644
--- a/hooks/quilt-patches-deb-export-hook
+++ b/hooks/quilt-patches-deb-export-hook
@@ -64,6 +64,9 @@ do_patches (){
 	case $line in
 	    \#*)
 		;;
+	     :debpatch:*)
+		GIT_DIR=$REPO_DIR/.git  git debpatch toquilt ${line//:debpatch:/}
+	        ;;
 	    *)
 		count=$(wc -l "$patch_dir/series" | cut -f1 -d' ')
 		if PATCHES="$(git --git-dir "$REPO_DIR/.git" format-patch --start-number $count -N -o "$patch_dir" "$line")"; then
-- 
1.7.9.1

Reply via email to