Code

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