threads / patch / 3245

patchgit-commit: revamp the git-commit semantics.

Subject: [PATCH] git-commit: revamp the git-commit semantics.

## tl;dr

16 messages between Feb 5, 2006 and Feb 8, 2006. Diffs are folded; open one to read it.

replies: 15people: 5as markdown or json

Junio C Hamano· Feb 5, 2006, 10:24 UTC · lore

I am addressing people who participated with their valuable inputs to the earlier "git commit" discussion.

Here is my first cut. I'd appreciate to hear from both git-experienced people and git newcomers how comfortable this new implementation is. Improvement patches on top of this are certainly appreciated.

The main focus of this patch is to reduce the confusion factor while not disrupting experienced git users with trained fingers, but it unfortunately does introduce two incompatible changes:

 - When the command decides not to create a commit for whatever
   reason, the updates made to the index file, either because of
   --all option or "-i paths..." option, are forgotten.
 - Traditional "git commit paths..." _must_ be spelled as "git
   commit -i paths...".  "git commit paths..." means a
   completely different thing now.

I think the first incompatibility would not break anybody's work habit and I am hoping that everybody considers this to be purely an improvement. It reduces major confusion factor in the current implementation when you do:

	$ edit foo
        $ git commit -a

and cause the commit to abort by giving it an empty log message. In the current implementation, "git diff" would not show anything after that, but with this you would see the edit of foo.

The second one is not something I am too happy about, but many people seem to have been confused that running "git update-index foo" and then "git commit bar" commits both foo and bar. To prevent this change from burning index-aware people too much, this form refuses to run during a merge. Also to reduce confusion for people who forget differences between "git diff" and "git diff HEAD", it refuses to run when the paths involved do not match between the index and HEAD.

-- >8 -- [PATCH] git-commit: revamp the git-commit semantics.

 - "git commit" without _any_ parameter keeps the traditional
   behaviour.  It commits the current index.
   We commit the whole index even when this form is run from a
   subdirectory.
 - "git commit --include paths..." (or "git commit -i paths...")
   is equivalent to:
   	git update-index --remove paths...
        git commit
 - "git commit paths..." acquires a new semantics.  This is an
   incompatible change that needs user training, which I am
   still a bit reluctant to swallow, but enough people seem to
   have complained that it is confusing to them.  It
   1. refuses to run if $GIT_DIR/MERGE_HEAD exists, and reminds
      trained git users that the traditional semantics now needs
      -i flag.
   2. refuses to run if named paths... are different in HEAD and
      the index (ditto about reminding).  Added paths are OK.
   3. reads HEAD commit into a temporary index file.
   4. updates named paths... from the working tree in this
      temporary index.
   5. does the same updates of the paths... from the working
      tree to the real index.
   6. makes a commit using the temporary index that has the
      current HEAD as the parent, and updates the HEAD with this
      new commit.
 - "git commit --all" can run from a subdirectory, but it updates
   the index with all the modified files and does a whole tree
   commit.
 - In all cases, when the command decides not to create a new
   commit, the index is left as it was before the command is
   run.  This means that the two "git diff" in the following
   sequence:
       $ git diff
       $ git commit -a
       $ git diff
   would show the same diff if you abort the commit process by
   making the commit log message empty.
This commit also introduces much requested --author option.
	$ git commit --author 'A U Thor <author@example.com>'
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/core-tutorial.txt |    6 -
 Documentation/git-commit.txt    |   61 ++++++
 git-commit.sh                   |  403 ++++++++++++++++++++++++++++-----------
 t/t1200-tutorial.sh             |    6 -
 4 files changed, 354 insertions(+), 122 deletions(-)
551045c2a2eefdc85307de5f4b72ba720d095106
Show changes to 4 files +354 −122

Documentation/core-tutorial.txt, Documentation/git-commit.txt, git-commit.sh, t/t1200-tutorial.sh

diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt
index 4513ad6..4e6ec0e 100644
--- a/Documentation/core-tutorial.txt
+++ b/Documentation/core-tutorial.txt
@@ -858,7 +858,7 @@ that branch, and do some work there.
 ------------------------------------------------
 $ git checkout mybranch
 $ echo "Work, work, work" >>hello
-$ git commit -m 'Some work.' hello
+$ git commit -m 'Some work.' -i hello
 ------------------------------------------------
 
 Here, we just added another line to `hello`, and we used a shorthand for
@@ -881,7 +881,7 @@ hasn't happened in the `master` branch a
 ------------
 $ echo "Play, play, play" >>hello
 $ echo "Lots of fun" >>example
-$ git commit -m 'Some fun.' hello example
+$ git commit -m 'Some fun.' -i hello example
 ------------
 
 since the master branch is obviously in a much better mood.
@@ -946,7 +946,7 @@ Work, work, work
 and once you're happy with your manual merge, just do a
 
 ------------
-$ git commit hello
+$ git commit -i hello
 ------------
 
 which will very loudly warn you that you're now committing a merge
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 72f96fc..53b64fa 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -8,8 +8,8 @@ git-commit - Record your changes
 SYNOPSIS
 --------
 [verse]
