]> git.ipfire.org Git - thirdparty/git.git/blob - git-format-patch.sh
Use symbolic name SHORT_NAME_AMBIGUOUS as error return value
[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 -c|--c|--ch|--che|--chec|--check)
38 check=t ;;
39 -a|--a|--au|--aut|--auth|--autho|--author|\
40 -d|--d|--da|--dat|--date|\
41 -m|--m|--mb|--mbo|--mbox) # now noop
42 ;;
43 -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
44 --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
45 keep_subject=t ;;
46 -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered)
47 numbered=t ;;
48 -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
49 signoff=t ;;
50 --st|--std|--stdo|--stdou|--stdout)
51 stdout=t mbox=t date=t author=t ;;
52 -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
53 --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
54 --output-direc=*|--output-direct=*|--output-directo=*|\
55 --output-director=*|--output-directory=*)
56 outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;;
57 -o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\
58 --output-di|--output-dir|--output-dire|--output-direc|--output-direct|\
59 --output-directo|--output-director|--output-directory)
60 case "$#" in 1) usage ;; esac; shift
61 outdir="$1" ;;
62 -h|--h|--he|--hel|--help)
63 usage
64 ;;
65 -*' '* | -*"$LF"* | -*' '*)
66 # Ignore diff option that has whitespace for now.
67 ;;
68 -*) diff_opts="$diff_opts$1 " ;;
69 *) break ;;
70 esac
71 shift
72 done
73
74 case "$keep_subject$numbered" in
75 tt)
76 die '--keep-subject and --numbered are incompatible.' ;;
77 esac
78
79 tmp=.tmp-series$$
80 trap 'rm -f $tmp-*' 0 1 2 3 15
81
82 series=$tmp-series
83 commsg=$tmp-commsg
84 filelist=$tmp-files
85
86 # Backward compatible argument parsing hack.
87 #
88 # Historically, we supported:
89 # 1. "rev1" is equivalent to "rev1..HEAD"
90 # 2. "rev1..rev2"
91 # 3. "rev1" "rev2 is equivalent to "rev1..rev2"
92 #
93 # We want to take a sequence of "rev1..rev2" in general.
94 # Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
95 # familiar with that syntax.
96
97 case "$#,$1$2" in
98 1,?*..?*)
99 # single "rev1..rev2"
100 ;;
101 1,?*..)
102 # single "rev1.." should mean "rev1..HEAD"
103 set x "$1"HEAD
104 shift
105 ;;
106 1,*)
107 # single rev1
108 set x "$1..HEAD"
109 shift
110 ;;
111 2,?*..?*)
112 # not traditional "rev1" "rev2"
113 ;;
114 2,*)
115 set x "$1..$2"
116 shift
117 ;;
118 esac
119
120 # Now we have what we want in $@
121 for revpair
122 do
123 case "$revpair" in
124 ?*..?*)
125 rev1=`expr "$revpair" : '\(.*\)\.\.'`
126 rev2=`expr "$revpair" : '.*\.\.\(.*\)'`
127 ;;
128 *)
129 rev1="$revpair^"
130 rev2="$revpair"
131 ;;
132 esac
133 git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
134 die "Not a valid rev $rev1 ($revpair)"
135 git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
136 die "Not a valid rev $rev2 ($revpair)"
137 git-cherry -v "$rev1" "$rev2" |
138 while read sign rev comment
139 do
140 case "$sign" in
141 '-')
142 echo >&2 "Merged already: $comment"
143 ;;
144 *)
145 echo $rev
146 ;;
147 esac
148 done
149 done >$series
150
151 me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
152
153 case "$outdir" in
154 */) ;;
155 *) outdir="$outdir/" ;;
156 esac
157 test -d "$outdir" || mkdir -p "$outdir" || exit
158
159 titleScript='
160 /./d
161 /^$/n
162 s/^\[PATCH[^]]*\] *//
163 s/[^-a-z.A-Z_0-9]/-/g
164 s/\.\.\.*/\./g
165 s/\.*$//
166 s/--*/-/g
167 s/^-//
168 s/-$//
169 s/$/./
170 p
171 q
172 '
173
174 process_one () {
175 perl -w -e '
176 my ($keep_subject, $num, $signoff, $commsg) = @ARGV;
177 my ($signoff_pattern, $done_header, $done_subject, $signoff_seen,
178 $last_was_signoff);
179
180 if ($signoff) {
181 $signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`;
182 $signoff =~ s/>.*/>/;
183 $signoff_pattern = quotemeta($signoff);
184 }
185
186 my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat);
187 my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
188
189 sub show_date {
190 my ($time, $tz) = @_;
191 my $minutes = abs($tz);
192 $minutes = ($minutes / 100) * 60 + ($minutes % 100);
193 if ($tz < 0) {
194 $minutes = -$minutes;
195 }
196 my $t = $time + $minutes * 60;
197 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t);
198 return sprintf("%s %s %d %02d:%02d:%02d %d %+05d",
199 $weekday_names[$wday],
200 $month_names[$mon],
201 $mday, $hour, $min, $sec,
202 $year+1900, $tz);
203 }
204
205 print "From nobody Mon Sep 17 00:00:00 2001\n";
206 open FH, "git stripspace <$commsg |" or die "open $commsg pipe";
207 while (<FH>) {
208 unless ($done_header) {
209 if (/^$/) {
210 $done_header = 1;
211 }
212 elsif (/^author (.*>) (.*)$/) {
213 my ($author_ident, $author_date) = ($1, $2);
214 my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/);
215 $author_date = show_date($utc, $off);
216
217 print "From: $author_ident\n";
218 print "Date: $author_date\n";
219 }
220 next;
221 }
222 unless ($done_subject) {
223 unless ($keep_subject) {
224 s/^\[PATCH[^]]*\]\s*//;
225 s/^/[PATCH$num] /;
226 }
227 print "Subject: $_";
228 $done_subject = 1;
229 next;
230 }
231
232 $last_was_signoff = 0;
233 if (/Signed-off-by:/i) {
234 if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) {
235 $signoff_seen = 1;
236 }
237 }
238 print $_;
239 }
240 if (!$signoff_seen && $signoff ne "") {
241 if (!$last_was_signoff) {
242 print "\n";
243 }
244 print "$signoff\n";
245 }
246 print "\n---\n\n";
247 close FH or die "close $commsg pipe";
248 ' "$keep_subject" "$num" "$signoff" $commsg
249
250 git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
251 echo
252 git-diff-tree -p $diff_opts "$commit"
253 echo "-- "
254 echo "@@GIT_VERSION@@"
255
256 echo
257 }
258
259 total=`wc -l <$series | tr -dc "[0-9]"`
260 case "$total,$numbered" in
261 1,*)
262 numfmt='' ;;
263 *,t)
264 numfmt=`echo "$total" | wc -c`
265 numfmt=$(($numfmt-1))
266 numfmt=" %0${numfmt}d/$total"
267 esac
268
269 i=1
270 while read commit
271 do
272 git-cat-file commit "$commit" | git-stripspace >$commsg
273 title=`sed -ne "$titleScript" <$commsg`
274 case "$numbered" in
275 '') num= ;;
276 *)
277 num=`printf "$numfmt" $i` ;;
278 esac
279
280 file=`printf '%04d-%stxt' $i "$title"`
281 if test '' = "$stdout"
282 then
283 echo "$file"
284 process_one >"$outdir$file"
285 if test t = "$check"
286 then
287 # This is slightly modified from Andrew Morton's Perfect Patch.
288 # Lines you introduce should not have trailing whitespace.
289 # Also check for an indentation that has SP before a TAB.
290 grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
291 :
292 fi
293 else
294 echo >&2 "$file"
295 process_one
296 fi
297 i=`expr "$i" + 1`
298 done <$series