]> git.ipfire.org Git - thirdparty/git.git/blob - git-format-patch.sh
GIT 1.1.1
[thirdparty/git.git] / git-format-patch.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2005 Junio C Hamano
4 #
5
6 USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--mbox] [--diff-options] <upstream> [<our-head>]'
7 LONG_USAGE='Prepare each commit with its patch since our-head forked from upstream,
8 one file per patch, for e-mail submission. Each output file is
9 numbered sequentially from 1, and uses the first line of the commit
10 message (massaged for pathname safety) as the filename.
11
12 There are three output modes. By default, output files are created in
13 the current working directory; when -o is specified, they are created
14 in that directory instead; when --stdout is specified, they are spit
15 on standard output, and can be piped to git-am.
16
17 When -n is specified, instead of "[PATCH] Subject", the first line is formatted
18 as "[PATCH N/M] Subject", unless you have only one patch.
19
20 When --mbox is specified, the output is formatted to resemble
21 UNIX mailbox format, and can be concatenated together for processing
22 with applymbox.'
23 . git-sh-setup
24
25 # Force diff to run in C locale.
26 LANG=C LC_ALL=C
27 export LANG LC_ALL
28
29 diff_opts=
30 LF='
31 '
32
33 outdir=./
34 while case "$#" in 0) break;; esac
35 do
36 case "$1" in
37 -a|--a|--au|--aut|--auth|--autho|--author)
38 author=t ;;
39 -c|--c|--ch|--che|--chec|--check)
40 check=t ;;
41 -d|--d|--da|--dat|--date)
42 date=t ;;
43 -m|--m|--mb|--mbo|--mbox)
44 date=t author=t mbox=t ;;
45 -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
46 --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
47 keep_subject=t ;;
48 -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered)
49 numbered=t ;;
50 -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
51 signoff=t ;;
52 --st|--std|--stdo|--stdou|--stdout)
53 stdout=t mbox=t date=t author=t ;;
54 -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
55 --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
56 --output-direc=*|--output-direct=*|--output-directo=*|\
57 --output-director=*|--output-directory=*)
58 outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;;
59 -o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\
60 --output-di|--output-dir|--output-dire|--output-direc|--output-direct|\
61 --output-directo|--output-director|--output-directory)
62 case "$#" in 1) usage ;; esac; shift
63 outdir="$1" ;;
64 -h|--h|--he|--hel|--help)
65 usage
66 ;;
67 -*' '* | -*"$LF"* | -*' '*)
68 # Ignore diff option that has whitespace for now.
69 ;;
70 -*) diff_opts="$diff_opts$1 " ;;
71 *) break ;;
72 esac
73 shift
74 done
75
76 case "$keep_subject$numbered" in
77 tt)
78 die '--keep-subject and --numbered are incompatible.' ;;
79 esac
80
81 tmp=.tmp-series$$
82 trap 'rm -f $tmp-*' 0 1 2 3 15
83
84 series=$tmp-series
85 commsg=$tmp-commsg
86 filelist=$tmp-files
87
88 # Backward compatible argument parsing hack.
89 #
90 # Historically, we supported:
91 # 1. "rev1" is equivalent to "rev1..HEAD"
92 # 2. "rev1..rev2"
93 # 3. "rev1" "rev2 is equivalent to "rev1..rev2"
94 #
95 # We want to take a sequence of "rev1..rev2" in general.
96 # Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
97 # familiar with that syntax.
98
99 case "$#,$1$2" in
100 1,?*..?*)
101 # single "rev1..rev2"
102 ;;
103 1,?*..)
104 # single "rev1.." should mean "rev1..HEAD"
105 set x "$1"HEAD
106 shift
107 ;;
108 1,*)
109 # single rev1
110 set x "$1..HEAD"
111 shift
112 ;;
113 2,?*..?*)
114 # not traditional "rev1" "rev2"
115 ;;
116 2,*)
117 set x "$1..$2"
118 shift
119 ;;
120 esac
121
122 # Now we have what we want in $@
123 for revpair
124 do
125 case "$revpair" in
126 ?*..?*)
127 rev1=`expr "$revpair" : '\(.*\)\.\.'`
128 rev2=`expr "$revpair" : '.*\.\.\(.*\)'`
129 ;;
130 *)
131 rev1="$revpair^"
132 rev2="$revpair"
133 ;;
134 esac
135 git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
136 die "Not a valid rev $rev1 ($revpair)"
137 git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
138 die "Not a valid rev $rev2 ($revpair)"
139 git-cherry -v "$rev1" "$rev2" |
140 while read sign rev comment
141 do
142 case "$sign" in
143 '-')
144 echo >&2 "Merged already: $comment"
145 ;;
146 *)
147 echo $rev
148 ;;
149 esac
150 done
151 done >$series
152
153 me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
154
155 case "$outdir" in
156 */) ;;
157 *) outdir="$outdir/" ;;
158 esac
159 test -d "$outdir" || mkdir -p "$outdir" || exit
160
161 titleScript='
162 /./d
163 /^$/n
164 s/^\[PATCH[^]]*\] *//
165 s/[^-a-z.A-Z_0-9]/-/g
166 s/\.\.\.*/\./g
167 s/\.*$//
168 s/--*/-/g
169 s/^-//
170 s/-$//
171 s/$/./
172 p
173 q
174 '
175
176 whosepatchScript='
177 /^author /{
178 s/'\''/'\''\\'\'\''/g
179 s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
180 q
181 }'
182
183 process_one () {
184 mailScript='
185 /./d
186 /^$/n'
187 case "$keep_subject" in
188 t) ;;
189 *)
190 mailScript="$mailScript"'
191 s|^\[PATCH[^]]*\] *||
192 s|^|[PATCH'"$num"'] |'
193 ;;
194 esac
195 mailScript="$mailScript"'
196 s|^|Subject: |'
197 case "$mbox" in
198 t)
199 echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
200 ;;
201 esac
202
203 eval "$(sed -ne "$whosepatchScript" $commsg)"
204 test "$author,$au" = ",$me" || {
205 mailScript="$mailScript"'
206 a\
207 From: '"$au"
208 }
209 test "$date,$au" = ",$me" || {
210 mailScript="$mailScript"'
211 a\
212 Date: '"$ad"
213 }
214
215 mailScript="$mailScript"'
216 a\
217
218 : body
219 p
220 n
221 b body'
222
223 (cat $commsg ; echo; echo) |
224 sed -ne "$mailScript" |
225 git-stripspace
226
227 test "$signoff" = "t" && {
228 offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
229 line="Signed-off-by: $offsigner"
230 grep -q "^$line\$" $commsg || {
231 echo
232 echo "$line"
233 echo
234 }
235 }
236 echo
237 echo '---'
238 echo
239 git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
240 echo
241 git-diff-tree -p $diff_opts "$commit"
242 echo "-- "
243 echo "@@GIT_VERSION@@"
244
245 case "$mbox" in
246 t)
247 echo
248 ;;
249 esac
250 }
251
252 total=`wc -l <$series | tr -dc "[0-9]"`
253 case "$total,$numbered" in
254 1,*)
255 numfmt='' ;;
256 *,t)
257 numfmt=`echo "$total" | wc -c`
258 numfmt=$(($numfmt-1))
259 numfmt=" %0${numfmt}d/$total"
260 esac
261
262 i=1
263 while read commit
264 do
265 git-cat-file commit "$commit" | git-stripspace >$commsg
266 title=`sed -ne "$titleScript" <$commsg`
267 case "$numbered" in
268 '') num= ;;
269 *)
270 num=`printf "$numfmt" $i` ;;
271 esac
272
273 file=`printf '%04d-%stxt' $i "$title"`
274 if test '' = "$stdout"
275 then
276 echo "$file"
277 process_one >"$outdir$file"
278 if test t = "$check"
279 then
280 # This is slightly modified from Andrew Morton's Perfect Patch.
281 # Lines you introduce should not have trailing whitespace.
282 # Also check for an indentation that has SP before a TAB.
283 grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
284 :
285 fi
286 else
287 echo >&2 "$file"
288 process_one
289 fi
290 i=`expr "$i" + 1`
291 done <$series