git.el: Preserve file marks when doing a full refresh.
[git] / git-commit.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2005 Linus Torvalds
4 # Copyright (c) 2006 Junio C Hamano
5
6 USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
7 SUBDIRECTORY_OK=Yes
8 . git-sh-setup
9 require_work_tree
10
11 git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
12
13 case "$0" in
14 *status)
15         status_only=t
16         ;;
17 *commit)
18         status_only=
19         ;;
20 esac
21
22 refuse_partial () {
23         echo >&2 "$1"
24         echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
25         exit 1
26 }
27
28 THIS_INDEX="$GIT_DIR/index"
29 NEXT_INDEX="$GIT_DIR/next-index$$"
30 rm -f "$NEXT_INDEX"
31 save_index () {
32         cp -p "$THIS_INDEX" "$NEXT_INDEX"
33 }
34
35 run_status () {
36         # If TMP_INDEX is defined, that means we are doing
37         # "--only" partial commit, and that index file is used
38         # to build the tree for the commit.  Otherwise, if
39         # NEXT_INDEX exists, that is the index file used to
40         # make the commit.  Otherwise we are using as-is commit
41         # so the regular index file is what we use to compare.
42         if test '' != "$TMP_INDEX"
43         then
44                 GIT_INDEX_FILE="$TMP_INDEX"
45                 export GIT_INDEX_FILE
46         elif test -f "$NEXT_INDEX"
47         then
48                 GIT_INDEX_FILE="$NEXT_INDEX"
49                 export GIT_INDEX_FILE
50         fi
51
52         if test "$status_only" = "t" -o "$use_status_color" = "t"; then
53                 color=
54         else
55                 color=--nocolor
56         fi
57         git runstatus ${color} \
58                 ${verbose:+--verbose} \
59                 ${amend:+--amend} \
60                 ${untracked_files:+--untracked}
61 }
62
63 trap '
64         test -z "$TMP_INDEX" || {
65                 test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
66         }
67         rm -f "$NEXT_INDEX"
68 ' 0
69
70 ################################################################
71 # Command line argument parsing and sanity checking
72
73 all=
74 also=
75 interactive=
76 only=
77 logfile=
78 use_commit=
79 amend=
80 edit_flag=
81 no_edit=
82 log_given=
83 log_message=
84 verify=t
85 quiet=
86 verbose=
87 signoff=
88 force_author=
89 only_include_assumed=
90 untracked_files=
91 templatefile="`git config commit.template`"
92 while test $# != 0
93 do
94         case "$1" in
95         -F|--F|-f|--f|--fi|--fil|--file)
96                 case "$#" in 1) usage ;; esac
97                 shift
98                 no_edit=t
99                 log_given=t$log_given
100                 logfile="$1"
101                 ;;
102         -F*|-f*)
103                 no_edit=t
104                 log_given=t$log_given
105                 logfile="${1#-[Ff]}"
106                 ;;
107         --F=*|--f=*|--fi=*|--fil=*|--file=*)
108                 no_edit=t
109                 log_given=t$log_given
110                 logfile="${1#*=}"
111                 ;;
112         -a|--a|--al|--all)
113                 all=t
114                 ;;
115         --au=*|--aut=*|--auth=*|--autho=*|--author=*)
116                 force_author="${1#*=}"
117                 ;;
118         --au|--aut|--auth|--autho|--author)
119                 case "$#" in 1) usage ;; esac
120                 shift
121                 force_author="$1"
122                 ;;
123         -e|--e|--ed|--edi|--edit)
124                 edit_flag=t
125                 ;;
126         -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
127                 also=t
128                 ;;
129         --int|--inte|--inter|--intera|--interac|--interact|--interacti|\
130         --interactiv|--interactive)
131                 interactive=t
132                 ;;
133         -o|--o|--on|--onl|--only)
134                 only=t
135                 ;;
136         -m|--m|--me|--mes|--mess|--messa|--messag|--message)
137                 case "$#" in 1) usage ;; esac
138                 shift
139                 log_given=m$log_given
140                 log_message="${log_message:+${log_message}
141
142 }$1"
143                 no_edit=t
144                 ;;
145         -m*)
146                 log_given=m$log_given
147                 log_message="${log_message:+${log_message}
148
149 }${1#-m}"
150                 no_edit=t
151                 ;;
152         --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
153                 log_given=m$log_given
154                 log_message="${log_message:+${log_message}
155
156 }${1#*=}"
157                 no_edit=t
158                 ;;
159         -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
160         --no-verify)
161                 verify=
162                 ;;
163         --a|--am|--ame|--amen|--amend)
164                 amend=t
165                 use_commit=HEAD
166                 ;;
167         -c)
168                 case "$#" in 1) usage ;; esac
169                 shift
170                 log_given=t$log_given
171                 use_commit="$1"
172                 no_edit=
173                 ;;
174         --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
175         --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
176         --reedit-messag=*|--reedit-message=*)
177                 log_given=t$log_given
178                 use_commit="${1#*=}"
179                 no_edit=
180                 ;;
181         --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
182         --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
183         --reedit-message)
184                 case "$#" in 1) usage ;; esac
185                 shift
186                 log_given=t$log_given
187                 use_commit="$1"
188                 no_edit=
189                 ;;
190         -C)
191                 case "$#" in 1) usage ;; esac
192                 shift
193                 log_given=t$log_given
194                 use_commit="$1"
195                 no_edit=t
196                 ;;
197         --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
198         --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
199         --reuse-message=*)
200                 log_given=t$log_given
201                 use_commit="${1#*=}"
202                 no_edit=t
203                 ;;
204         --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
205         --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
206                 case "$#" in 1) usage ;; esac
207                 shift
208                 log_given=t$log_given
209                 use_commit="$1"
210                 no_edit=t
211                 ;;
212         -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
213                 signoff=t
214                 ;;
215         -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
216                 case "$#" in 1) usage ;; esac
217                 shift
218                 templatefile="$1"
219                 no_edit=
220                 ;;
221         -q|--q|--qu|--qui|--quie|--quiet)
222                 quiet=t
223                 ;;
224         -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
225                 verbose=t
226                 ;;
227         -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
228         --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
229         --untracked-file|--untracked-files)
230                 untracked_files=t
231                 ;;
232         --)
233                 shift
234                 break
235                 ;;
236         -*)
237                 usage
238                 ;;
239         *)
240                 break
241                 ;;
242         esac
243         shift
244 done
245 case "$edit_flag" in t) no_edit= ;; esac
246
247 ################################################################
248 # Sanity check options
249
250 case "$amend,$initial_commit" in
251 t,t)
252         die "You do not have anything to amend." ;;
253 t,)
254         if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
255                 die "You are in the middle of a merge -- cannot amend."
256         fi ;;
257 esac
258
259 case "$log_given" in
260 tt*)
261         die "Only one of -c/-C/-F can be used." ;;
262 *tm*|*mt*)
263         die "Option -m cannot be combined with -c/-C/-F." ;;
264 esac
265
266 case "$#,$also,$only,$amend" in
267 *,t,t,*)
268         die "Only one of --include/--only can be used." ;;
269 0,t,,* | 0,,t,)
270         die "No paths with --include/--only does not make sense." ;;
271 0,,t,t)
272         only_include_assumed="# Clever... amending the last one with dirty index." ;;
273 0,,,*)
274         ;;
275 *,,,*)
276         only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
277         also=
278         ;;
279 esac
280 unset only
281 case "$all,$interactive,$also,$#" in
282 *t,*t,*)
283         die "Cannot use -a, --interactive or -i at the same time." ;;
284 t,,[1-9]*)
285         die "Paths with -a does not make sense." ;;
286 ,t,[1-9]*)
287         die "Paths with --interactive does not make sense." ;;
288 ,,t,0)
289         die "No paths with -i does not make sense." ;;
290 esac
291
292 if test ! -z "$templatefile" -a -z "$log_given"
293 then
294         if test ! -f "$templatefile"
295         then
296                 die "Commit template file does not exist."
297         fi
298 fi
299
300 ################################################################
301 # Prepare index to have a tree to be committed
302
303 case "$all,$also" in
304 t,)
305         if test ! -f "$THIS_INDEX"
306         then
307                 die 'nothing to commit (use "git add file1 file2" to include for commit)'
308         fi
309         save_index &&
310         (
311                 cd_to_toplevel &&
312                 GIT_INDEX_FILE="$NEXT_INDEX" &&
313                 export GIT_INDEX_FILE &&
314                 git diff-files --name-only -z |
315                 git update-index --remove -z --stdin
316         ) || exit
317         ;;
318 ,t)
319         save_index &&
320         git ls-files --error-unmatch -- "$@" >/dev/null || exit
321
322         git diff-files --name-only -z -- "$@"  |
323         (
324                 cd_to_toplevel &&
325                 GIT_INDEX_FILE="$NEXT_INDEX" &&
326                 export GIT_INDEX_FILE &&
327                 git update-index --remove -z --stdin
328         ) || exit
329         ;;
330 ,)
331         if test "$interactive" = t; then
332                 git add --interactive || exit
333         fi
334         case "$#" in
335         0)
336                 ;; # commit as-is
337         *)
338                 if test -f "$GIT_DIR/MERGE_HEAD"
339                 then
340                         refuse_partial "Cannot do a partial commit during a merge."
341                 fi
342
343                 TMP_INDEX="$GIT_DIR/tmp-index$$"
344                 W=
345                 test -z "$initial_commit" && W=--with-tree=HEAD
346                 commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit
347
348                 # Build a temporary index and update the real index
349                 # the same way.
350                 if test -z "$initial_commit"
351                 then
352                         GIT_INDEX_FILE="$THIS_INDEX" \
353                         git read-tree --index-output="$TMP_INDEX" -i -m HEAD
354                 else
355                         rm -f "$TMP_INDEX"
356                 fi || exit
357
358                 printf '%s\n' "$commit_only" |
359                 GIT_INDEX_FILE="$TMP_INDEX" \
360                 git update-index --add --remove --stdin &&
361
362                 save_index &&
363                 printf '%s\n' "$commit_only" |
364                 (
365                         GIT_INDEX_FILE="$NEXT_INDEX"
366                         export GIT_INDEX_FILE
367                         git update-index --add --remove --stdin
368                 ) || exit
369                 ;;
370         esac
371         ;;
372 esac
373
374 ################################################################
375 # If we do as-is commit, the index file will be THIS_INDEX,
376 # otherwise NEXT_INDEX after we make this commit.  We leave
377 # the index as is if we abort.
378
379 if test -f "$NEXT_INDEX"
380 then
381         USE_INDEX="$NEXT_INDEX"
382 else
383         USE_INDEX="$THIS_INDEX"
384 fi
385
386 case "$status_only" in
387 t)
388         # This will silently fail in a read-only repository, which is
389         # what we want.
390         GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh
391         run_status
392         exit $?
393         ;;
394 '')
395         GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit
396         ;;
397 esac
398
399 ################################################################
400 # Grab commit message, write out tree and make commit.
401
402 if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
403 then
404     GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \
405     || exit
406 fi
407
408 if test "$log_message" != ''
409 then
410         printf '%s\n' "$log_message"
411 elif test "$logfile" != ""
412 then
413         if test "$logfile" = -
414         then
415                 test -t 0 &&
416                 echo >&2 "(reading log message from standard input)"
417                 cat
418         else
419                 cat <"$logfile"
420         fi
421 elif test "$use_commit" != ""
422 then
423         encoding=$(git config i18n.commitencoding || echo UTF-8)
424         git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
425         sed -e '1,/^$/d' -e 's/^    //'
426 elif test -f "$GIT_DIR/MERGE_MSG"
427 then
428         cat "$GIT_DIR/MERGE_MSG"
429 elif test -f "$GIT_DIR/SQUASH_MSG"
430 then
431         cat "$GIT_DIR/SQUASH_MSG"
432 elif test "$templatefile" != ""
433 then
434         cat "$templatefile"
435 fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
436
437 case "$signoff" in
438 t)
439         sign=$(git-var GIT_COMMITTER_IDENT | sed -e '
440                 s/>.*/>/
441                 s/^/Signed-off-by: /
442                 ')
443         blank_before_signoff=
444         tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
445         grep 'Signed-off-by:' >/dev/null || blank_before_signoff='
446 '
447         tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
448         grep "$sign"$ >/dev/null ||
449         printf '%s%s\n' "$blank_before_signoff" "$sign" \
450                 >>"$GIT_DIR"/COMMIT_EDITMSG
451         ;;
452 esac
453
454 if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
455         echo "#"
456         echo "# It looks like you may be committing a MERGE."
457         echo "# If this is not correct, please remove the file"
458         printf '%s\n' "#        $GIT_DIR/MERGE_HEAD"
459         echo "# and try again"
460         echo "#"
461 fi >>"$GIT_DIR"/COMMIT_EDITMSG
462
463 # Author
464 if test '' != "$use_commit"
465 then
466         eval "$(get_author_ident_from_commit "$use_commit")"
467         export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
468 fi
469 if test '' != "$force_author"
470 then
471         GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
472         GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
473         test '' != "$GIT_AUTHOR_NAME" &&
474         test '' != "$GIT_AUTHOR_EMAIL" ||
475         die "malformed --author parameter"
476         export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
477 fi
478
479 PARENTS="-p HEAD"
480 if test -z "$initial_commit"
481 then
482         rloga='commit'
483         if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
484                 rloga='commit (merge)'
485                 PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
486         elif test -n "$amend"; then
487                 rloga='commit (amend)'
488                 PARENTS=$(git cat-file commit HEAD |
489                         sed -n -e '/^$/q' -e 's/^parent /-p /p')
490         fi
491         current="$(git rev-parse --verify HEAD)"
492 else
493         if [ -z "$(git ls-files)" ]; then
494                 echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
495                 exit 1
496         fi
497         PARENTS=""
498         rloga='commit (initial)'
499         current=''
500 fi
501 set_reflog_action "$rloga"
502
503 if test -z "$no_edit"
504 then
505         {
506                 echo ""
507                 echo "# Please enter the commit message for your changes."
508                 echo "# (Comment lines starting with '#' will not be included)"
509                 test -z "$only_include_assumed" || echo "$only_include_assumed"
510                 run_status
511         } >>"$GIT_DIR"/COMMIT_EDITMSG
512 else
513         # we need to check if there is anything to commit
514         run_status >/dev/null
515 fi
516 if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
517 then
518         rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
519         use_status_color=t
520         run_status
521         exit 1
522 fi
523
524 case "$no_edit" in
525 '')
526         git-var GIT_AUTHOR_IDENT > /dev/null  || die
527         git-var GIT_COMMITTER_IDENT > /dev/null  || die
528         git_editor "$GIT_DIR/COMMIT_EDITMSG"
529         ;;
530 esac
531
532 case "$verify" in
533 t)
534         if test -x "$GIT_DIR"/hooks/commit-msg
535         then
536                 "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
537         fi
538 esac
539
540 if test -z "$no_edit"
541 then
542     sed -e '
543         /^diff --git a\/.*/{
544             s///
545             q
546         }
547         /^#/d
548     ' "$GIT_DIR"/COMMIT_EDITMSG
549 else
550     cat "$GIT_DIR"/COMMIT_EDITMSG
551 fi |
552 git stripspace >"$GIT_DIR"/COMMIT_MSG
553
554 # Test whether the commit message has any content we didn't supply.
555 have_commitmsg=
556 grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
557         git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
558
559 # Is the commit message totally empty?
560 if test -s "$GIT_DIR"/COMMIT_BAREMSG
561 then
562         if test "$templatefile" != ""
563         then
564                 # Test whether this is just the unaltered template.
565                 if cnt=`sed -e '/^#/d' < "$templatefile" |
566                         git stripspace |
567                         diff "$GIT_DIR"/COMMIT_BAREMSG - |
568                         wc -l` &&
569                    test 0 -lt $cnt
570                 then
571                         have_commitmsg=t
572                 fi
573         else
574                 # No template, so the content in the commit message must
575                 # have come from the user.
576                 have_commitmsg=t
577         fi
578 fi
579
580 rm -f "$GIT_DIR"/COMMIT_BAREMSG
581
582 if test "$have_commitmsg" = "t"
583 then
584         if test -z "$TMP_INDEX"
585         then
586                 tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree)
587         else
588                 tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) &&
589                 rm -f "$TMP_INDEX"
590         fi &&
591         commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") &&
592         rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
593         git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
594         rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
595         if test -f "$NEXT_INDEX"
596         then
597                 mv "$NEXT_INDEX" "$THIS_INDEX"
598         else
599                 : ;# happy
600         fi
601 else
602         echo >&2 "* no commit message?  aborting commit."
603         false
604 fi
605 ret="$?"
606 rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
607
608 cd_to_toplevel
609
610 git rerere
611
612 if test "$ret" = 0
613 then
614         if test -x "$GIT_DIR"/hooks/post-commit
615         then
616                 "$GIT_DIR"/hooks/post-commit
617         fi
618         if test -z "$quiet"
619         then
620                 commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
621                        --summary --root HEAD --`
622                 echo "Created${initial_commit:+ initial} commit $commit"
623         fi
624 fi
625
626 exit "$ret"