threads / discuss / 17796

[DRAFT] Create stash from index state

Subject: [DRAFT] Create stash from index state

## tl;dr

2 messages between Feb 14, 2009 and Feb 14, 2009.

replies: 1people: 2as markdown or json

Adeodato Simó· Feb 14, 2009, 22:39 UTC · lore
Hello,

I've always missed a way to do what `bzr shelve` does: interactive stash, where you just select hunks à-la `add -p`, and then those gets stashed away.

I've recently realized that git-stash doesn't need to know how to prompt for changes, and that just a --from-index flag that would stash the state of the index would be simpler and (at least in my eyes) very git-ish.

Do you think such a flag would be appropriate? I've prepared a preliminary, but I need help with the part that removes the index changes from the working tree (a partial git-reset --hard). I know "checkout stash && checkout -" would do it, but I really don't know how to do that with low-level plumbing (unless git-checkout is actually okay for this.) Any suggestions?

Additionally, I have two behaviors I'd like in stash: the ability to apply/pop on top of a dirty state, perhaps with a -f flag (at the moment I'm just doing `commit -a -m foo && pop && reset HEAD^`), and the ability to --amend the last stash state, as in "oh, add this to the last stash please". Thoughts?

Many thanks in advance,
-- 
Adeodato Simó                                     dato at net.com.org.es
Debian Developer                                  adeodato at debian.org
 
When all is summed up, a man never speaks of himself without loss; his
accusations of himself are always believed; his praises never.
                -- Michel de Montaigne


diff --git a/git-stash.sh b/git-stash.sh
index b9ace99..0b6f5bd 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -67,26 +67,32 @@ create_stash () {
 		git commit-tree $i_tree -p $b_commit) ||
 		die "Cannot save the current index state"
 
-	# state of the working tree
-	w_tree=$( (
-		rm -f "$TMP-index" &&
-		cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
-		GIT_INDEX_FILE="$TMP-index" &&
-		export GIT_INDEX_FILE &&
-		git read-tree -m $i_tree &&
-		git add -u &&
-		git write-tree &&
-		rm -f "$TMP-index"
-	) ) ||
-		die "Cannot save the current worktree state"
-
-	# create the stash
 	if test -z "$stash_msg"
 	then
 		stash_msg=$(printf 'WIP on %s' "$msg")
 	else
 		stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
 	fi
+
+	if test -z "$from_index"
+	then
+		# state of the working tree
+		w_tree=$( (
+			rm -f "$TMP-index" &&
+			cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
+			GIT_INDEX_FILE="$TMP-index" &&
+			export GIT_INDEX_FILE &&
+			git read-tree -m $i_tree &&
+			git add -u &&
+			git write-tree &&
+			rm -f "$TMP-index"
+		) ) ||
+			die "Cannot save the current worktree state"
+	else
+		w_tree="$i_tree"
+	fi
+
+	# create the stash
 	w_commit=$(printf '%s\n' "$stash_msg" |
 		git commit-tree $w_tree -p $b_commit -p $i_commit) ||
 		die "Cannot record working tree state"
@@ -94,10 +100,22 @@ create_stash () {
 
 save_stash () {
 	keep_index=
+	from_index=
 	case "$1" in
 	--keep-index)
 		keep_index=t
 		shift
+		if [ "$1" = "--from-index" ]; then
+			die "--keep-index and --from-index are incompatible"
+		fi
+		;;
+	--from-index)
+		from_index=t
+		shift
+		if [ "$1" = "--keep-index" ]; then
+			die "--from-index and --keep-index are incompatible"
+		fi
+		;;
 	esac
 
 	stash_msg="$*"
@@ -120,7 +138,14 @@ save_stash () {
 		die "Cannot save the current status"
 	printf 'Saved working directory and index state "%s"\n' "$stash_msg"
 
-	git reset --hard
+	if test -z "$from_index"
+	then
+		git reset --hard
+	else
+		# XXX Use plumbing. How?
+		git checkout stash
+		git checkout -
+	fi
 
 	if test -n "$keep_index" && test -n $i_tree
 	then
Johannes Schindelin· Feb 14, 2009, 22:59 UTC · re: Adeodato Simó · lore

Re: [DRAFT] Create stash from index state

Hi,
On Sat, 14 Feb 2009, Adeodato Simó wrote:
> I've always missed a way to do what `bzr shelve` does: interactive
> stash, where you just select hunks à-la `add -p`, and then those gets
> stashed away.
I recently wrote this in my blog:

-- snip -- I think something like an interactive stash is needed. A method to specify what you want to keep in the working directory, the rest should be stashed. The idea would be something like this:

   1. Add the desired changes into a temporary index.
   2. Put the rest of the changes in another temporary index.
   3. Stash the latter index.
   4. Synchronize the working directory with the first index.
   5. Clean up temporary indices. 
Or in code:
$ cp .git/index .git/interactive-stash-1
$ GIT_INDEX_FILE=.git/interactive-stash-1 git add -i
$ cp .git/index .git/interactive-stash-2
$ GIT_INDEX_FILE=.git/interactive-stash-1 git diff -R |
        (GIT_INDEX_FILE=.git/interactive-stash-2 git apply--index)
$ tree=$(GIT_INDEX_FILE=.git/index git write-tree)
$ commit=$(echo Current index | git commit-tree $tree -p HEAD)
$ tree=$(GIT_INDEX_FILE=.git/interactive-stash-2 git write-tree)
$ commit=$(echo Edited out | git commit-tree $tree -p HEAD -p $commit)
$ git update-ref refs/stash $commit
$ GIT_INDEX_FILE=.git/interactive-stash-1 git checkout-index -a -f
$ rm .git/interactive-stash-1 .git/interactive-stash-2

This should probably go into git-stash.sh, maybe even with a switch to start git-gui to do the interactive adding instead of git-add. -- snap --

Now, the main reason I did not implement this already is that I am not quite sure if you should select the changes you _want_ to keep (which would seem the more natural way to say what needs stashing), or if it would be better to select the changes you do _not_ want to keep (this would seem more consistent, as "git add -i" is about selecting what goes into the index, so "git stash -i" should be about selecting what goes into the stash).

Maybe you want to give it a try?

Ciao, Dscho

← back to recent threads