git-format-patch.sh 6.0 KB
Newer Older
1 2 3 4 5
#!/bin/sh
#
# Copyright (c) 2005 Junio C Hamano
#

J
Junio C Hamano 已提交
6
. git-sh-setup || die "Not a git archive."
7

8
usage () {
9 10
    echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
    [--check] [--signoff] [-<diff options>...]
11
    [--help]
12
    ( from..to ... | upstream [ our-head ] )
13 14 15 16 17 18 19 20 21 22 23

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.
24 25 26 27

When --mbox is specified, the output is formatted to resemble
UNIX mailbox format, and can be concatenated together for processing
with applymbox.
28 29 30 31 32 33 34 35
'
    exit 1
}

diff_opts=
LF='
'

36
outdir=./
37 38 39
while case "$#" in 0) break;; esac
do
    case "$1" in
40 41 42 43
    -a|--a|--au|--aut|--auth|--autho|--author)
    author=t ;;
    -c|--c|--ch|--che|--chec|--check)
    check=t ;;
44 45
    -d|--d|--da|--dat|--date)
    date=t ;;
46 47
    -m|--m|--mb|--mbo|--mbox)
    date=t author=t mbox=t ;;
J
Junio C Hamano 已提交
48 49 50
    -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
    --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
    keep_subject=t ;;
51 52
    -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered)
    numbered=t ;;
53
    -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
54
    signoff=t ;;
55 56
    --st|--std|--stdo|--stdou|--stdout)
    stdout=t mbox=t date=t author=t ;;
57 58 59 60 61 62 63 64 65 66
    -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" ;;
67 68 69
    -h|--h|--he|--hel|--help)
        usage
	;;
70 71 72 73
    -*' '* | -*"$LF"* | -*'	'*)
	# Ignore diff option that has whitespace for now.
	;;
    -*)	diff_opts="$diff_opts$1 " ;;
74 75 76 77 78
    *) break ;;
    esac
    shift
done

J
Junio C Hamano 已提交
79 80 81 82 83
case "$keep_subject$numbered" in
tt)
	die '--keep-subject and --numbered are incompatible.' ;;
esac

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
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.
99 100
# Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
# familiar with that syntax.
101 102 103 104 105

case "$#,$1" in
1,?*..?*)
	# single "rev1..rev2"
	;;
106 107
1,?*..)
	# single "rev1.." should mean "rev1..HEAD"
J
Junio C Hamano 已提交
108
	set x "$1"HEAD
109 110
	shift
	;;
111 112 113 114 115 116 117
1,*)
	# single rev1
	set x "$1..HEAD"
	shift
	;;
2,?*..?*)
	# not traditional "rev1" "rev2"
118
	;;
119 120 121
2,*)
	set x "$1..$2"
	shift
122
	;;
123 124
esac

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
# 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

155 156
me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`

157 158 159 160 161 162 163
case "$outdir" in
*/) ;;
*) outdir="$outdir/" ;;
esac
test -d "$outdir" || mkdir -p "$outdir" || exit

titleScript='
164 165 166
	/./d
	/^$/n
	s/^\[PATCH[^]]*\] *//
167 168 169 170 171 172 173
	s/[^-a-z.A-Z_0-9]/-/g
        s/\.\.\.*/\./g
	s/\.*$//
	s/--*/-/g
	s/^-//
	s/-$//
	s/$/./
174
	p
175 176 177
	q
'

178 179 180 181 182 183
whosepatchScript='
/^author /{
	s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
	q
}'

184
process_one () {
185
	mailScript='
186
	/./d
J
Junio C Hamano 已提交
187 188 189
	/^$/n'
	case "$keep_subject" in
	t)  ;;
190 191
	*)
	    mailScript="$mailScript"'
J
Junio C Hamano 已提交
192
	    s|^\[PATCH[^]]*\] *||
193 194 195
	    s|^|[PATCH'"$num"'] |'
	    ;;
	esac
J
Junio C Hamano 已提交
196 197 198 199 200 201 202
	mailScript="$mailScript"'
	s|^|Subject: |'
	case "$mbox" in
	t)
	    echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
	    ;;
	esac
203

204
	eval "$(sed -ne "$whosepatchScript" $commsg)"
205
	test "$author,$au" = ",$me" || {
206 207 208 209 210 211 212 213 214 215 216
		mailScript="$mailScript"'
	a\
From: '"$au"
	}
	test "$date,$au" = ",$me" || {
		mailScript="$mailScript"'
	a\
Date: '"$ad"
	}

	mailScript="$mailScript"'
217 218 219 220 221
	: body
	p
	n
	b body'

222 223 224
	(cat $commsg ; echo; echo) |
	sed -ne "$mailScript" |
	git-stripspace
225 226 227

	test "$signoff" = "t" && {
		offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
228 229 230 231 232 233
		line="Signed-off-by: $offsigner"
		grep -q "^$line\$" $commsg || {
			echo
			echo "$line"
			echo
		}
234
	}
235
	echo
236 237 238 239
	echo '---'
	echo
	git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
	echo
240
	git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
241
	git-diff-tree -p $diff_opts "$commit"
242 243
	echo "---"
	echo "@@GIT_VERSION@@"
244 245 246 247 248 249

	case "$mbox" in
	t)
		echo
		;;
	esac
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
}

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
265
    esac
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284

    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`
285
done <$series