-'git-commit' [-a] [-s] [-v] [(-c | -C) <commit> | -F <file> | -m <msg>]
-	   [-e] [--] <file>...
+'git-commit' [-a] [-i] [-s] [-v] [(-c | -C) <commit> | -F <file> | -m <msg>]
+	   [-e] [--author <author>] [--] <file>...
 
 DESCRIPTION
 -----------
@@ -40,6 +40,10 @@ OPTIONS
 	Take the commit message from the given file.  Use '-' to
 	read the message from the standard input.
 
+--author <author>::
+	Override the author name used in the commit.  Use
+	`A U Thor <author@example.com>` format.
+
 -m <msg>::
 	Use the given <msg> as the commit message.
 
@@ -63,17 +67,66 @@ OPTIONS
 	commit log message unmodified.  This option lets you
 	further edit the message taken from these sources.
 
+-i|--include::
+	Instead of committing only the files specified on the
+	command line, update them in the index file and then
+	commit the whole index.  This is the traditional
+	behaviour.
+
 --::
 	Do not interpret any more arguments as options.
 
 <file>...::
-	Update specified paths in the index file before committing.
-
+	Commit only the files specified on the command line.
+	This format cannot be used during a merge, nor when the
+	index and the latest commit does not match on the
+	specified paths to avoid confusion.
 
 If you make a commit and then found a mistake immediately after
 that, you can recover from it with gitlink:git-reset[1].
 
 
+Discussion
+----------
+
+`git commit` without _any_ parameter commits the tree structure
+recorded by the current index file.  This is a whole-tree commit
+even the command is invoked from a subdirectory.
+
+`git commit --include paths...` is equivalent to
+
+	git update-index --remove paths...
+	git commit
+
+That is, update the specified paths to the index and then commit
+the whole tree.
+
+`git commit paths...` largely bypasses the index file and
+commits only the changes made to the specified paths.  It has
+however several safety valves to prevent confusion.
+
+. It refuses to run during a merge (i.e. when
+  `$GIT_DIR/MERGE_HEAD` exists), and reminds trained git users
+  that the traditional semantics now needs -i flag.
+
+. It refuses to run if named `paths...` are different in HEAD
+  and the index (ditto about reminding).  Added paths are OK.
+  This is because an earlier `git diff` (not `git diff HEAD`)
+  would have shown the differences since the last `git
+  update-index paths...` to the user, and an inexperienced user
+  may mistakenly think that the changes between the index and
+  the HEAD (i.e. earlier changes made before the last `git
+  update-index paths...` was done) are not being committed.
+
+. It reads HEAD commit into a temporary index file, updates the
+  specified `paths...` and makes a commit.  At the same time,
+  the real index file is also updated with the same `paths...`.
+
+`git commit --all` updates the index file with _all_ changes to
+the working tree, and makes a whole-tree commit, regardless of
+which subdirectory the command is invoked in.
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org> and
diff --git a/git-commit.sh b/git-commit.sh
index 10946ed..d3325bc 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -1,85 +1,170 @@
 #!/bin/sh
 #
 # 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>] [-e] [<path>...]'
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 
-all= logfile= use_commit= no_edit= log_given= log_message= verify=t signoff=
+git-rev-parse --verify HEAD >/dev/null 2>&1 ||
+initial_commit=t
+
+refuse_partial () {
+	echo >&2 "$1"
+	echo >&2 "Experienced git users:"
+	echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
+	exit 1
+}
+
+all=
+also=
+logfile=
+use_commit=
+no_edit=
+log_given=
+log_message=
+verify=t
+signoff=
+force_author=
 while case "$#" in 0) break;; esac
 do
   case "$1" in
+  -F|--F|-f|--f|--fi|--fil|--file)
+      case "$#" in 1) usage ;; esac
+      shift
+      no_edit=t
+      log_given=t$log_given
+      logfile="$1"
+      shift
+      ;;
+  -F*|-f*)
+      no_edit=t
+      log_given=t$log_given
+      logfile=`expr "$1" : '-[Ff]\(.*\)'`
+      shift
+      ;;
+  --F=*|--f=*|--fi=*|--fil=*|--file=*)
+      no_edit=t
+      log_given=t$log_given
+      logfile=`expr "$1" : '-[^=]*=\(.*\)'`
+      shift
+      ;;
   -a|--a|--al|--all)
-    all=t
-    shift ;;
-  -F=*|--f=*|--fi=*|--fil=*|--file=*)
-    log_given=t$log_given
-    logfile=`expr "$1" : '-[^=]*=\(.*\)'`
-    no_edit=t
-    shift ;;
-  -F|--f|--fi|--fil|--file)
-    case "$#" in 1) usage ;; esac; shift
-    log_given=t$log_given
-    logfile="$1"
-    no_edit=t
-    shift ;;
-  -m=*|--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
-    log_given=t$log_given
-    log_message=`expr "$1" : '-[^=]*=\(.*\)'`
-    no_edit=t
-    shift ;;
+      all=t
+      shift
+      ;;
+  --au=*|--aut=*|--auth=*|--autho=*|--author=*)
+      force_author=`expr "$1" : '-[^=]*=\(.*\)'`
+      shift
+      ;;
+  --au|--aut|--auth|--autho|--author)
+      case "$#" in 1) usage ;; esac
+      shift
+      force_author="$1"
+      shift
+      ;;
+  -e|--e|--ed|--edi|--edit)
+      no_edit=
+      shift
+      ;;
+  -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
+      also=t
+      shift
+      ;;
   -m|--m|--me|--mes|--mess|--messa|--messag|--message)
