3c3413b76fc43572d0eb36e4461845bbada24061
[git.git] / git-format-patch-script
1 #!/bin/sh
2 #
3 # Copyright (c) 2005 Junio C Hamano
4 #
5
6 . git-sh-setup-script || 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 IFS='
31 '
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     -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     -*) diff_opts="$diff_opts$LF$1" ;;
65     *) break ;;
66     esac
67     shift
68 done
69
70 case "$keep_subject$numbered" in
71 tt)
72         die '--keep-subject and --numbered are incompatible.' ;;
73 esac
74
75 revpair=
76 case "$#" in
77 2)
78     revpair="$1..$2" ;;
79 1)
80     case "$1" in
81     *..*)
82         revpair="$1";;
83     *)
84         revpair="$1..HEAD";;
85     esac ;;
86 *)
87     usage ;;
88 esac
89
90 me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
91
92 case "$outdir" in
93 */) ;;
94 *) outdir="$outdir/" ;;
95 esac
96 test -d "$outdir" || mkdir -p "$outdir" || exit
97
98 tmp=.tmp-series$$
99 trap 'rm -f $tmp-*' 0 1 2 3 15
100
101 series=$tmp-series
102 commsg=$tmp-commsg
103 filelist=$tmp-files
104
105 titleScript='
106         /./d
107         /^$/n
108         s/^\[PATCH[^]]*\] *//
109         s/[^-a-z.A-Z_0-9]/-/g
110         s/\.\.\.*/\./g
111         s/\.*$//
112         s/--*/-/g
113         s/^-//
114         s/-$//
115         s/$/./
116         p
117         q
118 '
119
120 whosepatchScript='
121 /^author /{
122         s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
123         q
124 }'
125
126 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
127 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
128 stripCommitHead='/^'"$_x40"' (from '"$_x40"')$/d'
129
130 git-rev-list --no-merges --merge-order \
131         $(git-rev-parse --revs-only "$revpair") >$series
132 total=`wc -l <$series | tr -dc "[0-9]"`
133 i=$total
134 while read commit
135 do
136     git-cat-file commit "$commit" | git-stripspace >$commsg
137     title=`sed -ne "$titleScript" <$commsg`
138     case "$numbered" in
139     '') num= ;;
140     *)
141         case $total in
142         1) num= ;;
143         *) num=' '`printf "%d/%d" $i $total` ;;
144         esac
145     esac
146
147     file=`printf '%04d-%stxt' $i "$title"`
148     i=`expr "$i" - 1`
149     echo >&2 "* $file"
150     {
151         mailScript='
152         /./d
153         /^$/n'
154         case "$keep_subject" in
155         t)  ;;
156         *)
157             mailScript="$mailScript"'
158             s|^\[PATCH[^]]*\] *||
159             s|^|[PATCH'"$num"'] |'
160             ;;
161         esac
162         mailScript="$mailScript"'
163         s|^|Subject: |'
164         case "$mbox" in
165         t)
166             echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
167             ;;
168         esac
169         eval "$(sed -ne "$whosepatchScript" $commsg)"
170         test "$author,$au" = ",$me" || {
171                 mailScript="$mailScript"'
172         a\
173 From: '"$au"
174         }
175         test "$date,$au" = ",$me" || {
176                 mailScript="$mailScript"'
177         a\
178 Date: '"$ad"
179         }
180
181         mailScript="$mailScript"'
182         : body
183         p
184         n
185         b body'
186
187         sed -ne "$mailScript" <$commsg
188
189         test "$signoff" = "t" && {
190                 offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
191                 line="Signed-off-by: $offsigner"
192                 grep -q "^$line\$" $commsg || {
193                         echo
194                         echo "$line"
195                         echo
196                 }
197         }
198
199         echo '---'
200         echo
201         git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
202         echo
203         git-diff-tree -p $diff_opts "$commit" | sed -e "$stripCommitHead"
204
205         case "$mbox" in
206         t)
207                 echo
208                 ;;
209         esac
210     } >"$outdir$file"
211     case "$check" in
212     t)
213         # This is slightly modified from Andrew Morton's Perfect Patch.
214         # Lines you introduce should not have trailing whitespace.
215         # Also check for an indentation that has SP before a TAB.
216         grep -n '^+\([  ]*      .*\|.*[         ]\)$' "$outdir$file"
217
218         : do not exit with non-zero because we saw no problem in the last one.
219     esac
220 done <$series