threads / patch / 3958

patchAdd git-stash to stash the working tree to a new tagged name

Subject: [PATCH] Add git-stash to stash the working tree to a new tagged name

## tl;dr

3 messages between Apr 24, 2006 and Apr 24, 2006. Diffs are folded; open one to read it.

replies: 2people: 2as markdown or json

Carl Worth· Apr 24, 2006, 19:37 UTC · lore

This is a really simple script which is convenient for stashing changes in the current working tree:

	git stash <tagname>
and recovering them later with something like:
	git cherry-pick -n <tagname>
	git tag -d <tagname>	# optional cleanup
---

This could also be implemented with a branch rather than a tag, (which would require simply removing the "git-tag" and "git-branch -D" lines from the implementation. But I preferred to emphasize that a stashed commit is not a natural basis for more development.

Less than seriously proposing this for inclusion as is, I'm trying to start some discussion around a workflow issue I keep running into. The git-reset documentation describes an "interrupted workflow" case and suggests something like the stash and recover operations I describe above but with the following usage:

Stashing (on branch 'feature'):
	git commit -a -m 'snapshot WIP'
Recovering:
	git checkout feature
	git reset --soft HEAD^
	git reset

My git-stash approach isn't really a lot less work, (in fact, it requires coming up with a new temporary name which makes it less desirable), but it's at least easier for me to remember how to do it. For me at least, it seems I'm often having to re-consult the git-reset documentation to decide which variant I need to use in any given situation---and I still find myself making frustrating mistakes with git-reset every once in a while.

I think an improved interface for interrupted workflow would look more like this:

Stashing (on branch 'feature'):
	git stash
Recovering:
	git checkout feature

That would be quite pleasant. And I may write my own git-checkout wrapper to do this based on some convention for tag naming (such as <branchname>-stash). A cleaner implementation might involve some per-branch metadata (as has been recently proposed) for storing the stashed information rather than a naming convention. [At that point, I'm suggesting ideas beyond my intent to code things up.]

And if all that were in place, maybe it would even make sense to do automatic stashing by default whenever switching away from a branch with a "dirty" working tree, (and without the -m option asking to merge those changes into the destination). Currently, git-checkout simply errors out in this situation, so there's room to add new functionality there. That would give the most ideal interface of all, which is simply:

Stashing (on branch 'somewhere'):
	git checkout elsewhere
Recovering:
	git checkout somewhere

One thing that is missing from all the schemes discussed so far is that they are lossy with respect to differences that originally exist between the working tree and the index. If an automatically stashing scheme were implemented via per-branch metadata, then it seems it would be feasible to stash the working tree and the index separately into the branch's metadata.

That would be quite convenient for me, since conceptually, the working tree and the index are just as much a part of my "current branch state" as the parent commit is.

I'd be interested in any feedback or other ideas.
-Carl
 .gitignore   |    1 +
 Makefile     |    2 +-
 git-stash.sh |   24 ++++++++++++++++++++++++
 3 files changed, 26 insertions(+), 1 deletions(-)
 create mode 100755 git-stash.sh
686ceaf3444d70914c251ad857fc424c9334141c
Show changes to 3 files +26 −1

.gitignore, Makefile, git-stash.sh

diff --git a/.gitignore b/.gitignore
index b5959d6..16c3149 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,6 +103,7 @@ git-ssh-fetch
 git-ssh-pull
 git-ssh-push
 git-ssh-upload
+git-stash
 git-status
 git-stripspace
 git-svnimport
diff --git a/Makefile b/Makefile
index 8aed3af..2a9906e 100644
--- a/Makefile
+++ b/Makefile
@@ -125,7 +125,7 @@ SCRIPT_SH = \
 	git-applymbox.sh git-applypatch.sh git-am.sh \
 	git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
 	git-merge-resolve.sh git-merge-ours.sh git-grep.sh \
-	git-lost-found.sh
+	git-lost-found.sh git-stash.sh
 
 SCRIPT_PERL = \
 	git-archimport.perl git-cvsimport.perl git-relink.perl \
diff --git a/git-stash.sh b/git-stash.sh
new file mode 100755
index 0000000..988ca51
--- /dev/null
+++ b/git-stash.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Carl D. Worth
+
+USAGE='<tagname>'
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+
+if test "$#" -ne 1
+then
+	usage
+fi
+
+tagname="$1"
+
+headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||')
+
+# We use tagname for the temporary branch name as well.
+git-checkout -b "$tagname" || exit
+git commit -a -m "Stash commit of $headref to $tagname"
+git tag "$tagname"
+git-checkout "$headref"
+git-branch -D "$tagname"
+
-- 
1.3.0.g85e6-dirty
Junio C Hamano· Apr 24, 2006, 20:54 UTC · re: Carl Worth · lore

Re: [PATCH] Add git-stash to stash the working tree to a new tagged name

Carl Worth <cworth@cworth.org> writes:
Show 9 quoted lines
> Stashing (on branch 'feature'):
>
> 	git commit -a -m 'snapshot WIP'
>
> Recovering:
>
> 	git checkout feature
> 	git reset --soft HEAD^
> 	git reset
If I were doing this today, I would probably do this:
	git commit -a -m 'WIP'
        git checkout elsewhere ;# interrupted
        ... hack hack hack ...
        git checkout feature ;# come back
        ... hack hack hack ...
        git commit --amend

But I wonder why the originally suggested sequence is reset soft to the state we want and then another reset. Without experimenting myself or thinking hard about it, I would expect "git reset HEAD^" should do what we want, in which case:

Stashing (on branch 'feature'):
	git commit -a -m 'snapshot WIP'
Recovering:
	git checkout feature
	git reset HEAD^
Carl Worth· Apr 24, 2006, 20:58 UTC · re: Junio C Hamano · lore

Re: [PATCH] Add git-stash to stash the working tree to a new tagged name

On Mon, 24 Apr 2006 13:54:39 -0700, Junio C Hamano wrote:
> If I were doing this today, I would probably do this:
...
>         git commit --amend
Oh, that's fantastic.

I hadn't picked up on this feature before, and it looks quite useful. Thanks for pointing that out.

-Carl

← back to recent threads