-    case "$#" in 1) usage ;; esac; shift
-    log_given=t$log_given
-    log_message="$1"
-    no_edit=t
-    shift ;;
-  -c=*|--ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
+      case "$#" in 1) usage ;; esac
+      shift
+      log_given=t$log_given
+      log_message="$1"
+      no_edit=t
+      shift
+      ;;
+  -m*)
+      log_given=t$log_given
+      log_message=`expr "$1" : '-m\(.*\)'`
+      no_edit=t
+      shift
+      ;;
+  --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
+      log_given=t$log_given
+      log_message=`expr "$1" : '-[^=]*=\(.*\)'`
+      no_edit=t
+      shift
+      ;;
+  -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|--no-verify)
+      verify=
+      shift
+      ;;
+  -c)
+      case "$#" in 1) usage ;; esac
+      shift
+      log_given=t$log_given
+      use_commit="$1"
+      no_edit=
+      shift
+      ;;
+  --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
   --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
   --reedit-messag=*|--reedit-message=*)
-    log_given=t$log_given
-    use_commit=`expr "$1" : '-[^=]*=\(.*\)'`
-    shift ;;
-  -c|--ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
+      log_given=t$log_given
+      use_commit=`expr "$1" : '-[^=]*=\(.*\)'`
+      no_edit=
+      shift
+      ;;
+  --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
   --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|--reedit-message)
-    case "$#" in 1) usage ;; esac; shift
-    log_given=t$log_given
-    use_commit="$1"
-    shift ;;
-  -C=*|--reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
+      case "$#" in 1) usage ;; esac
+      shift
+      log_given=t$log_given
+      use_commit="$1"
+      no_edit=
+      shift
+      ;;
+  -C)
+      case "$#" in 1) usage ;; esac
+      shift
+      log_given=t$log_given
+      use_commit="$1"
+      no_edit=t
+      shift
+      ;;
+  --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
   --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
   --reuse-message=*)
-    log_given=t$log_given
-    use_commit=`expr "$1" : '-[^=]*=\(.*\)'`
-    no_edit=t
-    shift ;;
-  -C|--reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
+      log_given=t$log_given
+      use_commit=`expr "$1" : '-[^=]*=\(.*\)'`
+      no_edit=t
+      shift
+      ;;
+  --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
   --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
-    case "$#" in 1) usage ;; esac; shift
-    log_given=t$log_given
-    use_commit="$1"
-    no_edit=t
-    shift ;;
-  -e|--e|--ed|--edi|--edit)
-    no_edit=
-    shift ;;
+      case "$#" in 1) usage ;; esac
+      shift
+      log_given=t$log_given
+      use_commit="$1"
+      no_edit=t
+      shift
+      ;;
   -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
-    signoff=t
-    shift ;;
-  -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|--no-verify)
-    verify=
-    shift ;;
+      signoff=t
+      shift
+      ;;
   -v|--v|--ve|--ver|--veri|--verif|--verify)
-    verify=t
-    shift ;;
+      verify=t
+      shift
+      ;;
   --)
-    shift
-    break ;;
+      shift
+      break
+      ;;
   -*)
-     usage ;;
+      usage
+      ;;
   *)
-    break ;;
+      break
+      ;;
   esac
 done
 
@@ -88,30 +173,92 @@ tt*)
   die "Only one of -c/-C/-F/-m can be used." ;;
 esac
 
-case "$all,$#" in
-t,0)
-	git-diff-files --name-only -z |
-	git-update-index --remove -z --stdin
-	;;
-t,*)
-	die "Cannot use -a and explicit files at the same time."
-	;;
-,0)
+TOP=`git-rev-parse --show-cdup`
+
+case "$all,$also" in
+t,t)
+	die "Cannot use -a and -i at the same time." ;;
+t,)
+	SAVE_INDEX="$GIT_DIR/save-index$$" &&
+	cp "$GIT_DIR/index" "$SAVE_INDEX" &&
+	(
+		if test '' != "$TOP"
+		then
+			cd "$TOP"
+		fi &&
+		git-diff-files --name-only -z |
+		git-update-index --remove -z --stdin
+	)
 	;;
-*)
-	git-diff-files --name-only -z -- "$@" |
+,t)
+	case "$#" in
+	0) die "No paths with -i does not make sense." ;;
+	esac
+	SAVE_INDEX="$GIT_DIR/save-index$$" &&
+	cp "$GIT_DIR/index" "$SAVE_INDEX" &&
+	git-diff-files --name-only -z -- "$@"  |
 	git-update-index --remove -z --stdin
 	;;
-esac || exit 1
+,)
+	case "$#" in
+	0)
+	    ;; # commit as-is
+	*)
+	    if test -f "$GIT_DIR/MERGE_HEAD"
+	    then
+		refuse_partial "Cannot do a partial commit during a merge."
+	    fi
+	    TMP_INDEX="$GIT_DIR/tmp-index$$"
+	    if test -z "$initial_commit"
+	    then
+		# make sure index is clean at the specified paths, or
+		# they are additions.
+		dirty_in_index=`git-diff-index --cached --name-status \
+			--diff-filter=DMTXU HEAD -- "$@"`
+		test -z "$dirty_in_index" ||
+		refuse_partial "Cannot do a partial commit of paths dirty in index:
+
+$dirty_in_index"
+	    fi
+	    commit_only=`git-ls-files -- "$@"` ;;
+	esac
+	;;
+esac
+
 git-update-index -q --refresh || exit 1
 
