Fix crash when reading the empty tree
[gitweb.git] / git-commit.sh
index 59551d99f9672233da2d8d847f9f4d535dba26de..26cd7ca54de2e0b4802546d2611cd379694ec005 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Linus Torvalds
 # Copyright (c) 2006 Junio C Hamano
 
-USAGE='[-a] [-i] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [--author <author>] [<path>...]'
+USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>) [--amend] [-e] [--author <author>] [[-i | -o] <path>...]'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
 
@@ -64,6 +64,22 @@ run_status () {
        # We always show status for the whole tree.
        cd "$TOP"
 
+       IS_INITIAL="$initial_commit"
+       REFERENCE=HEAD
+       case "$amend" in
+       t)
+               # If we are amending the initial commit, there
+               # is no HEAD^1.
+               if git-rev-parse --verify "HEAD^1" >/dev/null 2>&1
+               then
+                       REFERENCE="HEAD^1"
+                       IS_INITIAL=
+               else
+                       IS_INITIAL=t
+               fi
+               ;;
+       esac
+
        # If TMP_INDEX is defined, that means we are doing
        # "--only" partial commit, and that index file is used
        # to build the tree for the commit.  Otherwise, if
@@ -85,10 +101,10 @@ run_status () {
        *)  echo "# On branch $branch" ;;
        esac
 
-       if test -z "$initial_commit"
+       if test -z "$IS_INITIAL"
        then
            git-diff-index -M --cached --name-status \
-               --diff-filter=MDTCRA HEAD |
+               --diff-filter=MDTCRA $REFERENCE |
            sed -e '
                    s/\\/\\\\/g
                    s/ /\\ /g
@@ -145,14 +161,19 @@ run_status () {
            }
        '
 
-       if test -n "$verbose"
+       if test -n "$verbose" -a -z "$IS_INITIAL"
        then
-           git-diff-index --cached -M -p --diff-filter=MDTCRA HEAD
+           git-diff-index --cached -M -p --diff-filter=MDTCRA $REFERENCE
        fi
        case "$committable" in
        0)
-           echo "nothing to commit"
-           exit 1
+               case "$amend" in
+               t)
+                       echo "# No changes" ;;
+               *)
+                       echo "nothing to commit" ;;
+               esac
+               exit 1 ;;
        esac
        exit 0
     )
@@ -173,6 +194,7 @@ also=
 only=
 logfile=
 use_commit=
+amend=
 no_edit=
 log_given=
 log_message=
@@ -180,6 +202,7 @@ verify=t
 verbose=
 signoff=
 force_author=
+only_include_assumed=
 while case "$#" in 0) break;; esac
 do
   case "$1" in
@@ -253,6 +276,12 @@ do
       verify=
       shift
       ;;
+  --a|--am|--ame|--amen|--amend)
+      amend=t
+      log_given=t$log_given
+      use_commit=HEAD
+      shift
+      ;;
   -c)
       case "$#" in 1) usage ;; esac
       shift
@@ -327,28 +356,32 @@ done
 ################################################################
 # Sanity check options
 
+case "$amend,$initial_commit" in
+t,t)
+  die "You do not have anything to amend." ;;
+t,)
+  if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
+    die "You are in the middle of a merge -- cannot amend."
+  fi ;;
+esac
+
 case "$log_given" in
 tt*)
   die "Only one of -c/-C/-F/-m can be used." ;;
 esac
 
-case "$#,$also$only" in
-*,tt)
+case "$#,$also,$only,$amend" in
+*,t,t,*)
   die "Only one of --include/--only can be used." ;;
-0,t)
+0,t,,* | 0,,t,)
   die "No paths with --include/--only does not make sense." ;;
-0,)
+0,,t,t)
+  only_include_assumed="# Clever... amending the last one with dirty index." ;;
+0,,,*)
   ;;
-*,)
-  echo >&2 "assuming --include paths..."
-  also=t
-  # Later when switch the defaults, we will replace them with these:
-  # echo >&2 "assuming --only paths..."
-  # also=
-
-  # If we are going to launch an editor, the message won't be
-  # shown without this...
-  test -z "$log_given$status_only" && sleep 1
+*,,,*)
+  only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
+  also=
   ;;
 esac
 unset only
@@ -383,6 +416,8 @@ t,)
        ;;
 ,t)
        save_index &&
+       git-ls-files --error-unmatch -- "$@" >/dev/null || exit
+
        git-diff-files --name-only -z -- "$@"  |
        (
                cd "$TOP"
@@ -411,7 +446,7 @@ t,)
                refuse_partial "Different in index and the last commit:
 $dirty_in_index"
            fi
-           commit_only=`git-ls-files -- "$@"`
+           commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
 
            # Build the temporary index and update the real index
            # the same way.
@@ -509,7 +544,7 @@ t)
        ;;
 esac
 
-if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
+if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
        echo "#"
        echo "# It looks like you may be committing a MERGE."
        echo "# If this is not correct, please remove the file"
@@ -521,8 +556,8 @@ fi >>"$GIT_DIR"/COMMIT_EDITMSG
 # Author
 if test '' != "$force_author"
 then
-       GIT_AUTHOR_NAME=`expr "$force_author" : '\(.*[^ ]\) *<.*'` &&
-       GIT_AUTHOR_EMAIL=`expr "$force_author" : '.*\(<.*\)'` &&
+       GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
+       GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
        test '' != "$GIT_AUTHOR_NAME" &&
        test '' != "$GIT_AUTHOR_EMAIL" ||
        die "malformatted --author parameter"
@@ -563,22 +598,37 @@ if test -z "$initial_commit"
 then
        if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
                PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
+       elif test -n "$amend"; then
+               PARENTS=$(git-cat-file commit HEAD |
+                       sed -n -e '/^$/q' -e 's/^parent /-p /p')
        fi
+       current=$(git-rev-parse --verify HEAD)
 else
        if [ -z "$(git-ls-files)" ]; then
                echo >&2 Nothing to commit
                exit 1
        fi
        PARENTS=""
+       current=
 fi
 
-run_status >>"$GIT_DIR"/COMMIT_EDITMSG
-if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
+if test -z "$no_edit"
+then
+       {
+               test -z "$only_include_assumed" || echo "$only_include_assumed"
+               run_status
+       } >>"$GIT_DIR"/COMMIT_EDITMSG
+else
+       # we need to check if there is anything to commit
+       run_status >/dev/null 
+fi
+if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" -a -z "$amend" ]
 then
        rm -f "$GIT_DIR/COMMIT_EDITMSG"
        run_status
        exit 1
 fi
+
 case "$no_edit" in
 '')
        case "${VISUAL:-$EDITOR},$TERM" in