[PATCH] Do not send "want" lines for complete objects
[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