GIT 1.1.3
[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