threads / patch / 24783

v5, 8 partsdetached-stash: regularise handling of stash arguments by git stash

Subject: [PATCH v5 0/8] detached-stash: regularise handling of stash arguments by git stash

## tl;dr

4 messages between Aug 18, 2010 and Aug 18, 2010. Diffs are folded; open one to read it.

replies: 3people: 1as markdown or json

Jon Seymour· Aug 18, 2010, 13:09 UTC · lore
This patch introduces a common flags and revision parsing function to the following git stash commands:
 * apply
 * branch
 * pop
 * drop
 * show
With these changes, git stash now:
 * allows non-stash log entry references to passed to 'stash branch' provided they are stash-like
 * relaxes the requirement that a stash log currently entry exists for 'stash show' or 'stash branch' 
 * does not attempt to drop the specified revision if it doesn't look like a stash log entry reference
 * fails 'stash pop' and 'stash drop' early if the specified revision is not a stash log entry reference
 * fails early if more than one stash-like commit is specified
 * fails early if the specified revision is of the form ref@{n} and ref exists, but ref@{n} does not exist
 * reports various error conditions that can occur across multiple commanbds with consistent error messages.

The implementation of several commands is simplified to a lesser or greater degree by taking advantage of the new common parsing and validation function, parse_flags_and_rev().

This revision incorporates feedback and corrections from Johannes Sixt and Junio Hamano.
Jon Seymour (8):
  detached-stash: introduce parse_flags_and_revs function
  detached-stash: simplify stash_apply
  detached-stash: simplify stash_drop
  detached-stash: refactor git stash pop implementation
  detached-stash: simplify git stash branch
  detached-stash: simplify git stash show
  detached-stash: tests of git stash with stash-like arguments
  detached-stash: update Documentation
 Documentation/git-stash.txt |   16 ++-
 git-stash.sh                |  223 ++++++++++++++++++++++++++++---------------
 t/t3903-stash.sh            |  140 +++++++++++++++++++++++++++
 3 files changed, 296 insertions(+), 83 deletions(-)
-- 
1.7.2.1.95.g969a4.dirty
Jon Seymour· Aug 18, 2010, 13:09 UTC · re: Jon Seymour · lore

[PATCH v5 1/8] detached-stash: introduce parse_flags_and_revs function

Introduce parse_flags_and_revs. This function requires that there is at at most one stash-like revision parameter and sero or more flags.

It knows how to parse -q,--quiet and --index flags, but leaves other flags unparsed.

Specified revisions are checked to see that they are at least stash-like (meaning: they look like something created by git stash save or git stash create).

If this is so, then IS_STASH_LIKE is initialized to a non-empty value.

If the specified revision also looks like a stash log entry reference, then IS_STASH_REF is initialized to a non-empty value.

References of the form ref@{spec} are required to precisely identify an individual commit.

If no reference is specified, stash@{0} is assumed.

Once the specified reference is validated to be at least stash_like a ensemble of derived variables, (w_commit, w_tree, b_commit, etc) are initialized with a single call to git rev-parse.

Repeated calls to parse_flags_and_rev() avoid repeated calls to git rev-parse if the specified arguments have already been parsed.

An ensemble of supporting functions that make use of the state established by parse_flags_and_rev(). These are describe below. Subsequent patches in the series modify the existing git stash subcommands to make use of these functions as appropriate.

The ancillary functions are:

is_stash_like(): which can be used to test whether a specified commit looks like a commit created with git stash save or git stash create.

assert_stash_like(): which can be used by commands that misbehave unless their arguments stash-like.