-case "$verify" in
-t)
-	if test -x "$GIT_DIR"/hooks/pre-commit
+trap '
+	test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
+	test -f "$SAVE_INDEX" && mv -f "$SAVE_INDEX" "$GIT_DIR/index"
+' 0
+
+if test "$TMP_INDEX"
+then
+	if test -z "$initial_commit"
 	then
-		"$GIT_DIR"/hooks/pre-commit || exit
-	fi
-esac
+		GIT_INDEX_FILE="$TMP_INDEX" git-read-tree HEAD
+	else
+		rm -f "$TMP_INDEX"
+	fi || exit
+	echo "$commit_only" |
+	GIT_INDEX_FILE="$TMP_INDEX" git-update-index --add --remove --stdin &&
+	echo "$commit_only" |
+	git-update-index --remove --stdin ||
+	exit
+else
+	#
+	:
+fi
+
+if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
+then
+	if test "$TMP_INDEX"
+	then
+		GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit
+	else
+		"$GIT_DIR"/hooks/pre-commit
+	fi || exit
+fi
 
 if test "$log_message" != ''
 then
@@ -155,42 +302,52 @@ if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
 	echo "#"
 fi >>"$GIT_DIR"/COMMIT_EDITMSG
 
+# Author
+if test '' != "$use_commit"
+then
+	pick_author_script='
+	/^author /{
+		s/'\''/'\''\\'\'\''/g
+		h
+		s/^author \([^<]*\) <[^>]*> .*$/\1/
+		s/'\''/'\''\'\'\''/g
+		s/.*/GIT_AUTHOR_NAME='\''&'\''/p
+
+		g
+		s/^author [^<]* <\([^>]*\)> .*$/\1/
+		s/'\''/'\''\'\'\''/g
+		s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
+
+		g
+		s/^author [^<]* <[^>]*> \(.*\)$/\1/
+		s/'\''/'\''\'\'\''/g
+		s/.*/GIT_AUTHOR_DATE='\''&'\''/p
+
+		q
+	}
+	'
+	set_author_env=`git-cat-file commit "$use_commit" |
+	LANG=C LC_ALL=C sed -ne "$pick_author_script"`
+	eval "$set_author_env"
+	export GIT_AUTHOR_NAME
+	export GIT_AUTHOR_EMAIL
+	export GIT_AUTHOR_DATE
+elif test '' != "$force_author"
+then
+	GIT_AUTHOR_NAME=`expr "$force_author" : '\(.*[^ ]\) *<.*'` &&
+	GIT_AUTHOR_EMAIL=`expr "$force_author" : '.*\(<.*\)'` &&
+	test '' != "$GIT_AUTHOR_NAME" &&
+	test '' != "$GIT_AUTHOR_EMAIL" ||
+	die "malformatted --author parameter"
+	export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+fi
+
 PARENTS="-p HEAD"
-if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
+if test -z "$initial_commit"
 then
 	if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
 		PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
 	fi
-	if test "$use_commit" != ""
-	then
-		pick_author_script='
-		/^author /{
-			s/'\''/'\''\\'\'\''/g
-			h
-			s/^author \([^<]*\) <[^>]*> .*$/\1/
-			s/'\''/'\''\'\'\''/g
-			s/.*/GIT_AUTHOR_NAME='\''&'\''/p
-
-			g
-			s/^author [^<]* <\([^>]*\)> .*$/\1/
-			s/'\''/'\''\'\'\''/g
-			s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
-
-			g
-			s/^author [^<]* <[^>]*> \(.*\)$/\1/
-			s/'\''/'\''\'\'\''/g
-			s/.*/GIT_AUTHOR_DATE='\''&'\''/p
-
-			q
-		}
-		'
-		set_author_env=`git-cat-file commit "$use_commit" |
-		LANG=C LC_ALL=C sed -ne "$pick_author_script"`
-		eval "$set_author_env"
-		export GIT_AUTHOR_NAME
-		export GIT_AUTHOR_EMAIL
-		export GIT_AUTHOR_DATE
-	fi
 else
 	if [ -z "$(git-ls-files)" ]; then
 		echo Nothing to commit 1>&2
@@ -198,10 +355,21 @@ else
 	fi
 	PARENTS=""
 fi
-git-status >>"$GIT_DIR"/COMMIT_EDITMSG
+
+(
+	if test '' != "$TOP"
+	then
+		cd "$TOP"
+	fi &&
+	git-status >>"$GIT_DIR"/COMMIT_EDITMSG
+)
 if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
 then
 	rm -f "$GIT_DIR/COMMIT_EDITMSG"
+	if test '' != "$TOP"
+	then
+		cd "$TOP"
+	fi &&
 	git-status
 	exit 1
 fi
