]>
Commit | Line | Data |
---|---|---|
0acfc972 JH |
1 | #!/bin/sh |
2 | # | |
3 | # Copyright (c) 2005 Junio C Hamano | |
4 | # | |
5 | ||
19bb7327 | 6 | USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--diff-options] [--attach] <his> [<mine>]' |
66f04f38 AE |
7 | LONG_USAGE='Prepare each commit with its patch since <mine> head forked from |
8 | <his> head, one file per patch formatted to resemble UNIX mailbox | |
9 | format, for e-mail submission or use with git-am. | |
10 | ||
11 | Each output file is numbered sequentially from 1, and uses the | |
12 | first line of the commit message (massaged for pathname safety) | |
13 | as the filename. | |
14 | ||
15 | When -o is specified, output files are created in <dir>; otherwise | |
16 | they are created in the current working directory. This option | |
17 | is ignored if --stdout is specified. | |
18 | ||
19 | When -n is specified, instead of "[PATCH] Subject", the first | |
20 | line is formatted as "[PATCH N/M] Subject", unless you have only | |
19bb7327 MM |
21 | one patch. |
22 | ||
23 | When --attach is specified, patches are attached, not inlined.' | |
66f04f38 | 24 | |
806f36d4 FK |
25 | . git-sh-setup |
26 | ||
27 | # Force diff to run in C locale. | |
28 | LANG=C LC_ALL=C | |
29 | export LANG LC_ALL | |
0acfc972 JH |
30 | |
31 | diff_opts= | |
0acfc972 JH |
32 | LF=' |
33 | ' | |
0acfc972 | 34 | |
5c2c972f | 35 | outdir=./ |
0acfc972 JH |
36 | while case "$#" in 0) break;; esac |
37 | do | |
38 | case "$1" in | |
5c2c972f JH |
39 | -c|--c|--ch|--che|--chec|--check) |
40 | check=t ;; | |
36383a3d JH |
41 | -a|--a|--au|--aut|--auth|--autho|--author|\ |
42 | -d|--d|--da|--dat|--date|\ | |
43 | -m|--m|--mb|--mbo|--mbox) # now noop | |
44 | ;; | |
19bb7327 MM |
45 | --at|--att|--atta|--attac|--attach) |
46 | attach=t ;; | |
af5260ee JH |
47 | -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\ |
48 | --keep-subj|--keep-subje|--keep-subjec|--keep-subject) | |
49 | keep_subject=t ;; | |
0acfc972 JH |
50 | -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered) |
51 | numbered=t ;; | |
d9ac9df4 | 52 | -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) |
b097584b | 53 | signoff=t ;; |
655c7470 | 54 | --st|--std|--stdo|--stdou|--stdout) |
66f04f38 | 55 | stdout=t ;; |
0acfc972 JH |
56 | -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\ |
57 | --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\ | |
58 | --output-direc=*|--output-direct=*|--output-directo=*|\ | |
59 | --output-director=*|--output-directory=*) | |
60 | outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;; | |
61 | -o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\ | |
62 | --output-di|--output-dir|--output-dire|--output-direc|--output-direct|\ | |
63 | --output-directo|--output-director|--output-directory) | |
64 | case "$#" in 1) usage ;; esac; shift | |
65 | outdir="$1" ;; | |
93d69d86 JL |
66 | -h|--h|--he|--hel|--help) |
67 | usage | |
68 | ;; | |
4a5b63e3 JH |
69 | -*' '* | -*"$LF"* | -*' '*) |
70 | # Ignore diff option that has whitespace for now. | |
71 | ;; | |
72 | -*) diff_opts="$diff_opts$1 " ;; | |
0acfc972 JH |
73 | *) break ;; |
74 | esac | |
75 | shift | |
76 | done | |
77 | ||
af5260ee JH |
78 | case "$keep_subject$numbered" in |
79 | tt) | |
80 | die '--keep-subject and --numbered are incompatible.' ;; | |
81 | esac | |
82 | ||
603d8745 JH |
83 | tmp=.tmp-series$$ |
84 | trap 'rm -f $tmp-*' 0 1 2 3 15 | |
85 | ||
86 | series=$tmp-series | |
87 | commsg=$tmp-commsg | |
88 | filelist=$tmp-files | |
89 | ||
90 | # Backward compatible argument parsing hack. | |
91 | # | |
92 | # Historically, we supported: | |
93 | # 1. "rev1" is equivalent to "rev1..HEAD" | |
94 | # 2. "rev1..rev2" | |
95 | # 3. "rev1" "rev2 is equivalent to "rev1..rev2" | |
96 | # | |
97 | # We want to take a sequence of "rev1..rev2" in general. | |
bd7c8aab JH |
98 | # Also, "rev1.." should mean "rev1..HEAD"; git-diff users are |
99 | # familiar with that syntax. | |
603d8745 | 100 | |
88b5a748 | 101 | case "$#,$1$2" in |
603d8745 JH |
102 | 1,?*..?*) |
103 | # single "rev1..rev2" | |
104 | ;; | |
bd7c8aab JH |
105 | 1,?*..) |
106 | # single "rev1.." should mean "rev1..HEAD" | |
b748421a | 107 | set x "$1"HEAD |
bd7c8aab JH |
108 | shift |
109 | ;; | |
603d8745 JH |
110 | 1,*) |
111 | # single rev1 | |
112 | set x "$1..HEAD" | |
113 | shift | |
114 | ;; | |
115 | 2,?*..?*) | |
116 | # not traditional "rev1" "rev2" | |
4a5b63e3 | 117 | ;; |
603d8745 JH |
118 | 2,*) |
119 | set x "$1..$2" | |
120 | shift | |
4a5b63e3 | 121 | ;; |
0acfc972 JH |
122 | esac |
123 | ||
603d8745 JH |
124 | # Now we have what we want in $@ |
125 | for revpair | |
126 | do | |
127 | case "$revpair" in | |
128 | ?*..?*) | |
f327dbce MW |
129 | rev1=`expr "z$revpair" : 'z\(.*\)\.\.'` |
130 | rev2=`expr "z$revpair" : 'z.*\.\.\(.*\)'` | |
603d8745 JH |
131 | ;; |
132 | *) | |
88b5a748 JH |
133 | rev1="$revpair^" |
134 | rev2="$revpair" | |
603d8745 JH |
135 | ;; |
136 | esac | |
137 | git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 || | |
138 | die "Not a valid rev $rev1 ($revpair)" | |
139 | git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 || | |
140 | die "Not a valid rev $rev2 ($revpair)" | |
141 | git-cherry -v "$rev1" "$rev2" | | |
142 | while read sign rev comment | |
143 | do | |
144 | case "$sign" in | |
145 | '-') | |
146 | echo >&2 "Merged already: $comment" | |
147 | ;; | |
148 | *) | |
149 | echo $rev | |
150 | ;; | |
151 | esac | |
152 | done | |
153 | done >$series | |
154 | ||
44d2eb51 | 155 | me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'` |
00333ca3 | 156 | headers=`git-repo-config --get format.headers` |
19bb7327 MM |
157 | case "$attach" in |
158 | "") ;; | |
159 | *) | |
160 | mimemagic="050802040500080604070107" | |
161 | esac | |
44d2eb51 | 162 | |
0acfc972 JH |
163 | case "$outdir" in |
164 | */) ;; | |
165 | *) outdir="$outdir/" ;; | |
166 | esac | |
167 | test -d "$outdir" || mkdir -p "$outdir" || exit | |
168 | ||
0acfc972 | 169 | titleScript=' |
1855c044 JH |
170 | /./d |
171 | /^$/n | |
172 | s/^\[PATCH[^]]*\] *// | |
0acfc972 JH |
173 | s/[^-a-z.A-Z_0-9]/-/g |
174 | s/\.\.\.*/\./g | |
175 | s/\.*$// | |
176 | s/--*/-/g | |
177 | s/^-// | |
178 | s/-$// | |
179 | s/$/./ | |
1855c044 | 180 | p |
0acfc972 JH |
181 | q |
182 | ' | |
183 | ||
655c7470 | 184 | process_one () { |
36383a3d | 185 | perl -w -e ' |
19bb7327 | 186 | my ($keep_subject, $num, $signoff, $headers, $mimemagic, $commsg) = @ARGV; |
f891cb3f | 187 | my ($signoff_pattern, $done_header, $done_subject, $done_separator, $signoff_seen, |
36383a3d | 188 | $last_was_signoff); |
655c7470 | 189 | |
36383a3d | 190 | if ($signoff) { |
fc5be4fd | 191 | $signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`; |
36383a3d JH |
192 | $signoff =~ s/>.*/>/; |
193 | $signoff_pattern = quotemeta($signoff); | |
194 | } | |
44d2eb51 | 195 | |
36383a3d JH |
196 | my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat); |
197 | my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); | |
a5c21d6e | 198 | |
36383a3d JH |
199 | sub show_date { |
200 | my ($time, $tz) = @_; | |
201 | my $minutes = abs($tz); | |
fab5de79 | 202 | $minutes = int($minutes / 100) * 60 + ($minutes % 100); |
36383a3d JH |
203 | if ($tz < 0) { |
204 | $minutes = -$minutes; | |
205 | } | |
206 | my $t = $time + $minutes * 60; | |
207 | my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t); | |
262a6ef7 HD |
208 | return sprintf("%s, %d %s %d %02d:%02d:%02d %+05d", |
209 | $weekday_names[$wday], $mday, | |
210 | $month_names[$mon], $year+1900, | |
211 | $hour, $min, $sec, $tz); | |
36383a3d | 212 | } |
0acfc972 | 213 | |
36383a3d JH |
214 | print "From nobody Mon Sep 17 00:00:00 2001\n"; |
215 | open FH, "git stripspace <$commsg |" or die "open $commsg pipe"; | |
216 | while (<FH>) { | |
217 | unless ($done_header) { | |
218 | if (/^$/) { | |
219 | $done_header = 1; | |
220 | } | |
221 | elsif (/^author (.*>) (.*)$/) { | |
222 | my ($author_ident, $author_date) = ($1, $2); | |
223 | my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/); | |
224 | $author_date = show_date($utc, $off); | |
b097584b | 225 | |
36383a3d JH |
226 | print "From: $author_ident\n"; |
227 | print "Date: $author_date\n"; | |
b097584b | 228 | } |
36383a3d JH |
229 | next; |
230 | } | |
231 | unless ($done_subject) { | |
232 | unless ($keep_subject) { | |
233 | s/^\[PATCH[^]]*\]\s*//; | |
234 | s/^/[PATCH$num] /; | |
235 | } | |
00333ca3 MM |
236 | if ($headers) { |
237 | print "$headers\n"; | |
238 | } | |
36383a3d | 239 | print "Subject: $_"; |
19bb7327 MM |
240 | if ($mimemagic) { |
241 | print "MIME-Version: 1.0\n"; | |
242 | print "Content-Type: multipart/mixed;\n"; | |
243 | print " boundary=\"------------$mimemagic\"\n"; | |
244 | print "\n"; | |
245 | print "This is a multi-part message in MIME format.\n"; | |
246 | print "--------------$mimemagic\n"; | |
247 | print "Content-Type: text/plain; charset=UTF-8; format=fixed\n"; | |
248 | print "Content-Transfer-Encoding: 8bit\n"; | |
249 | } | |
36383a3d JH |
250 | $done_subject = 1; |
251 | next; | |
252 | } | |
f891cb3f AJ |
253 | unless ($done_separator) { |
254 | print "\n"; | |
255 | $done_separator = 1; | |
256 | next if (/^$/); | |
257 | } | |
36383a3d JH |
258 | |
259 | $last_was_signoff = 0; | |
260 | if (/Signed-off-by:/i) { | |
261 | if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) { | |
262 | $signoff_seen = 1; | |
263 | } | |
264 | } | |
265 | print $_; | |
266 | } | |
267 | if (!$signoff_seen && $signoff ne "") { | |
268 | if (!$last_was_signoff) { | |
269 | print "\n"; | |
270 | } | |
271 | print "$signoff\n"; | |
272 | } | |
273 | print "\n---\n\n"; | |
274 | close FH or die "close $commsg pipe"; | |
19bb7327 | 275 | ' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg |
36383a3d | 276 | |
9d76812b | 277 | git-diff-tree -p --stat --summary $diff_opts "$commit" |
0acfc972 | 278 | echo |
19bb7327 MM |
279 | case "$mimemagic" in |
280 | '');; | |
281 | *) | |
282 | echo "--------------$mimemagic" | |
283 | echo "Content-Type: text/x-patch;" | |
284 | echo " name=\"$commit.diff\"" | |
285 | echo "Content-Transfer-Encoding: 8bit" | |
286 | echo "Content-Disposition: inline;" | |
287 | echo " filename=\"$commit.diff\"" | |
288 | echo | |
289 | esac | |
b10c1a74 | 290 | git-diff-tree -p $diff_opts "$commit" |
19bb7327 MM |
291 | case "$mimemagic" in |
292 | '') | |
293 | echo "-- " | |
294 | echo "@@GIT_VERSION@@" | |
295 | ;; | |
296 | *) | |
297 | echo | |
298 | echo "--------------$mimemagic--" | |
299 | echo | |
300 | ;; | |
301 | esac | |
36383a3d | 302 | echo |
655c7470 JH |
303 | } |
304 | ||
305 | total=`wc -l <$series | tr -dc "[0-9]"` | |
7564577a JH |
306 | case "$total,$numbered" in |
307 | 1,*) | |
308 | numfmt='' ;; | |
309 | *,t) | |
310 | numfmt=`echo "$total" | wc -c` | |
311 | numfmt=$(($numfmt-1)) | |
312 | numfmt=" %0${numfmt}d/$total" | |
313 | esac | |
314 | ||
655c7470 JH |
315 | i=1 |
316 | while read commit | |
317 | do | |
318 | git-cat-file commit "$commit" | git-stripspace >$commsg | |
319 | title=`sed -ne "$titleScript" <$commsg` | |
320 | case "$numbered" in | |
321 | '') num= ;; | |
322 | *) | |
7564577a | 323 | num=`printf "$numfmt" $i` ;; |
5c2c972f | 324 | esac |
655c7470 JH |
325 | |
326 | file=`printf '%04d-%stxt' $i "$title"` | |
327 | if test '' = "$stdout" | |
328 | then | |
51b3c00e | 329 | echo "$file" |
655c7470 JH |
330 | process_one >"$outdir$file" |
331 | if test t = "$check" | |
332 | then | |
333 | # This is slightly modified from Andrew Morton's Perfect Patch. | |
334 | # Lines you introduce should not have trailing whitespace. | |
335 | # Also check for an indentation that has SP before a TAB. | |
336 | grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file" | |
337 | : | |
338 | fi | |
339 | else | |
51b3c00e | 340 | echo >&2 "$file" |
655c7470 JH |
341 | process_one |
342 | fi | |
343 | i=`expr "$i" + 1` | |
0acfc972 | 344 | done <$series |