Code

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