threads / patch / 8805

patchTeach git-stash to "apply --index"

Subject: [PATCH] Teach git-stash to "apply --index"

## tl;dr

3 messages between Jul 2, 2007 and Jul 3, 2007. Diffs are folded; open one to read it.

replies: 2people: 2as markdown or json

Johannes Schindelin· Jul 2, 2007, 11:14 UTC · lore

When given this subcommand, git-stash will try to merge the stashed index into the current one. Only trivial merges are possible, since we have no index for the index ;-) If a trivial merge is not possible, git-stash will bail out with a hint to skip the --index option.

For good measure, finally include a test case.
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
	I am not quite sure if this should not be the default, with
	--skip-index to turn it off if the trivial index merge fails,
	and the user might be interested only in the working directory
	changes anyway.
	Comments?
 git-stash.sh     |   21 ++++++++++++++++
 t/t3903-stash.sh |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+), 1 deletions(-)
Show changes to 2 files +88 −1

git-stash.sh, t/t3903-stash.sh

diff --git a/git-stash.sh b/git-stash.sh
index 04ce30a..45ad2f4 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -98,6 +98,13 @@ apply_stash () {
 	git-diff-files --quiet ||
 		die 'Cannot restore on top of a dirty state'
 
+	unstash_index=
+	case "$1" in
+	--index)
+		unstash_index=t
+		shift
+	esac
+
 	# current index state
 	c_tree=$(git-write-tree) ||
 		die 'Cannot apply a stash in the middle of a merge'
@@ -107,6 +114,15 @@ apply_stash () {
 	b_tree=$(git-rev-parse --verify "$s^:") ||
 		die "$*: no valid stashed state found"
 
+	test -z "$unstash_index" || {
+		git diff --binary $s^2^..$s^2 | git apply --cached
+		test $? -ne 0 &&
+			die 'Conflicts in index. Try without --index.'
+		unstashed_index_tree=$(git-write-tree) ||
+			die 'Could not save index tree'
+		git reset
+	}
+
 	eval "
 		GITHEAD_$w_tree='Stashed changes' &&
 		GITHEAD_$c_tree='Updated upstream' &&
@@ -124,9 +140,12 @@ apply_stash () {
 			die "Cannot unstage modified files"
 		git-status
 		rm -f "$a"
+		test -z "$unstash_index" || git read-tree $unstashed_index_tree
 	else
 		# Merge conflict; keep the exit status from merge-recursive
-		exit
+		status=$?
+		test -z "$unstash_index" || echo 'Index was not unstashed.' >&2
+		exit $status
 	fi
 }
 
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
new file mode 100755
index 0000000..392ac1c
--- /dev/null
+++ b/t/t3903-stash.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E Schindelin
+#
+
+test_description='Test git-stash'
+
+. ./test-lib.sh
+
+test_expect_success 'stash some dirty working directory' '
+	echo 1 > file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	echo 2 > file &&
+	git add file &&
+	echo 3 > file &&
+	test_tick &&
+	git stash &&
+	git diff-files --quiet &&
+	git diff-index --cached --quiet HEAD
+'
+
+cat > expect << EOF
+diff --git a/file b/file
+index 0cfbf08..00750ed 100644
+--- a/file
++++ b/file
+@@ -1 +1 @@
+-2
++3
+EOF
+
+test_expect_success 'parents of stash' '
+	test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
+	git diff stash^2..stash > output &&
+	diff -u output expect
+'
+
+test_expect_success 'apply needs clean working directory' '
+	echo 4 > other-file &&
+	git add other-file &&
+	echo 5 > other-file
+	! git stash apply
+'
+
+test_expect_success 'apply stashed changes' '
+	git add other-file &&
+	test_tick &&
+	git commit -m other-file &&
+	git stash apply &&
+	test 3 = $(cat file) &&
+	test 1 = $(git show :file) &&
+	test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'apply stashed changes (including index)' '
+	git reset --hard HEAD^ &&
+	echo 6 > other-file &&
+	git add other-file &&
+	test_tick &&
+	git commit -m other-file &&
+	git stash apply --index &&
+	test 3 = $(cat file) &&
+	test 2 = $(git show :file) &&
+	test 1 = $(git show HEAD:file)
+'
+
+test_done
Junio C Hamano· Jul 3, 2007, 04:33 UTC · re: Johannes Schindelin · lore

Re: [PATCH] Teach git-stash to "apply --index"

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 6 quoted lines
> 	I am not quite sure if this should not be the default, with
> 	--skip-index to turn it off if the trivial index merge fails,
> 	and the user might be interested only in the working directory
> 	changes anyway.
>
> 	Comments?

There is a bit of impedance mismatch between a rename-aware three-way merge (aka merge-recursive) used to update the working tree and a patch that updates the index. The "rename-aware" thing can be fixed by doing the diff with -M, though.

It might be easier to explain (1) not have --index option, but attempt to do this always, and (2) even if the index cannot be updated, keep going, without worrying about losing the difference between I and W, probably with a message to the end user.

Suppose you are the user who gets "perhaps you would want to run without --index?" hint. What can you do? Run without --index, or forget about unstashing. But the latter does not sound like an option. So it feels to me that --index is an unnecessary option.

The potential confusion problem I mentioned in two of my earlier messages due to the program doing one thing sometimes and another thing some other times still applies, though.

Johannes Schindelin· Jul 3, 2007, 11:17 UTC · re: Junio C Hamano · lore

Re: [PATCH] Teach git-stash to "apply --index"

Hi,
On Mon, 2 Jul 2007, Junio C Hamano wrote:
Show 13 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > 	I am not quite sure if this should not be the default, with
> > 	--skip-index to turn it off if the trivial index merge fails,
> > 	and the user might be interested only in the working directory
> > 	changes anyway.
> >
> > 	Comments?
> 
> There is a bit of impedance mismatch between a rename-aware
> three-way merge (aka merge-recursive) used to update the working
> tree and a patch that updates the index.  The "rename-aware"
> thing can be fixed by doing the diff with -M, though.

Ah yes, I did not think about renames. But like you said, adding -M to the diff should help that.

> It might be easier to explain (1) not have --index option, but attempt 
> to do this always, and (2) even if the index cannot be updated, keep 
> going, without worrying about losing the difference between I and W, 
> probably with a message to the end user.

Okay, in this case I propose to make this the default, with an option "--skip-index" to only try re-apply the changes to the working directory.

> Suppose you are the user who gets "perhaps you would want to run without 
> --index?" hint.  What can you do?  Run without --index, or forget about 
> unstashing.  But the latter does not sound like an option.  So it feels 
> to me that --index is an unnecessary option.

It is very much like the choice you have with "git checkout" vs "git checkout -f". Either you change your index accordingly, or you say "I don't care about the index. What changes I had there, I will reconstruct myself."

> The potential confusion problem I mentioned in two of my earlier 
> messages due to the program doing one thing sometimes and another thing 
> some other times still applies, though.

Yes, that's why I would suggest not going ahead when the index could not be easily merged. Rather give the user enough information to decide.

Of course, this means that in complicated cases, the user mustn't run and hide when she hears "index!", but I guess that complicated cases always need you to know your tools better than easy problems.

Ciao, Dscho

← back to recent threads