Code

gitweb: blame table row no highlight fix
[git.git] / git-am.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2005, 2006 Junio C Hamano
5 USAGE='[--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way]
6   [--interactive] [--whitespace=<option>] <mbox>...
7   or, when resuming [--skip | --resolved]'
8 . git-sh-setup
10 git var GIT_COMMITTER_IDENT >/dev/null || exit
12 stop_here () {
13     echo "$1" >"$dotest/next"
14     exit 1
15 }
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\"."
38     stop_here $1
39 }
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 }
48 fall_back_3way () {
49     O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
51     rm -fr "$dotest"/patch-merge-*
52     mkdir "$dotest/patch-merge-tmp-dir"
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
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
75     echo Falling back to patching base and 3-way merge...
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.
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 }
93 prec=4
94 rloga=am
95 dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg=
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;;
106         -i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\
107         --interacti|--interactiv|--interactive)
108         interactive=t; shift ;;
110         -b|--b|--bi|--bin|--bina|--binar|--binary)
111         binary=t; shift ;;
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 ;;
122         -r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved)
123         resolved=t; shift ;;
125         --sk|--ski|--skip)
126         skip=t; shift ;;
128         --whitespace=*)
129         ws=$1; shift ;;
131         --resolvemsg=*)
132         resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//"); shift ;;
134         --reflog-action=*)
135         rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;;
137         --)
138         shift; break ;;
139         -*)
140         usage ;;
141         *)
142         break ;;
143         esac
144 done
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
157 if test -d "$dotest"
158 then
159         if test ",$#," != ",0," || ! tty -s
160         then
161                 die "previous dotest directory $dotest still exists but mbox given."
162         fi
163         resume=yes
164 else
165         # Make sure we are not given --skip nor --resolved
166         test ",$skip,$resolved," = ,,, ||
167                 die "Resolve operation not in progress, we are not resuming."
169         # Start afresh.
170         mkdir -p "$dotest" || exit
172         git-mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
173                 rm -fr "$dotest"
174                 exit 1
175         }
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
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
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
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
228 if test "$this" -gt "$last"
229 then
230         echo Nothing to do.
231         rm -fr "$dotest"
232         exit
233 fi
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         }
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
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")"
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
272         export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
274         SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
275         case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
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
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
345         if test $action = skip
346         then
347                 go_next
348                 continue
349         fi
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
357         echo
358         echo "Applying '$SUBJECT'"
359         echo
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_user_resolve $this
377                 fi
378                 unmerged=$(git-ls-files -u)
379                 if test -n "$unmerged"
380                 then
381                         echo "You still have unmerged paths in your index"
382                         echo "did you forget update-index?"
383                         stop_here_user_resolve $this
384                 fi
385                 apply_status=0
386                 ;;
387         esac
389         if test $apply_status = 1 && test "$threeway" = t
390         then
391                 if (fall_back_3way)
392                 then
393                     # Applying the patch to an earlier tree and merging the
394                     # result may have produced the same tree as ours.
395                     changed="$(git-diff-index --cached --name-only HEAD)"
396                     if test '' = "$changed"
397                     then
398                             echo No changes -- Patch already applied.
399                             go_next
400                             continue
401                     fi
402                     # clear apply_status -- we have successfully merged.
403                     apply_status=0
404                 fi
405         fi
406         if test $apply_status != 0
407         then
408                 echo Patch failed at $msgnum.
409                 stop_here_user_resolve $this
410         fi
412         if test -x "$GIT_DIR"/hooks/pre-applypatch
413         then
414                 "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
415         fi
417         tree=$(git-write-tree) &&
418         echo Wrote tree $tree &&
419         parent=$(git-rev-parse --verify HEAD) &&
420         commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
421         echo Committed: $commit &&
422         git-update-ref -m "$rloga: $SUBJECT" HEAD $commit $parent ||
423         stop_here $this
425         if test -x "$GIT_DIR"/hooks/post-applypatch
426         then
427                 "$GIT_DIR"/hooks/post-applypatch
428         fi
430         go_next
431 done
433 rm -fr "$dotest"