@@ -213,7 +381,8 @@ case "$no_edit" in
 		echo >&2 "Please supply the commit log message using either"
 		echo >&2 "-m or -F option.  A boilerplate log message has"
 		echo >&2 "been prepared in $GIT_DIR/COMMIT_EDITMSG"
-		exit 1 ;;
+		exit 1
+		;;
 	esac
 	${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
 	;;
@@ -235,7 +404,13 @@ if cnt=`grep -v -i '^Signed-off-by' "$GI
 	wc -l` &&
    test 0 -lt $cnt
 then
-	tree=$(git-write-tree) &&
+	if test -z "$TMP_INDEX"
+	then
+		tree=$(git-write-tree)
+	else
+		tree=$(GIT_INDEX_FILE="$TMP_INDEX" git-write-tree) &&
+		rm -f "$TMP_INDEX"
+	fi &&
 	commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
 	git-update-ref HEAD $commit $current &&
 	rm -f -- "$GIT_DIR/MERGE_HEAD"
@@ -250,4 +425,8 @@ if test -x "$GIT_DIR"/hooks/post-commit 
 then
 	"$GIT_DIR"/hooks/post-commit
 fi
+if test 0 -eq "$ret"
+then
+	rm -f "$SAVE_INDEX"
+fi
 exit "$ret"
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
index 8ff5dd9..c8a85f9 100755
--- a/t/t1200-tutorial.sh
+++ b/t/t1200-tutorial.sh
@@ -95,13 +95,13 @@ test_expect_success 'git branch' 'cmp br
 
 git checkout mybranch
 echo "Work, work, work" >>hello
-git commit -m 'Some work.' hello
+git commit -m 'Some work.' -i hello
 
 git checkout master
 
 echo "Play, play, play" >>hello
 echo "Lots of fun" >>example
-git commit -m 'Some fun.' hello example
+git commit -m 'Some fun.' -i hello example
 
 test_expect_failure 'git resolve now fails' 'git resolve HEAD mybranch "Merge work in mybranch"'
 
@@ -112,7 +112,7 @@ Play, play, play
 Work, work, work
 EOF
 
-git commit -m 'Merged "mybranch" changes.' hello
+git commit -m 'Merged "mybranch" changes.' -i hello
 
 cat > show-branch.expect << EOF
 * [master] Merged "mybranch" changes.
-- 
1.1.6.g9e4e
Marco Costalba· Feb 5, 2006, 13:36 UTC · re: Junio C Hamano · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

On 2/5/06, Junio C Hamano <junkio@cox.net> wrote:
Show 8 quoted lines
> I am addressing people who participated with their valuable
> inputs to the earlier "git commit" discussion.
>
> Here is my first cut.  I'd appreciate to hear from both
> git-experienced people and git newcomers how comfortable this
> new implementation is.  Improvement patches on top of this are
> certainly appreciated.
>

I think the new semantics is clear and better then old one, especially for people coming from different scm systems.

Unfortunately it breaks qgit commit functionality. So I would like to ask _how_ do you plan to update main line.

I would like to update qgit also today, but it is impossible because "git-commit -i" _currently_ raises an error. So I am just wondering if adding a temporary -i "no op" option to _current_ git-commit, until semantics change is released could be done.

Adding a -i "place holder" option to _current_ git-commit has the following advantages:

1) No functionality change and 100%  compatibility with current git-commit use.
2) Give people time to update tools/script in the mean time
3) When the real patch will be released all the (modified) tools will
be automatically compatible.
What do you think?
Marco
Junio C Hamano· Feb 5, 2006, 20:03 UTC · re: Marco Costalba · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

I was planning to think about the migration strategy _after_ I get the feeling that everybody who got confused by the current one thinks this is a good change, and I am glad you did that work for me ;-).

I think it is sane to add a no-op '--include' to the current version.

I am not sure if it is worth to take two-phased introduction of this new "temporary index" thing. We could:

 * add '--only' that asks for the new "temporary index" thing.
 * initially make '--include' the default, not '--only'.
 * later switch the default to '--only'.
Marco Costalba· Feb 5, 2006, 22:08 UTC · re: Junio C Hamano · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

Junio C Hamano ha scritto:
Show 18 quoted lines
> I was planning to think about the migration strategy _after_ I
> get the feeling that everybody who got confused by the current
> one thinks this is a good change, and I am glad you did that
> work for me ;-).
> 
> I think it is sane to add a no-op '--include' to the current
> version.
> 
> I am not sure if it is worth to take two-phased introduction of
> this new "temporary index" thing.  We could:
> 
>  * add '--only' that asks for the new "temporary index" thing.
> 
>  * initially make '--include' the default, not '--only'.
> 
>  * later switch the default to '--only'.
> 
> 

I think both are viable choices, with the second one better in case the planned transition phase is going to be long, while the first one is better if it is going to be short.

Because when, at last, you will switch the default to '--only', this option could be removed. So it's a temporary option with, peraphs, a temporary documentation, and a temporary user training to use it: IMHO has a meaning if the transition period is long enough.

If we are talking about just one or two weeks, more then enough to update scripts, I think the first approach is simpler.

In any case to let things more clear could be useful to tag a fresh new git version for this commit semantics change.

Marco
Junio C Hamano· Feb 5, 2006, 23:10 UTC · re: Marco Costalba · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