is_stash_ref(): which checks whether an argument is valid stash reference(e.g. is of the form ['refs/']stash['@{'something'}])

assert_stash_ref(): which can be used by commands that misbehave unless their arguments are both stash-like and refer to valid stash entries.

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 git-stash.sh |  133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 133 insertions(+), 0 deletions(-)
Show changes to git-stash.sh +133 −0
diff --git a/git-stash.sh b/git-stash.sh
index 1d95447..42b0da2 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -225,6 +225,138 @@ show_stash () {
 	git diff $flags $b_commit $w_commit
 }
 
+#
+# Parses the remaining options looking for flags and
+# at most one revision defaulting to ${ref_stash}@{0}
+# if none found.
+#
+# Derives related tree and commit objects from the
+# revision, if one is found.
+#
+# stash records the work tree, and is a merge between the
+# base commit (first parent) and the index tree (second parent).
+#
+#   REV is set to the symbolic version of the specified stash-like commit
+#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash
+#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
+#   s is set to the SHA1 of the stash commit
+#   w_commit is set to the commit containing the working tree
+#   b_commit is set to the base commit
+#   i_commit is set to the commit containing the index tree
+#   w_tree is set to the working tree
+#   b_tree is set to the base tree
+#   i_tree is set to the index tree
+#
+#   GIT_QUIET is set to t if -q is specified
+#   INDEX_OPTION is set to --index if --index is specified.
+#   FLAGS is set to the remaining flags
+#
+# dies if:
+#   * too many revisions specified
+#   * no revision is specified and there is no stash stack
+#   * a revision is specified which cannot be resolve to a SHA1
+#   * a non-existent stash reference is specified
+#
+
+parse_flags_and_rev()
+{
+	test "$PARSE_CACHE" = "$*" && return 0 # optimisation
+	PARSE_CACHE="$*"
+
+	IS_STASH_LIKE=
+	IS_STASH_REF=
+	INDEX_OPTION=
+	s=
+	w_commit=
+	b_commit=
+	i_commit=
+	w_tree=
+	b_tree=
+	i_tree=
+
+	REV=$(git rev-parse --no-flags --symbolic "$@" 2>/dev/null)
+	FLAGS=$(git rev-parse --no-revs -- "$@" 2>/dev/null)
+
+	set -- $FLAGS
+
+	FLAGS=
+	while test $# -ne 0
+	do
+		case "$1" in
+			-q|--quiet)
+				GIT_QUIET=-t
+			;;
+			--index)
+				INDEX_OPTION=--index
+			;;
+			--)
+				:
+			;;
+			*)
+				FLAGS="${FLAGS}${FLAGS:+ }$1"
+			;;
+		esac
+		shift
+	done
+
+	set -- $REV
+
+	case $# in
+		0)
+			have_stash || die "No stash found."
+			set -- ${ref_stash}@{0}
+		;;
+		1)
+			:
+		;;
+		*)
+			die "Too many revisions specified: $REV"
+		;;
+	esac
+
+	REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || die "$1 is not valid reference"
+
+	i_commit=$(git rev-parse --quiet --verify $REV^2 2>/dev/null) &&
+	set -- $(git rev-parse $REV $REV^1 $REV: $REV^1: $REV^2: 2>/dev/null) &&
+	s=$1 &&
+	w_commit=$1 &&
+	b_commit=$2 &&
+	w_tree=$3 &&
+	b_tree=$4 &&
+	i_tree=$5 &&
+	IS_STASH_LIKE=t &&
+	test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
+	IS_STASH_REF=t
+
+	if test "${REV}" != "${REV%{*\}}"
+	then
+		# maintainers: it would be better if git rev-parse indicated
+		# this condition with a non-zero status code but as of 1.7.2.1 it
+		# it did not. So, we use non-empty stderr output as a proxy for the
+		# condition of interest.
+		test -z "$(git rev-parse "$REV" 2>&1 >/dev/null)" || die "$REV does not exist in the stash log"
+	fi
+
+}
+
+is_stash_like()
+{
+	parse_flags_and_rev "$@"
+	test -n "$IS_STASH_LIKE"
+}
+
+assert_stash_like() {
+	is_stash_like "$@" || die "'$*' is not a stash-like commit"
+}
+
+is_stash_ref() {
+	is_stash_like "$@" && test -n "$IS_STASH_REF"
+}
+
+assert_stash_ref() {
+	is_stash_ref "$@" || die "'$*' is not a stash reference"
+}
+
 apply_stash () {
 	applied_stash=
 	unstash_index=
@@ -375,6 +507,7 @@ apply_to_branch () {
 	drop_stash $stash
 }
 
+PARSE_CACHE='--not-parsed'
 # The default command is "save" if nothing but options are given
 seen_non_option=
 for opt
-- 
1.7.2.1.95.g4fabf
Jon Seymour· Aug 18, 2010, 13:09 UTC · re: Jon Seymour · lore

[PATCH v5 2/8] detached-stash: simplify stash_apply

The implementation of stash_apply() is simplified to take advantage of the common parsing function parse_flags_and_rev().

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 git-stash.sh |   38 +++-----------------------------------
 1 files changed, 3 insertions(+), 35 deletions(-)
Show changes to git-stash.sh +3 −35
diff --git a/git-stash.sh b/git-stash.sh
index 42b0da2..ba68f1e 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -358,40 +358,8 @@ assert_stash_ref() {
 }
 
 apply_stash () {
-	applied_stash=
-	unstash_index=
 
-	while test $# != 0
-	do
-		case "$1" in
-		--index)
-			unstash_index=t
-			;;
-		-q|--quiet)
-			GIT_QUIET=t
-			;;
-		*)
-			break
-			;;
-		esac
-		shift
-	done
-
-	if test $# = 0
-	then
-		have_stash || die 'Nothing to apply'
-		applied_stash="$ref_stash@{0}"
-	else
-		applied_stash="$*"
-	fi
-
-	# stash records the work tree, and is a merge between the
-	# base commit (first parent) and the index tree (second parent).
-	s=$(git rev-parse --quiet --verify --default $ref_stash "$@") &&
-	w_tree=$(git rev-parse --quiet --verify "$s:") &&
-	b_tree=$(git rev-parse --quiet --verify "$s^1:") &&
-	i_tree=$(git rev-parse --quiet --verify "$s^2:") ||
-		die "$*: no valid stashed state found"
+	assert_stash_like "$@"
 
 	git update-index -q --refresh &&
 	git diff-files --quiet --ignore-submodules ||
