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