Marco Costalba <mcostalba@gmail.com> writes:
Show 5 quoted lines
> Because when, at last, you will switch the default to
> '--only', this option could be removed. So it's a temporary
> option with, peraphs, a temporary documentation, and a
> temporary user training to use it: IMHO has a meaning if the
> transition period is long enough.

Hmph, actually I was planning to leave --only in forever if we go that route. Peoples' scripts would need to explicitly ask for --only during the transition if they want that semantics, and removing that support afterwards would mean they would then need to remove --only from their scripts again.

I was hoping that, once people get used to --include and --only, they start to "revel in the index" (as Linus puts it), and realize that defaulting to --only is not such a good idea after all. When that happens, I could leave --include as the default without getting complaints from them ;-).

The net effect, if we end up doing so, would be that we gained an extra flag --only that the user can use to explicitly ask for a partial commit when they want one, without disrupting the established workflow of old timers.

The --only semantics is a useful thing in many situations, while the --include semantics is also a useful thing in many other cases. The latter might be a better match to the way the git internal works, but both are equally useful options that support different workflows. I do not see an inherent reason to declare one over the other to be "the default". So we could instead have no defaults at all (i.e. you have to explicitly say which kind you want if you use paths...).

Nicolas Pitre· Feb 7, 2006, 16:59 UTC · re: Junio C Hamano · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

On Sun, 5 Feb 2006, Junio C Hamano wrote:
Show 5 quoted lines
> I was hoping that, once people get used to --include and --only,
> they start to "revel in the index" (as Linus puts it), and
> realize that defaulting to --only is not such a good idea after
> all.  When that happens, I could leave --include as the
> default without getting complaints from them ;-).

I know this is like fighting old habits, but at some point one as to realize that there was a mistake in the interface and the best way to solve that mistake is to go with the objectively most intuitive solution.

People can get used to the index concept like you did yourself of course, but that is still dismissing the very reason for doing that change in the first place, which is that to a git beginner it is counter intuitive to have side effects when explicit paths are specified _alone_.

> The net effect, if we end up doing so, would be that we gained
> an extra flag --only that the user can use to explicitly ask for
> a partial commit when they want one, without disrupting the
> established workflow of old timers.

It is a bit deconcerting to see that git has already reached the software stone age and that so called "old timers" already have more weight than clean interfaces... even after less than a year.

Please don't make it another CVS and fix those inconsistencies clean and quick while the user base is not yet inapt to change.

Show 6 quoted lines
> The --only semantics is a useful thing in many situations, while
> the --include semantics is also a useful thing in many other
> cases.  The latter might be a better match to the way the git
> internal works, but both are equally useful options that support
> different workflows.  I do not see an inherent reason to declare
> one over the other to be "the default".

What has happened to the "least surprise" principle? That clearly favorize the former IMHO.

> So we could instead
> have no defaults at all (i.e. you have to explicitly say which
> kind you want if you use paths...).
And that would be just a nice usability annoyance.

Maybe being too intimate with git doesn't make it obvious to you, but if you do a survey of the number of tools that allows accepting a list of paths for their only arguments it should be pretty obvious that the vast majority operate only on those explicit paths without any hidden side effect.

If "git commit" is meant to be a user helper script to hide the low level git plumbing then it better do it with the least surprise principle in mind, otherwise it misses its very purpose. And the faster this is corrected the better, especially since this is not such a disturbing change after all.

Nicolas
Junio C Hamano· Feb 7, 2006, 17:50 UTC · re: Nicolas Pitre · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

Nicolas Pitre <nico@cam.org> writes:
> What has happened to the "least surprise" principle?  That clearly 
> favorize the former IMHO.
Please see below.
> Maybe being too intimate with git doesn't make it obvious to you,...

I think you are looking at this with a bit shortsighted view. We are aiming for the same end results.

Let's say you have a git-commit that does --only semantics by default "some day", and everybody knows that is going to happen on that "some day" for a long time (say, a month), because we are discussing about it right now in this thread.

And a newcomer comes. He's never used git before. When he types "git commit paths...", the new --only semantics happens. By that time (a month is a long time in git timescale) everybody knows "git commit paths..." mean --only, so if the newcomer has trouble with "git commit paths...", people can help him with shared knowledge on what that command means. And what the command does is less surprising than the current behaviour.

It is a Good Change. I am agreeing with you that if --only were the default from before I inherited git, things might have been better.

But let's say you make that "some day" today, or in the 1.2.0 that is hopefully imminent. That newcomer is not using git yet. Many people and their scripts are already using git, expecting "git commit paths..." to mean --include semantics. What kind of surprise are you giving them?

That is this '--only/--include are available but currently the traditional --include is the default' is all about. It is about not breaking current users during transition. And People's Scripts. If a tool wants traditional semantics, it can hardcode "--include" (or --only if not), and does not have to worry about the default getting switched "some day".

Once things are prepared that way, we can switch the default any day, as long as we give users enough advance notice.

It is possible that you are well aware of all of the above, and are arguing that "enough advance notice" period should be zero days. But I don't think that is nice. When was this temporary thing formerly proposed in this round of discussion to make everybody realize that is what is happening? 4 days ago? And the actual code that claims to do --only hit the "master" branch only 8 hours or so ago.

