git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH (3rd try)] Add git-stash script
  2007-06-30  2:05 [PATCH (2nd " Johannes Schindelin
@ 2007-06-30  5:37 ` しらいしななこ
  2007-06-30  6:12   ` Jeff King
  2007-06-30 15:41   ` Johannes Schindelin
  0 siblings, 2 replies; 7+ messages in thread
From: しらいしななこ @ 2007-06-30  5:37 UTC (permalink / raw)
  To: GIT; +Cc: Johannes Schindelin, Junio C Hamano

When my boss has something to show me and I have to update, for some
reason I am always in the middle of doing something else, and git pull
command refuses to work in such a case.

I wrote this little script to save the changes I made, perform the
update, and then come back to where I was, but on top of the updated
commit.

This is how you would use the script:

  $ git stash
  $ git pull
  $ git stash apply

Signed-off-by: Nanako Shiraishi <nanako3@bluebottle.com>
---

Thank you for the hint for labeling the conflict blocks.
I also added an entry to gitignore and Makefile as requested.

 .gitignore   |    1 +
 Makefile     |    3 +-
 git-stash.sh |  160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 163 insertions(+), 1 deletions(-)
 create mode 100755 git-stash.sh

diff --git a/.gitignore b/.gitignore
index e8b060c..02d9b04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,6 +123,7 @@ git-ssh-fetch
 git-ssh-pull
 git-ssh-push
 git-ssh-upload
+git-stash
 git-status
 git-stripspace
 git-submodule
diff --git a/Makefile b/Makefile
index a98e27a..05b1fc0 100644
--- a/Makefile
+++ b/Makefile
@@ -212,7 +212,8 @@ SCRIPT_SH = \
 	git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
 	git-merge-resolve.sh git-merge-ours.sh \
 	git-lost-found.sh git-quiltimport.sh git-submodule.sh \
-	git-filter-branch.sh
+	git-filter-branch.sh \
+	git-stash.sh
 
 SCRIPT_PERL = \
 	git-add--interactive.perl \
diff --git a/git-stash.sh b/git-stash.sh
new file mode 100755
index 0000000..05ca3a6
--- /dev/null
+++ b/git-stash.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+# Copyright (c) 2007, Nanako Shiraishi
+
+USAGE='[ | list | show | apply | clear]'
+
+. git-sh-setup
+require_work_tree
+
+TMP="$GIT_DIR/.git-stash.$$"
+trap 'rm -f "$TMP-*"' 0
+
+ref_stash=refs/stash
+
+no_changes () {
+	git-diff-index --quiet --cached HEAD &&
+	git-diff-files --quiet
+}
+
+clear_stash () {
+	logfile="$GIT_DIR/logs/$ref_stash" &&
+	mkdir -p "$(dirname "$logfile")" &&
+	: >"$logfile"
+}
+
+save_stash () {
+	if no_changes
+	then
+		echo >&2 'No local changes to save'
+		exit 0
+	fi
+	test -f "$GIT_DIR/logs/refs/stash" ||
+		clear_stash || die "Cannot initialize stash"
+
+	# state of the base commit
+	if b_commit=$(git-rev-parse --verify HEAD)
+	then
+		head=$(git-log --abbrev-commit --pretty=oneline -n 1 HEAD)
+	else
+		die "You do not have the initial commit yet"
+	fi
+
+	if branch=$(git-symbolic-ref -q HEAD)
+	then
+		branch=${branch#refs/heads/}
+	else
+		branch='(no branch)'
+	fi
+	msg=$(printf '%s: %s' "$branch" "$head")
+
+	# state of the index
+	i_tree=$(git-write-tree) &&
+	i_commit=$(printf 'index on %s' "$msg" |
+		git-commit-tree $i_tree -p $b_commit) ||
+		die "Cannot save the current index state"
+
+	# state of the working tree
+	w_tree=$( (
+		GIT_INDEX_FILE="$TMP-index" &&
+		export GIT_INDEX_FILE &&
+
+		rm -f "$TMP-index" &&
+		git-read-tree $i_tree &&
+		git-add -u &&
+		git-write-tree &&
+		rm -f "$TMP-index"
+	) ) ||
+		die "Cannot save the current worktree state"
+
+	# create the stash
+	w_commit=$(printf 'WIP on %s' "$msg" |
+		git-commit-tree $w_tree -p $b_commit -p $i_commit) ||
+		die "Cannot record working tree state"
+
+	git-update-ref -m "$msg" $ref_stash $w_commit ||
+		die "Cannot save the current status"
+	printf >&2 'Saved WIP on %s\n' "$msg"
+}
+
+list_stash () {
+	git-log --pretty=oneline -g "$@" $ref_stash |
+	sed -n -e 's/^[.0-9a-f]* refs\///p'
+}
+
+show_stash () {
+	flags=$(git-rev-parse --no-revs --flags "$@")
+	if test -z "$flags"
+	then
+		flags=--stat
+	fi
+	s=$(git-rev-parse --revs-only --no-flags --default $ref_stash "$@")
+
+	w_commit=$(git-rev-parse --verify "$s") &&
+	b_commit=$(git-rev-parse --verify "$s^") &&
+	git-diff $flags $b_commit $w_commit
+}
+
+apply_stash () {
+	git-diff-files --quiet ||
+		die 'Cannot restore on top of a dirty state'
+
+	# current index state
+	c_tree=$(git-write-tree) ||
+		die 'Cannot apply a stash in the middle of a merge'
+
+	s=$(git-rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
+	w_tree=$(git-rev-parse --verify "$s:") &&
+	b_tree=$(git-rev-parse --verify "$s^:") ||
+		die "$*: no valid stashed state found"
+
+	eval "
+		GITHEAD_$w_tree='Stashed changes' &&
+		GITHEAD_$c_tree='Updated upstream' &&
+		GITHEAD_$b_tree='Version stash was based on' &&
+		export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
+	"
+
+	if git-merge-recursive $b_tree -- $c_tree $w_tree
+	then
+		# No conflict
+		a="$TMP-added" &&
+		git-diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
+		git-read-tree --reset $c_tree &&
+		git-update-index --add --stdin <"$a" ||
+			die "Cannot unstage modified files"
+		git-status
+		rm -f "$a"
+	else
+		# Merge conflict
+		exit 1
+	fi
+}
+
+# Main command set
+case "$1" in
+list)
+	shift
+	if test $# = 0
+	then
+		set x -n 10
+		shift
+	fi
+	list_stash "$@"
+	;;
+show)
+	shift
+	show_stash "$@"
+	;;
+apply)
+	shift
+	apply_stash "$@"
+	;;
+clear)
+	clear_stash
+	;;
+'')
+	save_stash && git-reset --hard
+	;;
+*)
+	usage
+esac
-- 
1.5.2

----------------------------------------------------------------------
Free pop3 email with a spam filter.
http://www.bluebottle.com

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH (3rd try)] Add git-stash script
  2007-06-30  5:37 ` [PATCH (3rd " しらいしななこ
@ 2007-06-30  6:12   ` Jeff King
  2007-06-30  6:25     ` Junio C Hamano
  2007-06-30 15:41   ` Johannes Schindelin
  1 sibling, 1 reply; 7+ messages in thread
From: Jeff King @ 2007-06-30  6:12 UTC (permalink / raw)
  To: しらいしななこ
  Cc: GIT, Johannes Schindelin, Junio C Hamano

On Sat, Jun 30, 2007 at 02:37:09PM +0900, しらいしななこ wrote:

> +ref_stash=refs/stash
[...]
> +save_stash () {
> +	if no_changes
> +	then
> +		echo >&2 'No local changes to save'
> +		exit 0
> +	fi
> +	test -f "$GIT_DIR/logs/refs/stash" ||
> +		clear_stash || die "Cannot initialize stash"

Nit: this should be .../logs/$ref_stash

-Peff

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH (3rd try)] Add git-stash script
  2007-06-30  6:12   ` Jeff King
@ 2007-06-30  6:25     ` Junio C Hamano
  0 siblings, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2007-06-30  6:25 UTC (permalink / raw)
  To: Jeff King
  Cc: しらいしななこ, GIT,
	Johannes Schindelin

Jeff King <peff@peff.net> writes:

> On Sat, Jun 30, 2007 at 02:37:09PM +0900, しらいしななこ wrote:
>
>> +ref_stash=refs/stash
> [...]
>> +save_stash () {
>> +	if no_changes
>> +	then
>> +		echo >&2 'No local changes to save'
>> +		exit 0
>> +	fi
>> +	test -f "$GIT_DIR/logs/refs/stash" ||
>> +		clear_stash || die "Cannot initialize stash"
>
> Nit: this should be .../logs/$ref_stash

Sharp eyes.

I'll take a look and possibly comment on it later, but from a
cursory look I do not see major problems, so if I decide to
apply it I'll try to remember fixing this up when I do so...

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH (3rd try)] Add git-stash script
  2007-06-30  5:37 ` [PATCH (3rd " しらいしななこ
  2007-06-30  6:12   ` Jeff King
@ 2007-06-30 15:41   ` Johannes Schindelin
  2007-06-30 17:19     ` Junio C Hamano
  2007-06-30 23:27     ` しらいしななこ
  1 sibling, 2 replies; 7+ messages in thread
From: Johannes Schindelin @ 2007-06-30 15:41 UTC (permalink / raw)
  To: しらいしななこ
  Cc: GIT, Junio C Hamano

[-- Attachment #1: Type: TEXT/PLAIN, Size: 1104 bytes --]

Hi,

On Sat, 30 Jun 2007, しらいしななこ wrote:

> diff --git a/git-stash.sh b/git-stash.sh
> [...]
> +	printf >&2 'Saved WIP on %s\n' "$msg"

You have an awful lot of printfs in the code. Why not just use echos?

> +list_stash () {
> +	git-log --pretty=oneline -g "$@" $ref_stash |

Wouldn't you want "--default $ref_stash" here?

> +apply_stash () {
> +	git-diff-files --quiet ||
> +		die 'Cannot restore on top of a dirty state'

You meant "no_changes", right? I think you miss changes in the index 
otherwise.

> +		git-diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
> +		git-read-tree --reset $c_tree &&
> +		git-update-index --add --stdin <"$a" ||
> +			die "Cannot unstage modified files"

Isn't there a way to avoid the temporary file here?

> +	else
> +		# Merge conflict
> +		exit 1

Since $? is already != 0, and it might tell the savvy user what kind of 
error merge-recursive returned, why not use "exit", which is equivalent to 
"exit $?"?

> +		set x -n 10
> +		shift

This is more elegantly written as "set -- -n 10", or in our context even 
"set -- -10".

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH (3rd try)] Add git-stash script
  2007-06-30 15:41   ` Johannes Schindelin
@ 2007-06-30 17:19     ` Junio C Hamano
  2007-06-30 23:27     ` しらいしななこ
  1 sibling, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2007-06-30 17:19 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: しらいしななこ, GIT

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> diff --git a/git-stash.sh b/git-stash.sh
>> [...]
>> +	printf >&2 'Saved WIP on %s\n' "$msg"
>
> You have an awful lot of printfs in the code. Why not just use echos?

I had the same impression, but I would say it is Ok (actually
printf is even better).  Judging from our recent changes
(e.g. a23bfae) I think it is prudent to avoid echo when printing
user supplied messages.  Not that corrupted backslash sequence
matters that much on stderr, though ;-).

>> +list_stash () {
>> +	git-log --pretty=oneline -g "$@" $ref_stash |
>
> Wouldn't you want "--default $ref_stash" here?

But "git log --pretty=oneline -g master --default refs/stash"
would not make much sense would it?  The design currently seems
to follow what I suggested earlier, namely to use a single stash
ref per repository, so $ref_stash is spelled as a variable but
it is a constant.  IOW, you do not specify "alternate stash"
from the command line.

Unfortunately we do not have a good way to reject non-flag rev
parameters in "$@" to error out "git stash list master".  What
we would want here is a way to allow things like --since=1.hour
and -20 while rejecting branch/tag names and other parameters.

>> +apply_stash () {
>> +	git-diff-files --quiet ||
>> +		die 'Cannot restore on top of a dirty state'
>
> You meant "no_changes", right? I think you miss changes in the index 
> otherwise.

Interestingly, I think this is actually correct in the sense
that the code is internally consistent, as it uses the current
index, not the HEAD, as the base of application.  It however is
debatable if this sequence (which is allowed because it does not
use no_changes here) makes sense:

	: hack hack hack
	: get interrupted
	$ git stash
	: do something else on a clean slate
	$ git commit
        : hack hack hack
        $ git add -u
        $ git unstash

>> +		git-diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
>> +		git-read-tree --reset $c_tree &&
>> +		git-update-index --add --stdin <"$a" ||
>> +			die "Cannot unstage modified files"
>
> Isn't there a way to avoid the temporary file here?

We could obviously do

	files=$(git-diff --cached ...) &&
        git-read-tree --reset $c_tree &&
        echo "$files" | git-update-index --add --stdin

Oops, the last "echo" may have to be "printf '%s'" ;-)

There might be quite many files that have been added to make the
$files variable too big to be given to echo, though.

	git-diff --cached ... |
        (
        	git-read-tree --reset $c_tree &&
                git-update-index --add --stdin
	)

would not work, unless you copy the index into a temporary file
and have the upstream git-diff use it.  But then we are using a
temporary file anyway.

>> +		set x -n 10
>> +		shift
>
> This is more elegantly written as "set -- -n 10", or in our context even 
> "set -- -10".

That is Ok in POSIX only world, but so far we have stayed away
from using "set -- potentially-dangerous-string" in any of our
scripts.  I would feel x + shift is safer and more comfortable
but probably that is largely because I am old fashioned.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH (3rd try)] Add git-stash script
  2007-06-30 15:41   ` Johannes Schindelin
  2007-06-30 17:19     ` Junio C Hamano
@ 2007-06-30 23:27     ` しらいしななこ
  1 sibling, 0 replies; 7+ messages in thread
From: しらいしななこ @ 2007-06-30 23:27 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, GIT

Quoting Johannes Schindelin <Johannes.Schindelin@gmx.de>:

> Hi,

Hello.

> On Sat, 30 Jun 2007, しらいしななこ wrote:
>
>> diff --git a/git-stash.sh b/git-stash.sh
>> [...]
>> +	printf >&2 'Saved WIP on %s\n' "$msg"
>
> You have an awful lot of printfs in the code. Why not just use echos?

I just imitated other scripts.  I can change it to

echo >&2 "Saved WIP on $msg"

but after reading Junio's comments, I think I probably should not.

>> +list_stash () {
>> +	git-log --pretty=oneline -g "$@" $ref_stash |
>
> Wouldn't you want "--default $ref_stash" here?

I do not know, and I'm sorry I do not understand Junio's comments.

What does --default do in this case?

>> +apply_stash () {
>> +	git-diff-files --quiet ||
>> +		die 'Cannot restore on top of a dirty state'
>
> You meant "no_changes", right? I think you miss changes in the index 
> otherwise.

After I read exchanges between you and Junio I do not know which way
is preferred.  Using no_changes does not forbid me from doing that,
but I think Junio's example will be forbidden.  The original scenario
was that I apply a stashed change to an unmodified state, and there is
no problem either way.

-- 
Nanako Shiraishi
http://ivory.ap.teacup.com/nanako3/

----------------------------------------------------------------------
Free pop3 email with a spam filter.
http://www.bluebottle.com

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH (3rd try)] Add git-stash script
       [not found] <200706302327.l5UNRMtc027974@mi0.bluebottle.com>
@ 2007-07-01  0:16 ` Johannes Schindelin
  0 siblings, 0 replies; 7+ messages in thread
From: Johannes Schindelin @ 2007-07-01  0:16 UTC (permalink / raw)
  To: しらいしななこ
  Cc: Junio C Hamano, GIT

[-- Attachment #1: Type: TEXT/PLAIN, Size: 2267 bytes --]

Hi,

On Sun, 1 Jul 2007, しらいしななこ wrote:

> Quoting Johannes Schindelin <Johannes.Schindelin@gmx.de>:
> 
> > On Sat, 30 Jun 2007, しらいしななこ wrote:
> >
> >> diff --git a/git-stash.sh b/git-stash.sh
> >> [...]
> >> +	printf >&2 'Saved WIP on %s\n' "$msg"
> >
> > You have an awful lot of printfs in the code. Why not just use echos?
> 
> I just imitated other scripts.  I can change it to
> 
> echo >&2 "Saved WIP on $msg"
> 
> but after reading Junio's comments, I think I probably should not.

You can leave them as-are, but I am actually more used to reading 
something like

	echo "Saved WIP on $msg" >&2

> >> +list_stash () {
> >> +	git-log --pretty=oneline -g "$@" $ref_stash |
> >
> > Wouldn't you want "--default $ref_stash" here?
> 
> I do not know, and I'm sorry I do not understand Junio's comments.
> 
> What does --default do in this case?

I had the impression that

	git stash list stash@{3}

would make sense. Probably I was mistaken.

> >> +apply_stash () {
> >> +	git-diff-files --quiet ||
> >> +		die 'Cannot restore on top of a dirty state'
> >
> > You meant "no_changes", right? I think you miss changes in the index 
> > otherwise.
> 
> After I read exchanges between you and Junio I do not know which way is 
> preferred.

Well, there are two differing, and contradicting, preferences: Junio's and 
mine.

Now you can have a third preference, but in the end what weighs most is 
the opinion of the person implementing it.

> Using no_changes does not forbid me from doing that, but I think Junio's 
> example will be forbidden.  The original scenario was that I apply a 
> stashed change to an unmodified state, and there is no problem either 
> way.

Oh, but there is. Imagine this:

File "f" contains "1" in HEAD, "2" in the index and "3" in the working 
directory. Now you say "git stash". Okay, file "f" now contains "1" in all 
three, and the working directory is clean. Suppose you decide the stash 
was a mistake. "git stash apply". File "f" contains "1" in _both_ the HEAD 
and the index, and "3" in the working directory.

Now, you might say that it does not matter. But then we should not even 
save the index.

Can I make one further request? Add "git stash save" to do the same as 
"git stash"?

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2007-07-01  0:16 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <200706302327.l5UNRMtc027974@mi0.bluebottle.com>
2007-07-01  0:16 ` [PATCH (3rd try)] Add git-stash script Johannes Schindelin
2007-06-30  2:05 [PATCH (2nd " Johannes Schindelin
2007-06-30  5:37 ` [PATCH (3rd " しらいしななこ
2007-06-30  6:12   ` Jeff King
2007-06-30  6:25     ` Junio C Hamano
2007-06-30 15:41   ` Johannes Schindelin
2007-06-30 17:19     ` Junio C Hamano
2007-06-30 23:27     ` しらいしななこ

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).