3 # Create a patch from a commit or a series of commits
4 # Copyright (c) Petr Baudis, 2005
6 # Generate a patch with diff statistics and meta info about each commit.
8 # Note that if you want to use this as the output interface for your
9 # GIT tree containing changes against upstream GIT tree, please consider
10 # using the StGIT tool ("quilt for GIT"), which will enable you to
11 # update your patches seamlessly, rebase them against the latest upstream
12 # version, directly send them over mail, etc.
16 # -d DIRNAME:: Create patches in the DIRNAME directory
17 # Split the patches to separate files with their names in the
18 # format "%02d.patch", created in directory DIRNAME (will be
19 # created if non-existent). Note that this makes sense only
20 # when generating patch series, that is when you use the -r
23 # -f FORMAT:: Specify patch file name format
24 # Format string used for generating the patch filename when
25 # outputting the split-out patches (that is, passed the -d
26 # option). This is by default "%s/%02d-%s.patch". The first %s
27 # represents the directory name and %d represents the patch
28 # sequence number. The last %s is mangled first line of the
29 # commit message - kind of patch title.
31 # -m:: Base the diff at the merge base
32 # Base the patches at the merge base of the -r arguments
33 # (defaulting to HEAD and 'origin' or the current branch's default
34 # remote branch, see `cg-fetch` for details).
36 # -r FROM_ID[..TO_ID]:: Limit to revision range
37 # Specify a set of commits to make patches from using either
38 # '-r FROM_ID[..TO_ID]' or '-r FROM_ID -r TO_ID'. In both cases the
39 # option expects IDs which resolve to commits and will include the
40 # specified IDs. If 'TO_ID' is omitted patches for all commits
41 # from 'FROM_ID' to the initial commit will be generated. If the
42 # `-r` option is not given the commit ID defaults to 'HEAD'.
44 # -s:: Omit patch header
45 # Specify whether to print a short version of the patch without
46 # a patch header with meta info such as author and committer.
50 # To make patches for all commits between two releases tagged as
51 # 'releasetag-0.9' and 'releasetag-0.10' do:
53 # $ cg-mkpatch -r releasetag-0.9..releasetag-0.10
55 # The output will be a continuous dump of patches each separated by
58 # !-------------------------------------------------------------flip-
62 # The ':' is equivalent to '..' in revisions range specification (to make
63 # things more comfortable to SVN users). See cogito(7) for more details
64 # about revision specification.
68 USAGE
="cg-mkpatch [-m] [-s] [-r FROM_ID[..TO_ID] [-d DIRNAME]]"
72 .
"${COGITO_LIB}"cg-Xlib ||
exit 1
76 header
="$(mktemp -t gitpatch.XXXXXX)"
77 patch="$(mktemp -t gitpatch.XXXXXX)"
79 cg-diff
-p -r "$id" >"$patch"
80 git-cat-file commit
"$id" |
while read -r key rest
; do
84 showdate
${date[*]}; pdate
="$_showdate"
85 [ "$pdate" ] && rest
="${rest%> *}> $pdate"
86 echo "$key" "$rest" >>"$header"
90 if [ ! "$omit_header" ]; then
97 cat "$patch" | git-apply
--stat
101 echo "$key" "$rest" >>"$header"
107 rm "$header" "$patch"
116 fileformat
="%s/%02d-%s.patch"
120 elif optparse
-r=; then
121 if echo "$OPTARG" | fgrep
-q '..'; then
122 log_end
="${OPTARG#*..}"
123 [ "$log_end" ] || log_end
="HEAD"
124 log_start
="${OPTARG%..*}"
125 elif echo "$OPTARG" |
grep -q ':'; then
126 log_end
="${OPTARG#*:}"
127 [ "$log_end" ] || log_end
="HEAD"
128 log_start
="${OPTARG%:*}"
129 elif [ -z "$log_start" ]; then
134 elif optparse
-m; then
136 elif optparse
-d=; then
138 elif optparse
-f=; then
145 if [ "$mergebase" ]; then
146 [ "$log_start" ] || log_start
="HEAD"
147 [ "$log_end" ] ||
{ log_end
="$(choose_origin refs/heads "what to mkpatch against?
")" ||
exit 1; }
148 id1
="$(cg-object-id -c "$log_start")" ||
exit 1
149 id2
="$(cg-object-id -c "$log_end")" ||
exit 1
150 conservative_merge_base
"$id1" "$id2" ||
exit 1
151 [ "$_cg_base_conservative" ] &&
152 warn
-b "multiple merge bases, picking the most conservative one"
153 log_start
="$_cg_baselist"
156 if [ "$log_end" ]; then
157 id1
="$(cg-object-id -c "$log_start")" ||
exit 1
158 id2
="$(cg-object-id -c "$log_end")" ||
exit 1
160 if [ "$outdir" ]; then
161 mkdir
-p "$outdir" || die
"cannot create patch directory"
165 git-rev-list
--topo-order "$id2" "^$id1" |
tac |
while read id
; do
166 if [ "$outdir" ]; then
167 title
="$(git-cat-file commit "$id" |
169 s/^[ \t]*\[[^]]*\][ \t]*//;
170 s/[:., \t][:., \t]*/-/g;
173 y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/;
176 filename
="$(printf "$fileformat" "$outdir" "$pnum" "$title")"
178 showpatch
"$id" >"$filename"
185 echo '!-------------------------------------------------------------flip-'
192 [ "$outdir" ] && die
"-d makes sense only for patch series"
193 id
="$(cg-object-id -c "$log_start")" ||
exit 1