I was hoping that before 1.2.0 I can take a final "just in case" quick poll to see everybody agrees making --only the default is the way to go, then state it clearly that 1.3.0 will have the default switched to --only in the release announcement.

Junio C Hamano· Feb 7, 2006, 18:18 UTC · re: Junio C Hamano · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

Junio C Hamano <junkio@cox.net> writes:
> I was hoping that before 1.2.0 I can take a final "just in case"
> quick poll to see everybody agrees making --only the default is
> the way to go, then state it clearly that 1.3.0 will have the
> default switched to --only in the release announcement.
Clarification.  Here is what I want to do.

I've said enough times that we _may_ switch the default from traditional --include semantics to new --only semantics. Nico already even objected to the "may" part, and I am hoping it is shared by everybody on the list that "may" should be "will soon". Unless somebody comes up with a convincing argument otherwise, I'll write "this will switch to --only by the time 1.3.0 release is made, so start converting your scripts to explicitly say --include or --only if you want a specific behaviour, and start training your fingers in the meantime as well" in the 1.2.0 announcement, probably done sometime late this week.

That means 1.2.0 (and its maintenance series 1.2.X) will ship with --include semantics for "git commit paths...", and will allow explicit --only/--include. After 1.2.0, at some point, the "master" branch will start shipping with --only semantics as default. No script should break when 1.3.0 happens.

People who want to use --include semantics will acquire a habit of explicitly askign for --include during 1.3.0 development period. They do not need to unlearn anything when 1.3.0 happens.

People who learned to type --only can unlearn it to reduce typing when 1.3.0 happens, but unlearning is not a requirement. Being explicit should always work.

New people that come after 1.3.0 will get the --only semantics by default, the intuitiveness of which has been argued to death, without using any flags.

Jon Loeliger· Feb 7, 2006, 19:25 UTC · re: Junio C Hamano · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

On Tue, 2006-02-07 at 12:18, Junio C Hamano wrote:
Show 17 quoted lines
> That means 1.2.0 (and its maintenance series 1.2.X) will ship
> with --include semantics for "git commit paths...", and will
> allow explicit --only/--include.  After 1.2.0, at some point,
> the "master" branch will start shipping with --only semantics as
> default.  No script should break when 1.3.0 happens.
> 
> People who want to use --include semantics will acquire a habit
> of explicitly askign for --include during 1.3.0 development
> period.  They do not need to unlearn anything when 1.3.0 happens.
> 
> People who learned to type --only can unlearn it to reduce
> typing when 1.3.0 happens, but unlearning is not a requirement.
> Being explicit should always work.
> 
> New people that come after 1.3.0 will get the --only semantics
> by default, the intuitiveness of which has been argued to death,
> without using any flags.

Hmmm... So maybe just to add fire to the discussion, maybe the problem lies in the "tool name". What if there were two tools named "git-commit-index" and "git-commit-path"? I understand that this is, in some way, identical to the "--include" and "--only" sorts of notions. But I think it might make the intent clearer, and easier to describe/document. I think part of my concern with the options is trying to recall/describe just exactly _what_ is "included" with the "--include", or what is excluded with the "--only". It is the index's data/state, of course. So perhaps a "git commit-index" might be consistent tool naming? And for contrast "git commit-file" or "git commit-path"?

Just a thought.
jdl
Nicolas Pitre· Feb 7, 2006, 18:39 UTC · re: Junio C Hamano · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

On Tue, 7 Feb 2006, Junio C Hamano wrote:
Show 6 quoted lines
> That is this '--only/--include are available but currently the
> traditional --include is the default' is all about.  It is about
> not breaking current users during transition.  And People's
> Scripts.  If a tool wants traditional semantics, it can hardcode
> "--include" (or --only if not), and does not have to worry about
> the default getting switched "some day".

I'm completely with you here. However is there so many tools that relies on the current behavior of "git commit"?

> Once things are prepared that way, we can switch the default any
> day, as long as we give users enough advance notice.

Good. It just seemed to me that you weren't convinced that any default switching was needed. Sorry if I misread you.

What prompted my initial reply to you was this:
|The --only semantics is a useful thing in many situations, while
|the --include semantics is also a useful thing in many other
|cases.  The latter might be a better match to the way the git
|internal works, but both are equally useful options that support
|different workflows.  I do not see an inherent reason to declare
|one over the other to be "the default".  So we could instead
|have no defaults at all (i.e. you have to explicitly say which
|kind you want if you use paths...).
to which I disagreed.
> It is possible that you are well aware of all of the above, and
> are arguing that "enough advance notice" period should be zero
> days.  But I don't think that is nice.

Certainly not zero days. I'm just trying to counter-balance the "without disrupting the established workflow of old timers" argument which is the worst enemy of healthy software evolution. If such an argument was to prevail in the Linux world then we would have had those evil "stable driver ABI" for a long time already. If that argument was to prevail for git then that would be the beginning of its stagnation.

As for the advance notice, it might be a good idea to simply switch the default in 1.3.0 giving any script author relying on the --include behavior (if any) proper insentive to fix them. The 1.3.x branch being the so called "unstable" branch makes it the appropriate place to do it at the earliest, providing script authors enough time to test their modifications on the real thing before it becomes official 1.4.0.

