Add functions git_config_set() and git_config_set_multivar()
[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     [--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" 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                 usage
135                 ;;
136         esac
137         git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
138                 die "Not a valid rev $rev1 ($revpair)"
139         git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
140                 die "Not a valid rev $rev2 ($revpair)"
141         git-cherry -v "$rev1" "$rev2" |
142         while read sign rev comment
143         do
144                 case "$sign" in
145                 '-')
146                         echo >&2 "Merged already: $comment"
147                         ;;
148                 *)
149                         echo $rev
150                         ;;
151                 esac
152         done
153 done >$series
154
155 me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
156
157 case "$outdir" in
158 */) ;;
159 *) outdir="$outdir/" ;;
160 esac
161 test -d "$outdir" || mkdir -p "$outdir" || exit
162
163 titleScript='
164         /./d
165         /^$/n
166         s/^\[PATCH[^]]*\] *//
167         s/[^-a-z.A-Z_0-9]/-/g
168         s/\.\.\.*/\./g
169         s/\.*$//
170         s/--*/-/g
171         s/^-//
172         s/-$//
173         s/$/./
174         p
175         q
176 '
177
178 whosepatchScript='
179 /^author /{
180         s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
181         q
182 }'
183
184 process_one () {
185         mailScript='
186         /./d
187         /^$/n'
188         case "$keep_subject" in
189         t)  ;;
190         *)
191             mailScript="$mailScript"'
192             s|^\[PATCH[^]]*\] *||
193             s|^|[PATCH'"$num"'] |'
194             ;;
195         esac
196         mailScript="$mailScript"'
197         s|^|Subject: |'
198         case "$mbox" in
199         t)
200             echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
201             ;;
202         esac
203
204         eval "$(LANG=C LC_ALL=C sed -ne "$whosepatchScript" $commsg)"
205         test "$author,$au" = ",$me" || {
206                 mailScript="$mailScript"'
207         a\
208 From: '"$au"
209         }
210         test "$date,$au" = ",$me" || {
211                 mailScript="$mailScript"'
212         a\
213 Date: '"$ad"
214         }
215
216         mailScript="$mailScript"'
217         : body
218         p
219         n
220         b body'
221
222         (cat $commsg ; echo; echo) |
223         sed -ne "$mailScript" |
224         git-stripspace
225
226         test "$signoff" = "t" && {
227                 offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
228                 line="Signed-off-by: $offsigner"
229                 grep -q "^$line\$" $commsg || {
230                         echo
231                         echo "$line"
232                         echo
233                 }
234         }
235         echo
236         echo '---'
237         echo
238         git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
239         echo
240         git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
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