603d874587
This enhances set of revs you can give format-patch. Originally, format-patch took either one rev, or two revs: format-patch rev1 format-patch rev1 rev2 The first format was a short-hand for "format-patch rev1 HEAD" (i.e. rev2==HEAD). What this meant was to find commits that are in branch rev2 that has not been merged to branch rev1. The above notation is still supported, but now it takes sequence of "from1..to1 from2..to2 ...". In short, the second format has become a short-hand for "format-patch rev1..rev2". Commits in to1 but not in from1, to2 but not in from2, ... are formatted as emailable patches. With this, cherry-picking from other branch can be written as: git-format-patch -k --stdout master..branch1 master..branch2 | git-am -k -3 which is generally faster than traditional cherry-pick (which always did 3-way merge) if patches apply cleanly, and still falls back on 3-way merge if some of them do not. Signed-off-by: Junio C Hamano <junkio@cox.net>
275 lines
5.8 KiB
Bash
Executable File
275 lines
5.8 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2005 Junio C Hamano
|
|
#
|
|
|
|
. git-sh-setup || die "Not a git archive."
|
|
|
|
usage () {
|
|
echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
|
|
[--check] [--signoff] [-<diff options>...]
|
|
( from..to ... | upstream [ our-head ] )
|
|
|
|
Prepare each commit with its patch since our-head forked from upstream,
|
|
one file per patch, for e-mail submission. Each output file is
|
|
numbered sequentially from 1, and uses the first line of the commit
|
|
message (massaged for pathname safety) as the filename.
|
|
|
|
When -o is specified, output files are created in that directory; otherwise in
|
|
the current working directory.
|
|
|
|
When -n is specified, instead of "[PATCH] Subject", the first line is formatted
|
|
as "[PATCH N/M] Subject", unless you have only one patch.
|
|
|
|
When --mbox is specified, the output is formatted to resemble
|
|
UNIX mailbox format, and can be concatenated together for processing
|
|
with applymbox.
|
|
'
|
|
exit 1
|
|
}
|
|
|
|
diff_opts=
|
|
LF='
|
|
'
|
|
|
|
outdir=./
|
|
while case "$#" in 0) break;; esac
|
|
do
|
|
case "$1" in
|
|
-a|--a|--au|--aut|--auth|--autho|--author)
|
|
author=t ;;
|
|
-c|--c|--ch|--che|--chec|--check)
|
|
check=t ;;
|
|
-d|--d|--da|--dat|--date)
|
|
date=t ;;
|
|
-m|--m|--mb|--mbo|--mbox)
|
|
date=t author=t mbox=t ;;
|
|
-k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
|
|
--keep-subj|--keep-subje|--keep-subjec|--keep-subject)
|
|
keep_subject=t ;;
|
|
-n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered)
|
|
numbered=t ;;
|
|
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
|
|
signoff=t ;;
|
|
--st|--std|--stdo|--stdou|--stdout)
|
|
stdout=t mbox=t date=t author=t ;;
|
|
-o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
|
|
--output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
|
|
--output-direc=*|--output-direct=*|--output-directo=*|\
|
|
--output-director=*|--output-directory=*)
|
|
outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;;
|
|
-o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\
|
|
--output-di|--output-dir|--output-dire|--output-direc|--output-direct|\
|
|
--output-directo|--output-director|--output-directory)
|
|
case "$#" in 1) usage ;; esac; shift
|
|
outdir="$1" ;;
|
|
-*' '* | -*"$LF"* | -*' '*)
|
|
# Ignore diff option that has whitespace for now.
|
|
;;
|
|
-*) diff_opts="$diff_opts$1 " ;;
|
|
*) break ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
case "$keep_subject$numbered" in
|
|
tt)
|
|
die '--keep-subject and --numbered are incompatible.' ;;
|
|
esac
|
|
|
|
tmp=.tmp-series$$
|
|
trap 'rm -f $tmp-*' 0 1 2 3 15
|
|
|
|
series=$tmp-series
|
|
commsg=$tmp-commsg
|
|
filelist=$tmp-files
|
|
|
|
# Backward compatible argument parsing hack.
|
|
#
|
|
# Historically, we supported:
|
|
# 1. "rev1" is equivalent to "rev1..HEAD"
|
|
# 2. "rev1..rev2"
|
|
# 3. "rev1" "rev2 is equivalent to "rev1..rev2"
|
|
#
|
|
# We want to take a sequence of "rev1..rev2" in general.
|
|
|
|
case "$#,$1" in
|
|
1,?*..?*)
|
|
# single "rev1..rev2"
|
|
;;
|
|
1,*)
|
|
# single rev1
|
|
set x "$1..HEAD"
|
|
shift
|
|
;;
|
|
2,?*..?*)
|
|
# not traditional "rev1" "rev2"
|
|
;;
|
|
2,*)
|
|
set x "$1..$2"
|
|
shift
|
|
;;
|
|
esac
|
|
|
|
# Now we have what we want in $@
|
|
for revpair
|
|
do
|
|
case "$revpair" in
|
|
?*..?*)
|
|
rev1=`expr "$revpair" : '\(.*\)\.\.'`
|
|
rev2=`expr "$revpair" : '.*\.\.\(.*\)'`
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|
|
git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
|
|
die "Not a valid rev $rev1 ($revpair)"
|
|
git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
|
|
die "Not a valid rev $rev2 ($revpair)"
|
|
git-cherry -v "$rev1" "$rev2" |
|
|
while read sign rev comment
|
|
do
|
|
case "$sign" in
|
|
'-')
|
|
echo >&2 "Merged already: $comment"
|
|
;;
|
|
*)
|
|
echo $rev
|
|
;;
|
|
esac
|
|
done
|
|
done >$series
|
|
|
|
me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
|
|
|
|
case "$outdir" in
|
|
*/) ;;
|
|
*) outdir="$outdir/" ;;
|
|
esac
|
|
test -d "$outdir" || mkdir -p "$outdir" || exit
|
|
|
|
titleScript='
|
|
/./d
|
|
/^$/n
|
|
s/^\[PATCH[^]]*\] *//
|
|
s/[^-a-z.A-Z_0-9]/-/g
|
|
s/\.\.\.*/\./g
|
|
s/\.*$//
|
|
s/--*/-/g
|
|
s/^-//
|
|
s/-$//
|
|
s/$/./
|
|
p
|
|
q
|
|
'
|
|
|
|
whosepatchScript='
|
|
/^author /{
|
|
s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
|
|
q
|
|
}'
|
|
|
|
process_one () {
|
|
mailScript='
|
|
/./d
|
|
/^$/n'
|
|
case "$keep_subject" in
|
|
t) ;;
|
|
*)
|
|
mailScript="$mailScript"'
|
|
s|^\[PATCH[^]]*\] *||
|
|
s|^|[PATCH'"$num"'] |'
|
|
;;
|
|
esac
|
|
mailScript="$mailScript"'
|
|
s|^|Subject: |'
|
|
case "$mbox" in
|
|
t)
|
|
echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
|
|
;;
|
|
esac
|
|
|
|
eval "$(sed -ne "$whosepatchScript" $commsg)"
|
|
test "$author,$au" = ",$me" || {
|
|
mailScript="$mailScript"'
|
|
a\
|
|
From: '"$au"
|
|
}
|
|
test "$date,$au" = ",$me" || {
|
|
mailScript="$mailScript"'
|
|
a\
|
|
Date: '"$ad"
|
|
}
|
|
|
|
mailScript="$mailScript"'
|
|
: body
|
|
p
|
|
n
|
|
b body'
|
|
|
|
(cat $commsg ; echo; echo) |
|
|
sed -ne "$mailScript" |
|
|
git-stripspace
|
|
|
|
test "$signoff" = "t" && {
|
|
offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
|
|
line="Signed-off-by: $offsigner"
|
|
grep -q "^$line\$" $commsg || {
|
|
echo
|
|
echo "$line"
|
|
echo
|
|
}
|
|
}
|
|
echo
|
|
echo '---'
|
|
echo
|
|
git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
|
|
echo
|
|
git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
|
|
git-diff-tree -p $diff_opts "$commit"
|
|
echo "---"
|
|
echo "@@GIT_VERSION@@"
|
|
|
|
case "$mbox" in
|
|
t)
|
|
echo
|
|
;;
|
|
esac
|
|
}
|
|
|
|
total=`wc -l <$series | tr -dc "[0-9]"`
|
|
i=1
|
|
while read commit
|
|
do
|
|
git-cat-file commit "$commit" | git-stripspace >$commsg
|
|
title=`sed -ne "$titleScript" <$commsg`
|
|
case "$numbered" in
|
|
'') num= ;;
|
|
*)
|
|
case $total in
|
|
1) num= ;;
|
|
*) num=' '`printf "%d/%d" $i $total` ;;
|
|
esac
|
|
esac
|
|
|
|
file=`printf '%04d-%stxt' $i "$title"`
|
|
if test '' = "$stdout"
|
|
then
|
|
echo "* $file"
|
|
process_one >"$outdir$file"
|
|
if test t = "$check"
|
|
then
|
|
# This is slightly modified from Andrew Morton's Perfect Patch.
|
|
# Lines you introduce should not have trailing whitespace.
|
|
# Also check for an indentation that has SP before a TAB.
|
|
grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
|
|
:
|
|
fi
|
|
else
|
|
echo >&2 "* $file"
|
|
process_one
|
|
fi
|
|
i=`expr "$i" + 1`
|
|
done <$series
|