320 lines
9.1 KiB
Diff
320 lines
9.1 KiB
Diff
guilt: enhanced patch queue management
|
|
|
|
guilt prefers to track the status and series of patches
|
|
under .git/patches. But this location doesn't allow the
|
|
status of a quilt queue to be committed to a secondary
|
|
repository and later restored.
|
|
|
|
This change does three things:
|
|
|
|
- allows GUILT_BASE to be changed (with a default to "wrs")
|
|
- allows shadow tracking of the patches (for rebase)
|
|
- enhances the header detection and creation of patches
|
|
as they are pushed onto the tree.
|
|
|
|
Upstream-Status: Inappropriate [oe-specific]
|
|
|
|
Signed-off-by: Bruce Ashfield <bruce.ashfield@windriver.com>
|
|
|
|
---
|
|
guilt | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
|
|
1 file changed, 159 insertions(+), 24 deletions(-)
|
|
|
|
--- a/guilt
|
|
+++ b/guilt
|
|
@@ -153,14 +153,16 @@ get_branch()
|
|
|
|
verify_branch()
|
|
{
|
|
- [ ! -d "$GIT_DIR/patches" ] &&
|
|
+ [ ! -d "$GUILT_DIR" ] &&
|
|
die "Patches directory doesn't exist, try guilt-init"
|
|
- [ ! -d "$GIT_DIR/patches/$branch" ] &&
|
|
+ [ ! -d "$GUILT_DIR/$branch" ] &&
|
|
die "Branch $branch is not initialized, try guilt-init"
|
|
- [ ! -f "$GIT_DIR/patches/$branch/series" ] &&
|
|
+ [ ! -f "$GUILT_DIR/$branch/series" ] &&
|
|
die "Branch $branch does not have a series file"
|
|
- [ ! -f "$GIT_DIR/patches/$branch/status" ] &&
|
|
+ [ ! -f "$GUILT_DIR/$branch/status" ] &&
|
|
die "Branch $branch does not have a status file"
|
|
+ [ -f "$GUILT_DIR/$branch/applied" ] &&
|
|
+ die "Warning: Branch $branch has 'applied' file - guilt is not compatible with stgit"
|
|
[ -f "$GIT_DIR/patches/$branch/applied" ] &&
|
|
die "Warning: Branch $branch has 'applied' file - guilt is not compatible with stgit"
|
|
}
|
|
@@ -339,6 +341,17 @@ BEGIN{}
|
|
'
|
|
}
|
|
|
|
+# usage: do_get_only_patch patchfile
|
|
+# similar to do_get_patch except everything leading up to
|
|
+# the first diff line and after the last chunk are removed
|
|
+do_get_only_patch()
|
|
+{
|
|
+ cat "$1" | awk '
|
|
+BEGIN{}
|
|
+/^(diff )/,/^(-- |END{})/
|
|
+' | sed '/^-- *$/D'
|
|
+}
|
|
+
|
|
# usage: do_get_header patchfile
|
|
do_get_header()
|
|
{
|
|
@@ -352,8 +365,13 @@ do_get_header()
|
|
BEGIN{skip=0}
|
|
/^Subject:/ && (NR==1){print substr($0, 10); next}
|
|
/^From:/{skip=1; next}
|
|
+/^Author:/{skip=1; next}
|
|
+/^Date:/{skip=1; next}
|
|
+/^commit/{skip=1; next}
|
|
/^[ \t\f\n\r\v]*$/ && (skip==1){skip=0; next}
|
|
/^(diff |---$|--- )/{exit}
|
|
+/^diff --git/{exit}
|
|
+/^Index: /{exit}
|
|
{print $0}
|
|
END{}
|
|
'
|
|
@@ -415,6 +433,15 @@ series_insert_patch()
|
|
mv "$series.tmp" "$series"
|
|
}
|
|
|
|
+series_append_patch()
|
|
+{
|
|
+ # unlike series_insert_patch, which inserts the passed
|
|
+ # patch after the current top patch, this function always
|
|
+ # appends the patch to the series
|
|
+
|
|
+ echo $1 >> "$series"
|
|
+}
|
|
+
|
|
# usage: series_remove_patch <patchname>
|
|
series_remove_patch()
|
|
{
|
|
@@ -473,8 +500,7 @@ remove_patch_refs()
|
|
# usage: pop_many_patches <commitish> <number of patches>
|
|
pop_many_patches()
|
|
{
|
|
- assert_head_check
|
|
-
|
|
+ head_check "`tail -1 < "$applied" | cut -d: -f 1`"
|
|
(
|
|
cd_to_toplevel
|
|
|
|
@@ -508,50 +534,149 @@ remove_ref()
|
|
)
|
|
}
|
|
|
|
+prep_patch()
|
|
+{
|
|
+ patch=$1;
|
|
+ tgt=$2;
|
|
+
|
|
+ if test -f $patch; then
|
|
+ case $patch in
|
|
+ *.gz) gzip -dc $patch > $tgt ;;
|
|
+ *.bz2) bzip2 -dc $patch > $tgt ;;
|
|
+ *) cp $patch $tgt ;;
|
|
+ esac;
|
|
+ fi;
|
|
+}
|
|
+
|
|
# usage: commit patchname parent
|
|
commit()
|
|
{
|
|
(
|
|
TMP_MSG=`get_tmp_file msg`
|
|
+ TMP_PATCH=`get_tmp_file patch`
|
|
+ TMP_PATCH_OUT=`get_tmp_file patch_out`
|
|
+ TMP_INFO=`get_tmp_file info`
|
|
|
|
p="$GUILT_DIR/$branch/$1"
|
|
pname="$1"
|
|
+ prep_patch "$p" "$TMP_PATCH"
|
|
+ p=$TMP_PATCH
|
|
+
|
|
cd_to_toplevel
|
|
|
|
git diff-files --name-only | (while read n; do git update-index "$n" ; done)
|
|
|
|
+ # borrowed from git-am
|
|
+ header_type=git
|
|
+ git mailinfo "$TMP_MSG" "$TMP_PATCH_OUT" \
|
|
+ <"$p" >"$TMP_INFO";
|
|
+
|
|
+ # skip pine's internal folder data
|
|
+ grep '^Author: Mail System Internal Data$' \
|
|
+ <"$TMP_INFO" >/dev/null
|
|
+
|
|
+ git stripspace < "$TMP_MSG" > "$TMP_MSG.clean"
|
|
+ mv "$TMP_MSG.clean" "$TMP_MSG"
|
|
+ git stripspace < "$TMP_INFO" > "$TMP_INFO.clean"
|
|
+ mv "$TMP_INFO.clean" "$TMP_INFO"
|
|
+
|
|
+ # If mailinfo couldn't get something , try another way
|
|
# grab a commit message out of the patch
|
|
- do_get_header "$p" > "$TMP_MSG"
|
|
+ if [ ! -s "$TMP_MSG" ] || [ ! -s "$TMP_INFO" ]; then
|
|
+ do_get_header "$p" > "$TMP_MSG"
|
|
+ header_type=guilt
|
|
+ fi
|
|
|
|
- # make a default commit message if patch doesn't contain one
|
|
- [ ! -s "$TMP_MSG" ] && echo "patch $pname" > "$TMP_MSG"
|
|
+ # last try: make a default commit message if patch doesn't contain one
|
|
+ [ ! -s "$TMP_MSG" ] && echo "auto_msg: patch $pname" > "$TMP_MSG"
|
|
|
|
- # extract a From line from the patch header, and set
|
|
- # GIT_AUTHOR_{NAME,EMAIL}
|
|
- author_str=`sed -n -e '/^From:/ { s/^From: //; p; q; }; /^(diff |---$|--- )/ q' "$p"`
|
|
- if [ ! -z "$author_str" ]; then
|
|
+
|
|
+ if [ "$header_type" = "guilt" ]; then
|
|
+
|
|
+ # extract a From line from the patch header, and set
|
|
+ # GIT_AUTHOR_{NAME,EMAIL}
|
|
+ author_str=`sed -n -e '/^From:/ { s/^From: //; p; q }; /^(diff |---)/ q' "$p"`
|
|
+ if [ ! -z "$author_str" ]; then
|
|
GIT_AUTHOR_NAME=`echo $author_str | sed -e 's/ *<.*$//'`
|
|
export GIT_AUTHOR_NAME="${GIT_AUTHOR_NAME:-" "}"
|
|
export GIT_AUTHOR_EMAIL="`echo $author_str | sed -e 's/[^<]*//'`"
|
|
- fi
|
|
+ fi
|
|
+
|
|
+
|
|
+ # check in the patch for a subject
|
|
+ SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$p")"
|
|
+ if [ -z "$SUBJECT" ]; then
|
|
+ # if we can't find a subject in the patch, then let's construct
|
|
+ # one from the header of the patch itself
|
|
+ SUBJECT=`cat "$TMP_MSG" | head -n 1`
|
|
+ if [ ${#SUBJECT} -gt 60 ]; then
|
|
+ SUBJECT=${SUBJECT: -60}
|
|
+ fi
|
|
+ fi
|
|
+
|
|
+ if [ -z "$SUBJECT" ]; then
|
|
+ # if we are *still* without a subject, then just use
|
|
+ # the patch name
|
|
+ SUBJECT=`echo $1 | sed 's%^patch *%%'`
|
|
+
|
|
+ if [ ${#SUBJECT} -gt 60 ]; then
|
|
+ SUBJECT=${SUBJECT: -60}
|
|
+ fi
|
|
+ fi
|
|
|
|
- # must strip nano-second part otherwise git gets very
|
|
- # confused, and makes up strange timestamps from the past
|
|
- # (chances are it decides to interpret it as a unix
|
|
- # timestamp).
|
|
- export GIT_AUTHOR_DATE="`stat -c %y "$p" | sed -e '
|
|
+ SUBJECT=`echo $SUBJECT | sed s'%^ *%%'`
|
|
+
|
|
+ if [ ! -z "$SUBJECT" ]; then
|
|
+ echo "$SUBJECT" >> $TMP_MSG.subject
|
|
+ echo "" >> $TMP_MSG.subject
|
|
+ cat "$TMP_MSG" >> $TMP_MSG.subject
|
|
+ mv "$TMP_MSG.subject" "$TMP_MSG"
|
|
+ fi
|
|
+
|
|
+ # must strip nano-second part otherwise git gets very
|
|
+ # confused, and makes up strange timestamps from the past
|
|
+ # (chances are it decides to interpret it as a unix
|
|
+ # timestamp).
|
|
+ export GIT_AUTHOR_DATE="`stat -c %y "$p" | sed -e '\
|
|
s/^\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\) \([0-9]\{2\}\):\([0-9]\{2\}\):\([0-9]\{2\}\)\.[0-9]* \(.*\)$/\1-\2-\3 \4:\5:\6 \7/'`"
|
|
- export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
|
+ export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
|
+ else
|
|
+ # using git headers, closely related to git-am
|
|
+
|
|
+ GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$TMP_INFO")"
|
|
+ GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$TMP_INFO")"
|
|
+ GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$TMP_INFO")"
|
|
+ if test -z "$GIT_AUTHOR_EMAIL"
|
|
+ then
|
|
+ echo "Warning: patch does not have a valid e-mail address."
|
|
+ GIT_AUTHOR_EMAIL=$GIT_AUTHOR_NAME
|
|
+ fi
|
|
+
|
|
+ SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$TMP_INFO")"
|
|
+ if [ ! -z "$SUBJECT" ]; then
|
|
+ echo "$SUBJECT" >> $TMP_MSG.subject
|
|
+ echo "" >> $TMP_MSG.subject
|
|
+ cat "$TMP_MSG" >> $TMP_MSG.subject
|
|
+ mv "$TMP_MSG.subject" "$TMP_MSG"
|
|
+ fi
|
|
+ export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
|
+ fi
|
|
|
|
# commit
|
|
treeish=`git write-tree`
|
|
commitish=`git commit-tree $treeish -p $2 < "$TMP_MSG"`
|
|
+ if [ ! $? -eq 0 ]; then
|
|
+ echo "ERROR. Could not commit tree"
|
|
+ git-reset --hard HEAD^
|
|
+ exit 1
|
|
+ fi
|
|
git update-ref HEAD $commitish
|
|
|
|
# mark patch as applied
|
|
git update-ref "refs/patches/$branch/$pname" HEAD
|
|
|
|
- rm -f "$TMP_MSG"
|
|
+ rm -f "$TMP_MSG" "$TMP_LOG" "$TMP_PATCH" "$TMP_INFO" "$TMP_PATCH_OUT"
|
|
+
|
|
)
|
|
}
|
|
|
|
@@ -568,7 +693,7 @@ push_patch()
|
|
bail_action="$2"
|
|
reject="--reject"
|
|
|
|
- assert_head_check
|
|
+ head_check "`tail -1 < "$applied" | cut -d: -f 1`"
|
|
cd_to_toplevel
|
|
|
|
# apply the patch if and only if there is something to apply
|
|
@@ -671,7 +796,7 @@ refresh_patch()
|
|
# incldiffstat
|
|
__refresh_patch()
|
|
{
|
|
- assert_head_check
|
|
+ head_check "`tail -1 < "$applied" | cut -d: -f 1`"
|
|
|
|
(
|
|
TMP_DIFF=`get_tmp_file diff`
|
|
@@ -711,6 +836,10 @@ __refresh_patch()
|
|
|
|
head -n "-$N" < "$applied" > "$applied.tmp"
|
|
mv "$applied.tmp" "$applied"
|
|
+
|
|
+ # update the shadow status.
|
|
+ ref=`cat $GIT_DIR/refs/tags/${branch}_top`
|
|
+ echo "$ref:$1" >> $applied_shadow
|
|
)
|
|
}
|
|
|
|
@@ -791,7 +920,12 @@ diffstat=`git config --bool guilt.diffst
|
|
# The following gets run every time this file is source'd
|
|
#
|
|
|
|
-GUILT_DIR="$GIT_DIR/patches"
|
|
+
|
|
+# GUILT_DIR="$GIT_DIR/patches"
|
|
+if [ -z "$GUILT_BASE" ]; then
|
|
+ GUILT_BASE=wrs
|
|
+fi
|
|
+GUILT_DIR="$GUILT_BASE/patches"
|
|
|
|
branch=`get_branch`
|
|
|
|
@@ -814,6 +948,7 @@ fi
|
|
# very useful files
|
|
series="$GUILT_DIR/$branch/series"
|
|
applied="$GUILT_DIR/$branch/status"
|
|
+applied_shadow="$GUILT_DIR/$branch/shadow_status"
|
|
guards_file="$GUILT_DIR/$branch/guards"
|
|
|
|
# determine a pager to use for anything interactive (fall back to more)
|