@@ -402,7 +370,7 @@ apply_stash () {
 		die 'Cannot apply a stash in the middle of a merge'
 
 	unstashed_index_tree=
-	if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
+	if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
 			test "$c_tree" != "$i_tree"
 	then
 		git diff-tree --binary $s^2^..$s^2 | git apply --cached
@@ -447,7 +415,7 @@ apply_stash () {
 	else
 		# Merge conflict; keep the exit status from merge-recursive
 		status=$?
-		if test -n "$unstash_index"
+		if test -n "$INDEX_OPTION"
 		then
 			echo >&2 'Index was not unstashed.'
 		fi
-- 
1.7.2.1.95.g4fabf
Jon Seymour· Aug 18, 2010, 13:09 UTC · re: Jon Seymour · lore

[PATCH v5 3/8] detached-stash: simplify stash_drop

Previously, git stash drop would fail noisily while executing git reflog delete if the specified revision was not a stash reference.

Now, git stash drop fails with an error message which more precisely indicates the reason for failure.

Furthermore, git stash drop will now fail with a non-zero status code if stash@{n} specifies a stash log entry that does not actually exist.

This change in behaviour is achieved by delegating argument parsing to the common parse_flags_and_rev() function (via a call to assert_stash_ref).

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 git-stash.sh |   31 +++----------------------------
 1 files changed, 3 insertions(+), 28 deletions(-)
Show changes to git-stash.sh +3 −28
diff --git a/git-stash.sh b/git-stash.sh
index ba68f1e..750f360 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -424,35 +424,10 @@ apply_stash () {
 }
 
 drop_stash () {
-	have_stash || die 'No stash entries to drop'
+	assert_stash_ref "$@"
 
-	while test $# != 0
-	do
-		case "$1" in
-		-q|--quiet)
-			GIT_QUIET=t
-			;;
-		*)
-			break
-			;;
-		esac
-		shift
-	done
-
-	if test $# = 0
-	then
-		set x "$ref_stash@{0}"
-		shift
-	fi
-	# Verify supplied argument looks like a stash entry
-	s=$(git rev-parse --verify "$@") &&
-	git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
-	git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
-	git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
-		die "$*: not a valid stashed state"
-
-	git reflog delete --updateref --rewrite "$@" &&
-		say "Dropped $* ($s)" || die "$*: Could not drop stash entry"
+	git reflog delete --updateref --rewrite "${REV}" &&
+		say "Dropped ${REV} ($s)" || die "${REV}: Could not drop stash entry"
 
 	# clear_stash if we just dropped the last stash entry
 	git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
-- 
1.7.2.1.95.g4fabf

← back to recent threads