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