]> git.ipfire.org Git - thirdparty/git.git/blob - git-format-patch.sh
[PATCH] Omit patches that have already been merged from format-patch output.
[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] [--keep-subject] [--mbox] [--check] [--signoff] [-<diff options>...] upstream [ our-head ]
10
11 Prepare each commit with its patch since our-head forked from upstream,
12 one file per patch, for e-mail submission. Each output file is
13 numbered sequentially from 1, and uses the first line of the commit
14 message (massaged for pathname safety) as the filename.
15
16 When -o is specified, output files are created in that directory; otherwise in
17 the current working directory.
18
19 When -n is specified, instead of "[PATCH] Subject", the first line is formatted
20 as "[PATCH N/M] Subject", unless you have only one patch.
21
22 When --mbox is specified, the output is formatted to resemble
23 UNIX mailbox format, and can be concatenated together for processing
24 with applymbox.
25 '
26 exit 1
27 }
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 -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
53 --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
54 --output-direc=*|--output-direct=*|--output-directo=*|\
55 --output-director=*|--output-directory=*)
56 outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;;
57 -o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\
58 --output-di|--output-dir|--output-dire|--output-direc|--output-direct|\
59 --output-directo|--output-director|--output-directory)
60 case "$#" in 1) usage ;; esac; shift
61 outdir="$1" ;;
62 -*' '* | -*"$LF"* | -*' '*)
63 # Ignore diff option that has whitespace for now.
64 ;;
65 -*) diff_opts="$diff_opts$1 " ;;
66 *) break ;;
67 esac
68 shift
69 done
70
71 case "$keep_subject$numbered" in
72 tt)
73 die '--keep-subject and --numbered are incompatible.' ;;
74 esac
75
76 rev1= rev2=
77 case "$#" in
78 2)
79 rev1="$1" rev2="$2" ;;
80 1)
81 case "$1" in
82 *..*)
83 rev1=`expr "$1" : '\(.*\)\.\.'`
84 rev2=`expr "$1" : '.*\.\.\(.*\)'`
85 ;;
86 *)
87 rev1="$1"
88 rev2="HEAD"
89 ;;
90 esac ;;
91 *)
92 usage ;;
93 esac
94
95 me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
96
97 case "$outdir" in
98 */) ;;
99 *) outdir="$outdir/" ;;
100 esac
101 test -d "$outdir" || mkdir -p "$outdir" || exit
102
103 tmp=.tmp-series$$
104 trap 'rm -f $tmp-*' 0 1 2 3 15
105
106 series=$tmp-series
107 commsg=$tmp-commsg
108 filelist=$tmp-files
109
110 titleScript='
111 /./d
112 /^$/n
113 s/^\[PATCH[^]]*\] *//
114 s/[^-a-z.A-Z_0-9]/-/g
115 s/\.\.\.*/\./g
116 s/\.*$//
117 s/--*/-/g
118 s/^-//
119 s/-$//
120 s/$/./
121 p
122 q
123 '
124
125 whosepatchScript='
126 /^author /{
127 s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
128 q
129 }'
130
131 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
132 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
133 stripCommitHead='/^'"$_x40"' (from '"$_x40"')$/d'
134
135 git-cherry -v "$rev1" "$rev2" |
136 while read sign rev comment
137 do
138 case "$sign" in
139 '-')
140 echo >&2 "Merged already: $comment"
141 ;;
142 *)
143 echo $rev
144 ;;
145 esac
146 done >$series
147
148 total=`wc -l <$series | tr -dc "[0-9]"`
149 i=1
150 while read commit
151 do
152 git-cat-file commit "$commit" | git-stripspace >$commsg
153 title=`sed -ne "$titleScript" <$commsg`
154 case "$numbered" in
155 '') num= ;;
156 *)
157 case $total in
158 1) num= ;;
159 *) num=' '`printf "%d/%d" $i $total` ;;
160 esac
161 esac
162
163 file=`printf '%04d-%stxt' $i "$title"`
164 i=`expr "$i" + 1`
165 echo "* $file"
166 {
167 mailScript='
168 /./d
169 /^$/n'
170 case "$keep_subject" in
171 t) ;;
172 *)
173 mailScript="$mailScript"'
174 s|^\[PATCH[^]]*\] *||
175 s|^|[PATCH'"$num"'] |'
176 ;;
177 esac
178 mailScript="$mailScript"'
179 s|^|Subject: |'
180 case "$mbox" in
181 t)
182 echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
183 ;;
184 esac
185 eval "$(sed -ne "$whosepatchScript" $commsg)"
186 test "$author,$au" = ",$me" || {
187 mailScript="$mailScript"'
188 a\
189 From: '"$au"
190 }
191 test "$date,$au" = ",$me" || {
192 mailScript="$mailScript"'
193 a\
194 Date: '"$ad"
195 }
196
197 mailScript="$mailScript"'
198 : body
199 p
200 n
201 b body'
202
203 sed -ne "$mailScript" <$commsg
204
205 test "$signoff" = "t" && {
206 offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
207 line="Signed-off-by: $offsigner"
208 grep -q "^$line\$" $commsg || {
209 echo
210 echo "$line"
211 echo
212 }
213 }
214 echo
215 echo '---'
216 echo
217 git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
218 echo
219 git-diff-tree -p $diff_opts "$commit" | sed -e "$stripCommitHead"
220
221 case "$mbox" in
222 t)
223 echo
224 ;;
225 esac
226 } >"$outdir$file"
227 case "$check" in
228 t)
229 # This is slightly modified from Andrew Morton's Perfect Patch.
230 # Lines you introduce should not have trailing whitespace.
231 # Also check for an indentation that has SP before a TAB.
232 grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
233
234 : do not exit with non-zero because we saw no problem in the last one.
235 esac
236 done <$series