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