But you should consider all above rambling as part of the feedback you asked for. I don't pretend to always be right.

Nicolas
Nicolas Pitre· Feb 7, 2006, 19:18 UTC · re: Nicolas Pitre · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

On Tue, 7 Feb 2006, Nicolas Pitre wrote:
> The 1.3.x branch being the so called "unstable" branch makes it the 
> appropriate place to do it at the earliest...

As someone refreshed my memory in private, there is no "unstable" branch like Linux used to have. But hopefully you all understood what I meant i.e. in the main branch after the stable 1.2.0 branch is forked.

Nicolas
Junio C Hamano· Feb 8, 2006, 05:41 UTC · re: Nicolas Pitre · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

Nicolas Pitre <nico@cam.org> writes:
> As someone refreshed my memory in private, there is no "unstable" branch 
> like Linux used to have.  But hopefully you all understood what I meant 
> i.e. in the main branch after the stable 1.2.0 branch is forked.

Yes I understood what you meant. Eventually we ship with --only as the default and the best timing is between 1.2.0 and 1.3.0.

I am just worried if it might turn out to be like shipping a bicycle with training wheels welded onto it (so it cannot be easily removed). That is where my reluctance comes from.

But I do not have to be convinced 100% myself in order to set it to the default, as long as the more important users are happy.

Junio C Hamano· Feb 6, 2006, 00:03 UTC · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

Michael Fischer <michael@visv.net> writes:

[Michael, forgive me to respond in public -- which I know is against an established netiquette, but I think this comment is useful to people on the list in general.]

Show 12 quoted lines
> $ git-applymbox ../patch 
> 1 patch(es) to process.
>
> Applying 'git-commit: revamp the git-commit semantics.'
>
> error: Documentation/core-tutorial.txt: does not exist in index
> error: patch failed: git-commit.sh:213
> error: git-commit.sh: patch does not apply
>
> So, kind of hard to give an opinion on it.
>
> I've got 1.1.6 from a couple of days ago, downloaded source.

Ah, sorry, the development happens on the "master" branch. The maintenance release 1.1.6 has only fixes since 1.1.0 was done, and lacks all the new developments on the master branch.

Especially this update involves backward incompatible changes, it will _not_ appear in any of the future 1.1.X releases.

To keep yourself up to date:
	$ git clone git://git.kernel.org/pub/scm/git master.git
        $ cd master.git

This would give you the latest from the master branch that the patch would apply cleanly to.

It is in the the pu (proposed updates) branch, so once you make clone like this, you do not have to apply the patch yourself. Instead, you could find the commit that merges jc/commit topic branch on it:

        $ git show-branch --topo-order master pu
which probably would show something like this:
	...
         - [pu^] Merge branch 'jc/commit'
         + [pu^^2] git-commit: revamp the git-commit semantics.
        ...
The easiest way to try it out is:
	$ git checkout -b test pu^
Note: this would also get other topic branches merged on top of
master, but I consider them to be safe.  The rule of thumb is
that the closer to the tip of the "pu", the less ready the
change is to be of general use.
Then build it as usual.
	$ make && make install
Thanks.
Junio C Hamano· Feb 6, 2006, 01:32 UTC · re: Junio C Hamano · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

Junio C Hamano <junkio@cox.net> writes:
> To keep yourself up to date:
>
> 	$ git clone git://git.kernel.org/pub/scm/git master.git
Silly me.  This URL is wrong.
	$ git clone git://git.kernel.org/pub/scm/git/git master.git
Michael Fischer· Feb 6, 2006, 02:25 UTC · re: Junio C Hamano · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

On Sun, Feb 05, Junio C Hamano wrote:
Show 9 quoted lines
> Junio C Hamano <junkio@cox.net> writes:
> 
> > To keep yourself up to date:
> >
> > 	$ git clone git://git.kernel.org/pub/scm/git master.git
> 
> Silly me.  This URL is wrong.
> 
> 	$ git clone git://git.kernel.org/pub/scm/git/git master.git
Thanks. Instructions followed, patch tried:
>From within a subdirectory:

$ git commit -m"fix the bounce file" bounce.html assuming --include paths... $

Both my nits are solved. Happy. Thanks.
Michael
-- 
Michael Fischer                         Happiness is a config option.
michael@visv.net                        Recompile and be happy. 
Junio C Hamano· Feb 6, 2006, 07:31 UTC · re: Michael Fischer · lore

Re: [PATCH] git-commit: revamp the git-commit semantics.

Michael Fischer <michael@visv.net> writes:
Show 20 quoted lines
> On Sun, Feb 05, Junio C Hamano wrote:
>> Junio C Hamano <junkio@cox.net> writes:
>> 
>> > To keep yourself up to date:
>> >
>> > 	$ git clone git://git.kernel.org/pub/scm/git master.git
>> 
>> Silly me.  This URL is wrong.
>> 
>> 	$ git clone git://git.kernel.org/pub/scm/git/git master.git
>
> Thanks. Instructions followed, patch tried:
>
> From within a subdirectory:
>
> $ git commit -m"fix the bounce file" bounce.html 
> assuming --include paths...
> $
>
> Both my nits are solved. Happy. Thanks.
Thanks for the feedback.

← back to recent threads