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