Merge branch 'lt/apply' into next
[git.git] / git-am.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2005, 2006 Junio C Hamano
4
5 USAGE='[--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] <mbox>
6   or, when resuming [--skip | --resolved]'
7 . git-sh-setup
8
9 git var GIT_COMMITTER_IDENT >/dev/null || exit
10
11 stop_here () {
12     echo "$1" >"$dotest/next"
13     exit 1
14 }
15
16 go_next () {
17         rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
18                 "$dotest/patch" "$dotest/info"
19         echo "$next" >"$dotest/next"
20         this=$next
21 }
22
23 fall_back_3way () {
24     O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
25
26     rm -fr "$dotest"/patch-merge-*
27     mkdir "$dotest/patch-merge-tmp-dir"
28
29     # First see if the patch records the index info that we can use.
30     if git-apply -z --index-info "$dotest/patch" \
31         >"$dotest/patch-merge-index-info" 2>/dev/null &&
32         GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
33         git-update-index -z --index-info <"$dotest/patch-merge-index-info" &&
34         GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
35         git-write-tree >"$dotest/patch-merge-base+" &&
36         # index has the base tree now.
37         (
38             cd "$dotest/patch-merge-tmp-dir" &&
39             GIT_INDEX_FILE="../patch-merge-tmp-index" \
40             GIT_OBJECT_DIRECTORY="$O_OBJECT" \
41             git-apply $binary --index <../patch
42         )
43     then
44         echo Using index info to reconstruct a base tree...
45         mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
46         mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
47     else
48         # Otherwise, try nearby trees that can be used to apply the
49         # patch.
50         (
51             N=10
52
53             # Hoping the patch is against our recent commits...
54             git-rev-list --max-count=$N HEAD
55
56             # or hoping the patch is against known tags...
57             git-ls-remote --tags .
58         ) |
59         while read base junk
60         do
61             # See if we have it as a tree...
62             git-cat-file tree "$base" >/dev/null 2>&1 || continue
63
64             rm -fr "$dotest"/patch-merge-* &&
65             mkdir "$dotest/patch-merge-tmp-dir" || break
66             (
67                 cd "$dotest/patch-merge-tmp-dir" &&
68                 GIT_INDEX_FILE=../patch-merge-tmp-index &&
69                 GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
70                 export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
71                 git-read-tree "$base" &&
72                 git-apply $binary --index &&
73                 mv ../patch-merge-tmp-index ../patch-merge-index &&
74                 echo "$base" >../patch-merge-base
75             ) <"$dotest/patch"  2>/dev/null && break
76         done
77     fi
78
79     test -f "$dotest/patch-merge-index" &&
80     his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git-write-tree) &&
81     orig_tree=$(cat "$dotest/patch-merge-base") &&
82     rm -fr "$dotest"/patch-merge-* || exit 1
83
84     echo Falling back to patching base and 3-way merge...
85
86     # This is not so wrong.  Depending on which base we picked,
87     # orig_tree may be wildly different from ours, but his_tree
88     # has the same set of wildly different changes in parts the
89     # patch did not touch, so resolve ends up cancelling them,
90     # saying that we reverted all those changes.
91
92     git-merge-resolve $orig_tree -- HEAD $his_tree || {
93             if test -d "$GIT_DIR/rr-cache"
94             then
95                 git-rerere
96             fi
97             echo Failed to merge in the changes.
98             exit 1
99     }
100 }
101
102 prec=4
103 dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws=
104
105 while case "$#" in 0) break;; esac
106 do
107         case "$1" in
108         -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
109         dotest=`expr "$1" : '-[^=]*=\(.*\)'`; shift ;;
110         -d|--d|--do|--dot|--dote|--dotes|--dotest)
111         case "$#" in 1) usage ;; esac; shift
112         dotest="$1"; shift;;
113
114         -i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\
115         --interacti|--interactiv|--interactive)
116         interactive=t; shift ;;
117
118         -b|--b|--bi|--bin|--bina|--binar|--binary)
119         binary=t; shift ;;
120
121         -3|--3|--3w|--3wa|--3way)
122         threeway=t; shift ;;
123         -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
124         sign=t; shift ;;
125         -u|--u|--ut|--utf|--utf8)
126         utf8=t; shift ;;
127         -k|--k|--ke|--kee|--keep)
128         keep=t; shift ;;
129
130         -r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved)
131         resolved=t; shift ;;
132
133         --sk|--ski|--skip)
134         skip=t; shift ;;
135
136         --whitespace=*)
137         ws=$1; shift ;;
138
139         --)
140         shift; break ;;
141         -*)
142         usage ;;
143         *)
144         break ;;
145         esac
146 done
147
148 # If the dotest directory exists, but we have finished applying all the
149 # patches in them, clear it out.
150 if test -d "$dotest" &&
151    last=$(cat "$dotest/last") &&
152    next=$(cat "$dotest/next") &&
153    test $# != 0 &&
154    test "$next" -gt "$last"
155 then
156    rm -fr "$dotest"
157 fi
158
159 if test -d "$dotest"
160 then
161         test ",$#," = ",0," ||
162         die "previous dotest directory $dotest still exists but mbox given."
163         resume=yes
164 else
165         # Make sure we are not given --skip nor --resolved
166         test ",$skip,$resolved," = ,,, ||
167                 die "we are not resuming."
168
169         # Start afresh.
170         mkdir -p "$dotest" || exit
171
172         git-mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
173                 rm -fr "$dotest"
174                 exit 1
175         }
176
177         # -b, -s, -u, -k and --whitespace flags are kept for the
178         # resuming session after a patch failure.
179         # -3 and -i can and must be given when resuming.
180         echo "$binary" >"$dotest/binary"
181         echo " $ws" >"$dotest/whitespace"
182         echo "$sign" >"$dotest/sign"
183         echo "$utf8" >"$dotest/utf8"
184         echo "$keep" >"$dotest/keep"
185         echo 1 >"$dotest/next"
186 fi
187
188 case "$resolved" in
189 '')
190         files=$(git-diff-index --cached --name-only HEAD) || exit
191         if [ "$files" ]; then
192            echo "Dirty index: cannot apply patches (dirty: $files)" >&2
193            exit 1
194         fi
195 esac
196
197 if test "$(cat "$dotest/binary")" = t
198 then
199         binary=--allow-binary-replacement
200 fi
201 if test "$(cat "$dotest/utf8")" = t
202 then
203         utf8=-u
204 fi
205 if test "$(cat "$dotest/keep")" = t
206 then
207         keep=-k
208 fi
209 ws=`cat "$dotest/whitespace"`
210 if test "$(cat "$dotest/sign")" = t
211 then
212         SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
213                         s/>.*/>/
214                         s/^/Signed-off-by: /'
215                 `
216 else
217         SIGNOFF=
218 fi
219
220 last=`cat "$dotest/last"`
221 this=`cat "$dotest/next"`
222 if test "$skip" = t
223 then
224         this=`expr "$this" + 1`
225         resume=
226 fi
227
228 if test "$this" -gt "$last"
229 then
230         echo Nothing to do.
231         rm -fr "$dotest"
232         exit
233 fi
234
235 while test "$this" -le "$last"
236 do
237         msgnum=`printf "%0${prec}d" $this`
238         next=`expr "$this" + 1`
239         test -f "$dotest/$msgnum" || {
240                 resume=
241                 go_next
242                 continue
243         }
244
245         # If we are not resuming, parse and extract the patch information
246         # into separate files:
247         #  - info records the authorship and title
248         #  - msg is the rest of commit log message
249         #  - patch is the patch body.
250         #
251         # When we are resuming, these files are either already prepared
252         # by the user, or the user can tell us to do so by --resolved flag.
253         case "$resume" in
254         '')
255                 git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
256                         <"$dotest/$msgnum" >"$dotest/info" ||
257                         stop_here $this
258                 git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
259                 ;;
260         esac
261
262         GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
263         GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
264         GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
265
266         if test -z "$GIT_AUTHOR_EMAIL"
267         then
268                 echo "Patch does not have a valid e-mail address."
269                 stop_here $this
270         fi
271
272         export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
273
274         SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
275         case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
276
277         case "$resume" in
278         '')
279             if test '' != "$SIGNOFF"
280             then
281                 LAST_SIGNED_OFF_BY=`
282                     sed -ne '/^Signed-off-by: /p' \
283                     "$dotest/msg-clean" |
284                     tail -n 1
285                 `
286                 ADD_SIGNOFF=`
287                     test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
288                     test '' = "$LAST_SIGNED_OFF_BY" && echo
289                     echo "$SIGNOFF"
290                 }`
291             else
292                 ADD_SIGNOFF=
293             fi
294             {
295                 echo "$SUBJECT"
296                 if test -s "$dotest/msg-clean"
297                 then
298                         echo
299                         cat "$dotest/msg-clean"
300                 fi
301                 if test '' != "$ADD_SIGNOFF"
302                 then
303                         echo "$ADD_SIGNOFF"
304                 fi
305             } >"$dotest/final-commit"
306             ;;
307         *)
308                 case "$resolved$interactive" in
309                 tt)
310                         # This is used only for interactive view option.
311                         git-diff-index -p --cached HEAD >"$dotest/patch"
312                         ;;
313                 esac
314         esac
315
316         resume=
317         if test "$interactive" = t
318         then
319             test -t 0 ||
320             die "cannot be interactive without stdin connected to a terminal."
321             action=again
322             while test "$action" = again
323             do
324                 echo "Commit Body is:"
325                 echo "--------------------------"
326                 cat "$dotest/final-commit"
327                 echo "--------------------------"
328                 printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
329                 read reply
330                 case "$reply" in
331                 [yY]*) action=yes ;;
332                 [aA]*) action=yes interactive= ;;
333                 [nN]*) action=skip ;;
334                 [eE]*) "${VISUAL:-${EDITOR:-vi}}" "$dotest/final-commit"
335                        action=again ;;
336                 [vV]*) action=again
337                        LESS=-S ${PAGER:-less} "$dotest/patch" ;;
338                 *)     action=again ;;
339                 esac
340             done
341         else
342             action=yes
343         fi
344
345         if test $action = skip
346         then
347                 go_next
348                 continue
349         fi
350
351         if test -x "$GIT_DIR"/hooks/applypatch-msg
352         then
353                 "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
354                 stop_here $this
355         fi
356
357         echo
358         echo "Applying '$SUBJECT'"
359         echo
360
361         case "$resolved" in
362         '')
363                 git-apply $binary --index $ws "$dotest/patch"
364                 apply_status=$?
365                 ;;
366         t)
367                 # Resolved means the user did all the hard work, and
368                 # we do not have to do any patch application.  Just
369                 # trust what the user has in the index file and the
370                 # working tree.
371                 resolved=
372                 changed="$(git-diff-index --cached --name-only HEAD)"
373                 if test '' = "$changed"
374                 then
375                         echo "No changes - did you forget update-index?"
376                         stop_here $this
377                 fi
378                 apply_status=0
379                 ;;
380         esac
381
382         if test $apply_status = 1 && test "$threeway" = t
383         then
384                 if (fall_back_3way)
385                 then
386                     # Applying the patch to an earlier tree and merging the
387                     # result may have produced the same tree as ours.
388                     changed="$(git-diff-index --cached --name-only HEAD)"
389                     if test '' = "$changed"
390                     then
391                             echo No changes -- Patch already applied.
392                             go_next
393                             continue
394                     fi
395                     # clear apply_status -- we have successfully merged.
396                     apply_status=0
397                 fi
398         fi
399         if test $apply_status != 0
400         then
401                 echo Patch failed at $msgnum.
402                 stop_here $this
403         fi
404
405         if test -x "$GIT_DIR"/hooks/pre-applypatch
406         then
407                 "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
408         fi
409
410         tree=$(git-write-tree) &&
411         echo Wrote tree $tree &&
412         parent=$(git-rev-parse --verify HEAD) &&
413         commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
414         echo Committed: $commit &&
415         git-update-ref HEAD $commit $parent ||
416         stop_here $this
417
418         if test -x "$GIT_DIR"/hooks/post-applypatch
419         then
420                 "$GIT_DIR"/hooks/post-applypatch
421         fi
422
423         go_next
424 done
425
426 rm -fr "$dotest"