threads / bug / 45407

[BUG] "git stash -- path" reports wrong unstaged changes

Subject: [BUG] "git stash -- path" reports wrong unstaged changes

## tl;dr

26 messages between Mar 17, 2017 and Mar 22, 2017.

replies: 25people: 3as markdown or json

Jeff King· Mar 17, 2017, 14:50 UTC · lore

I used "git stash -- path" for the first time today and happened to notice an oddity. If I run:

	git init -q repo
	cd repo
	
	for i in one two; do
		echo content >$i
		git add $i
	done
	git commit -qm base
	
	for i in one two; do
		echo change >$i
	done
	git stash -- one
it says:
  Saved working directory and index state WIP on master: 20cfadf base
  Unstaged changes after reset:
  M	one
  M	two
Even though "one" no longer has unstaged changes.
If I run with GIT_TRACE=1, that message is generated by:
  git reset -- one

which makes sense. At that stage we've just reset the index, but the working tree file still has modifications. In the non-pathspec case we run "git reset --hard", which takes care of the index and the working tree.

It's really "checkout-index" that cleans the working tree, but it doesn't have porcelain finery like an "Unstaged changes" message. I think the patch below would fix it, but I wonder if we can do it in a way that doesn't involve calling diff-files twice.

-Peff
---
diff --git a/git-stash.sh b/git-stash.sh
index 9c70662cc..9a4bb503a 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -299,10 +299,15 @@ push_stash () {
 	then
 		if test $# != 0
 		then
-			git reset ${GIT_QUIET:+-q} -- "$@"
+			git reset -q -- "$@"
 			git ls-files -z --modified -- "$@" |
 			git checkout-index -z --force --stdin
 			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
+			if test -z "$GIT_QUIET" && ! git diff-files --quiet
+			then
+				say "$(gettext "Unstaged changes after reset:")"
+				git diff-files --name-status
+			fi
 		else
 			git reset --hard ${GIT_QUIET:+-q}
 		fi
Thomas Gummerer· Mar 18, 2017, 18:36 UTC · re: Jeff King · lore

Re: [BUG] "git stash -- path" reports wrong unstaged changes

On 03/17, Jeff King wrote:
Show 25 quoted lines
> I used "git stash -- path" for the first time today and happened to
> notice an oddity. If I run:
> 
> 	git init -q repo
> 	cd repo
> 	
> 	for i in one two; do
> 		echo content >$i
> 		git add $i
> 	done
> 	git commit -qm base
> 	
> 	for i in one two; do
> 		echo change >$i
> 	done
> 	git stash -- one
> 
> it says:
> 
>   Saved working directory and index state WIP on master: 20cfadf base
>   Unstaged changes after reset:
>   M	one
>   M	two
> 
> Even though "one" no longer has unstaged changes.

Yeah, this is clearly not right. Thanks for catching this before it got into any release.

Show 38 quoted lines
> If I run with GIT_TRACE=1, that message is generated by:
> 
>   git reset -- one
> 
> which makes sense. At that stage we've just reset the index, but the
> working tree file still has modifications. In the non-pathspec case we
> run "git reset --hard", which takes care of the index and the working
> tree.
> 
> It's really "checkout-index" that cleans the working tree, but it
> doesn't have porcelain finery like an "Unstaged changes" message. I
> think the patch below would fix it, but I wonder if we can do it in a
> way that doesn't involve calling diff-files twice.
> 
> -Peff
> 
> ---
> diff --git a/git-stash.sh b/git-stash.sh
> index 9c70662cc..9a4bb503a 100755
> --- a/git-stash.sh
> +++ b/git-stash.sh
> @@ -299,10 +299,15 @@ push_stash () {
>  	then
>  		if test $# != 0
>  		then
> -			git reset ${GIT_QUIET:+-q} -- "$@"
> +			git reset -q -- "$@"
>  			git ls-files -z --modified -- "$@" |
>  			git checkout-index -z --force --stdin
>  			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
> +			if test -z "$GIT_QUIET" && ! git diff-files --quiet
> +			then
> +				say "$(gettext "Unstaged changes after reset:")"
> +				git diff-files --name-status
> +			fi
>  		else
>  			git reset --hard ${GIT_QUIET:+-q}
>  		fi
This would mean the user gets something like in your case above:
    Unstaged changes after reset:
     M	two

As a user that doesn't know the internal implementation of push_stash, this would make me wonder why git stash would mention a file that is not provided as pathspec, but not the one that was provided in the pathspec argument.

I think one option would be to to just keep quiet about the exact changes that git stash push makes, similar to what we do in the --include-untracked and in the -p case. The other option would be to find the files that are affected and print them, but that would probably be a bit too noisy especially in cases such as git stash push -- docs/*.

Also from reading the code in the -p case, when --keep-index is given, the git reset there doesn't respect $GIT_QUIET at all, and also doesn't respect the pathspec argument, which seems like another bug. I can submit a patch series for those, but I won't get to it before tomorrow :)

Thomas Gummerer· Mar 19, 2017, 20:23 UTC · re: Thomas Gummerer · lore

Re: [BUG] "git stash -- path" reports wrong unstaged changes

This series implements my proposal for fixing bug in git stash -- <pathspec>, plus the two other things I noticed while looking at the code.

Show 6 quoted lines
> I think one option would be to to just keep quiet about the exact
> changes that git stash push makes, similar to what we do in the
> --include-untracked and in the -p case.  The other option would be to
> find the files that are affected and print them, but that would
> probably be a bit too noisy especially in cases such as
> git stash push -- docs/*.

1/3 implements just this. This may deserve some more discussion on what should actually be done.

Show 5 quoted lines
> Also from reading the code in the -p case, when --keep-index is given,
> the git reset there doesn't respect $GIT_QUIET at all, and also
> doesn't respect the pathspec argument, which seems like another bug.
> I can submit a patch series for those, but I won't get to it before
> tomorrow :)

2/3 and 3/3 have the fixes for the above. No tests yet, as I'm not 100% sure 3/3 is doing the right thing, though I think it makes the overall experience better.

Thomas Gummerer (3):
  stash: show less information for stash push -- <pathspec>
  stash: make push -p -q --no-keep-index quiet
  stash: pass the pathspec argument to git reset
 git-stash.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
-- 
2.12.0.483.gad4152297
Thomas Gummerer· Mar 19, 2017, 20:23 UTC · re: Thomas Gummerer · lore

[PATCH/RFC 1/3] stash: show less information for stash push -- <pathspec>

When using git stash push -- <pathspec> in the following sequence:
       git init -q repo
       cd repo
       for i in one two; do
               echo content >$i
               git add $i
       done
       git commit -qm base
       for i in one two; do
               echo change >$i
       done
       git stash -- one
it shows:
   Saved working directory and index state WIP on master: 20cfadf base
   Unstaged changes after reset:
    M   one
    M   two
Even though "one" no longer has unstaged changes.

It really is enough for the user to know that the stash is created, without bothering them with the internal details of what's happening. Always pass the -q flag to git clean and git reset in the pathspec case, to avoid unnecessary and potentially confusing output.

Reported-by: Jeff King <peff@peff.net>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---
 git-stash.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/git-stash.sh b/git-stash.sh
index 9c70662cc8..59f055e27b 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -299,10 +299,10 @@ push_stash () {
 	then
 		if test $# != 0
 		then
-			git reset ${GIT_QUIET:+-q} -- "$@"
+			git reset -q -- "$@"
 			git ls-files -z --modified -- "$@" |
 			git checkout-index -z --force --stdin
-			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
+			git clean --force -q -d -- "$@"
 		else
 			git reset --hard ${GIT_QUIET:+-q}
 		fi
-- 
2.12.0.483.gad4152297
Junio C Hamano· Mar 20, 2017, 17:51 UTC · re: Thomas Gummerer · lore

Re: [PATCH/RFC 1/3] stash: show less information for stash push -- <pathspec>

Thomas Gummerer <t.gummerer@gmail.com> writes:
Show 53 quoted lines
> When using git stash push -- <pathspec> in the following sequence:
>
>        git init -q repo
>        cd repo
>
>        for i in one two; do
>                echo content >$i
>                git add $i
>        done
>        git commit -qm base
>
>        for i in one two; do
>                echo change >$i
>        done
>        git stash -- one
>
> it shows:
>
>    Saved working directory and index state WIP on master: 20cfadf base
>    Unstaged changes after reset:
>     M   one
>     M   two
>
> Even though "one" no longer has unstaged changes.
>
> It really is enough for the user to know that the stash is created,
> without bothering them with the internal details of what's happening.
> Always pass the -q flag to git clean and git reset in the pathspec case,
> to avoid unnecessary and potentially confusing output.
>
> Reported-by: Jeff King <peff@peff.net>
> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
> ---
>  git-stash.sh | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/git-stash.sh b/git-stash.sh
> index 9c70662cc8..59f055e27b 100755
> --- a/git-stash.sh
> +++ b/git-stash.sh
> @@ -299,10 +299,10 @@ push_stash () {
>  	then
>  		if test $# != 0
>  		then
> -			git reset ${GIT_QUIET:+-q} -- "$@"
> +			git reset -q -- "$@"
>  			git ls-files -z --modified -- "$@" |
>  			git checkout-index -z --force --stdin
> -			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
> +			git clean --force -q -d -- "$@"
>  		else
>  			git reset --hard ${GIT_QUIET:+-q}
>  		fi

Yup, we only said "HEAD is now at ..." in the non-pathspec case (and we of course still do). We didn't report changes to which paths have been reverted in (or which new paths are removed from) the working tree to the original state (and we of course still don't).

The messages from reset and clean that reports these probably do not need to be shown by default to the users (they can always check with "git stash show" when they are curious or when they want to double check).

Jeff King· Mar 20, 2017, 18:48 UTC · re: Junio C Hamano · lore

Re: [PATCH/RFC 1/3] stash: show less information for stash push -- <pathspec>

On Mon, Mar 20, 2017 at 10:51:16AM -0700, Junio C Hamano wrote:
Show 27 quoted lines
> > diff --git a/git-stash.sh b/git-stash.sh
> > index 9c70662cc8..59f055e27b 100755
> > --- a/git-stash.sh
> > +++ b/git-stash.sh
> > @@ -299,10 +299,10 @@ push_stash () {
> >  	then
> >  		if test $# != 0
> >  		then
> > -			git reset ${GIT_QUIET:+-q} -- "$@"
> > +			git reset -q -- "$@"
> >  			git ls-files -z --modified -- "$@" |
> >  			git checkout-index -z --force --stdin
> > -			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
> > +			git clean --force -q -d -- "$@"
> >  		else
> >  			git reset --hard ${GIT_QUIET:+-q}
> >  		fi
> 
> Yup, we only said "HEAD is now at ..." in the non-pathspec case (and
> we of course still do).  We didn't report changes to which paths
> have been reverted in (or which new paths are removed from) the
> working tree to the original state (and we of course still don't).
> 
> The messages from reset and clean that reports these probably do not
> need to be shown by default to the users (they can always check with
> "git stash show" when they are curious or when they want to double
> check).

I'm not sure if you are arguing here that the non-pathspec case should move to an unconditional "-q", too, to suppress the "HEAD is now at" message. But I think that is a good suggestion. It would make the two cases consistent, and it is not really adding anything of value (it is always just HEAD, and if you do not provide a custom message, the short-sha1 and subject are already in the "Saved..." line above).

-Peff
Junio C Hamano· Mar 20, 2017, 19:42 UTC · re: Jeff King · lore

Re: [PATCH/RFC 1/3] stash: show less information for stash push -- <pathspec>

Jeff King <peff@peff.net> writes:
Show 36 quoted lines
> On Mon, Mar 20, 2017 at 10:51:16AM -0700, Junio C Hamano wrote:
>
>> > diff --git a/git-stash.sh b/git-stash.sh
>> > index 9c70662cc8..59f055e27b 100755
>> > --- a/git-stash.sh
>> > +++ b/git-stash.sh
>> > @@ -299,10 +299,10 @@ push_stash () {
>> >  	then
>> >  		if test $# != 0
>> >  		then
>> > -			git reset ${GIT_QUIET:+-q} -- "$@"
>> > +			git reset -q -- "$@"
>> >  			git ls-files -z --modified -- "$@" |
>> >  			git checkout-index -z --force --stdin
>> > -			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
>> > +			git clean --force -q -d -- "$@"
>> >  		else
>> >  			git reset --hard ${GIT_QUIET:+-q}
>> >  		fi
>> 
>> Yup, we only said "HEAD is now at ..." in the non-pathspec case (and
>> we of course still do).  We didn't report changes to which paths
>> have been reverted in (or which new paths are removed from) the
>> working tree to the original state (and we of course still don't).
>> 
>> The messages from reset and clean that reports these probably do not
>> need to be shown by default to the users (they can always check with
>> "git stash show" when they are curious or when they want to double
>> check).
>
> I'm not sure if you are arguing here that the non-pathspec case should
> move to an unconditional "-q", too, to suppress the "HEAD is now at"
> message.  But I think that is a good suggestion. It would make the two
> cases consistent, and it is not really adding anything of value (it is
> always just HEAD, and if you do not provide a custom message, the
> short-sha1 and subject are already in the "Saved..." line above).

I wasn't suggesting it (I was just saying that these extra messages are not something we found necessary for consistency with the original codepath when we added the pathspec support). I wasn't even thinking about what the original codepath did, i.e. when the command is run without pathspec.

I too suspect that most of the ${GIT_QUIET:+-q} can just become an unconditional -q as you do.

Thomas Gummerer· Mar 21, 2017, 20:48 UTC · re: Junio C Hamano · lore

Re: [PATCH/RFC 1/3] stash: show less information for stash push -- <pathspec>

On 03/20, Junio C Hamano wrote:
Show 47 quoted lines
> Jeff King <peff@peff.net> writes:
> 
> > On Mon, Mar 20, 2017 at 10:51:16AM -0700, Junio C Hamano wrote:
> >
> >> > diff --git a/git-stash.sh b/git-stash.sh
> >> > index 9c70662cc8..59f055e27b 100755
> >> > --- a/git-stash.sh
> >> > +++ b/git-stash.sh
> >> > @@ -299,10 +299,10 @@ push_stash () {
> >> >  	then
> >> >  		if test $# != 0
> >> >  		then
> >> > -			git reset ${GIT_QUIET:+-q} -- "$@"
> >> > +			git reset -q -- "$@"
> >> >  			git ls-files -z --modified -- "$@" |
> >> >  			git checkout-index -z --force --stdin
> >> > -			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
> >> > +			git clean --force -q -d -- "$@"
> >> >  		else
> >> >  			git reset --hard ${GIT_QUIET:+-q}
> >> >  		fi
> >> 
> >> Yup, we only said "HEAD is now at ..." in the non-pathspec case (and
> >> we of course still do).  We didn't report changes to which paths
> >> have been reverted in (or which new paths are removed from) the
> >> working tree to the original state (and we of course still don't).
> >> 
> >> The messages from reset and clean that reports these probably do not
> >> need to be shown by default to the users (they can always check with
> >> "git stash show" when they are curious or when they want to double
> >> check).
> >
> > I'm not sure if you are arguing here that the non-pathspec case should
> > move to an unconditional "-q", too, to suppress the "HEAD is now at"
> > message.  But I think that is a good suggestion. It would make the two
> > cases consistent, and it is not really adding anything of value (it is
> > always just HEAD, and if you do not provide a custom message, the
> > short-sha1 and subject are already in the "Saved..." line above).
> 
> I wasn't suggesting it (I was just saying that these extra messages
> are not something we found necessary for consistency with the
> original codepath when we added the pathspec support).  I wasn't
> even thinking about what the original codepath did, i.e. when the
> command is run without pathspec.
> 
> I too suspect that most of the ${GIT_QUIET:+-q} can just become an
> unconditional -q as you do.

Thanks both, I do agree that passing -q unconditionally is probably the right thing to do. Will do that, and also pass -q unconditionally to the git reset I addressed in 2/3 here in the re-roll.

Jeff King· Mar 20, 2017, 18:42 UTC · re: Thomas Gummerer · lore

Re: [PATCH/RFC 1/3] stash: show less information for stash push -- <pathspec>

On Sun, Mar 19, 2017 at 08:23:49PM +0000, Thomas Gummerer wrote:
Show 29 quoted lines
> When using git stash push -- <pathspec> in the following sequence:
> 
>        git init -q repo
>        cd repo
> 
>        for i in one two; do
>                echo content >$i
>                git add $i
>        done
>        git commit -qm base
> 
>        for i in one two; do
>                echo change >$i
>        done
>        git stash -- one
> 
> it shows:
> 
>    Saved working directory and index state WIP on master: 20cfadf base
>    Unstaged changes after reset:
>     M   one
>     M   two
> 
> Even though "one" no longer has unstaged changes.
> 
> It really is enough for the user to know that the stash is created,
> without bothering them with the internal details of what's happening.
> Always pass the -q flag to git clean and git reset in the pathspec case,
> to avoid unnecessary and potentially confusing output.
Yeah, on further reflection, this is definitely the right thing to do.
-Peff
Thomas Gummerer· Mar 19, 2017, 20:23 UTC · re: Thomas Gummerer · lore

[PATCH/RFC 3/3] stash: pass the pathspec argument to git reset

For "git stash -p --no-keep-index", the pathspec argument is currently not passed to "git reset". This means that changed that are staged but that are excluded from the pathspec still get unstaged by git stash -p.

Make sure that doesn't happen by passing the pathspec argument to the git reset in question, bringing the behaviour in line with "git stash -- <pathspec>".

Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---
So this make things a bit inconsistent in for example the following
situation:
    $ git status -s
     M git-stash.sh
    M  read-cache.c

where using git stash -p --no-keep-index, when only changes in git-stash.sh are added to the stash would reset read-cache.c, but with git stash -p --no-keep-index -- git-stash.sh, read-cache.c would not be reset.

However it does bring things more in line with git stash -- <pathspec>, so I think this is an improvement.

 git-stash.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/git-stash.sh b/git-stash.sh
index c58f98f1d6..2d5c9a609c 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -322,7 +322,7 @@ push_stash () {
 
 		if test "$keep_index" != "t"
 		then
-			git reset ${GIT_QUIET:+-q}
+			git reset ${GIT_QUIET:+-q} -- "$@"
 		fi
 	fi
 }
-- 
2.12.0.483.gad4152297
Jeff King· Mar 20, 2017, 19:08 UTC · re: Thomas Gummerer · lore

Re: [PATCH/RFC 3/3] stash: pass the pathspec argument to git reset

On Sun, Mar 19, 2017 at 08:23:51PM +0000, Thomas Gummerer wrote:
> For "git stash -p --no-keep-index", the pathspec argument is currently
> not passed to "git reset".  This means that changed that are staged but
> that are excluded from the pathspec still get unstaged by git stash -p.

Yeah, I noticed this while playing around with patch 2. This seems like an improvement to me. Unlike the other patches (which are just tweaking quietness), I think this one really needs a test.

Also, s/changed/changes/ above.
Show 12 quoted lines
> ---
> So this make things a bit inconsistent in for example the following
> situation:
> 
>     $ git status -s
>      M git-stash.sh
>     M  read-cache.c
> 
> where using git stash -p --no-keep-index, when only changes in
> git-stash.sh are added to the stash would reset read-cache.c, but with
> git stash -p --no-keep-index -- git-stash.sh, read-cache.c would not
> be reset.

I think it's OK. You can't select (or not select) changes from the index anyway. TBH, I kind of wonder if "git stash -p --no-keep-index" makes any sense at all.

> However it does bring things more in line with git stash --
> <pathspec>, so I think this is an improvement.

I did notice one other case while looking at this that I think is much more serious. The "read-tree" call in the non-patch-mode case doesn't use a pathspec either. So if we have our same setup where "one" and "two" have unstaged changes and we do:

  git stash push -k one
Then we stash "one", but the change in "two" is wiped out completely!

I don't think read-tree takes pathspecs, so it would have to drop the "-u" and replace it with checkout-index. I'm confused about why we would need it in the first place, though. We should have already dealt with the working tree files in the earlier block, so there should be nothing to checkout.

The "-u" goes all the way back to 7bedebcaa (stash: introduce 'stash save --keep-index' option, 2008-06-27). I wonder if it has always been unnecessary, but we never noticed because it's just a noop.

-Peff
Thomas Gummerer· Mar 21, 2017, 21:14 UTC · re: Jeff King · lore

Re: [PATCH/RFC 3/3] stash: pass the pathspec argument to git reset

On 03/20, Jeff King wrote:
Show 11 quoted lines
> On Sun, Mar 19, 2017 at 08:23:51PM +0000, Thomas Gummerer wrote:
> 
> > For "git stash -p --no-keep-index", the pathspec argument is currently
> > not passed to "git reset".  This means that changed that are staged but
> > that are excluded from the pathspec still get unstaged by git stash -p.
> 
> Yeah, I noticed this while playing around with patch 2. This seems
> like an improvement to me. Unlike the other patches (which are just
> tweaking quietness), I think this one really needs a test.
>
> Also, s/changed/changes/ above.
Thanks.  Will add a test in the re-roll.
Show 16 quoted lines
> > ---
> > So this make things a bit inconsistent in for example the following
> > situation:
> > 
> >     $ git status -s
> >      M git-stash.sh
> >     M  read-cache.c
> > 
> > where using git stash -p --no-keep-index, when only changes in
> > git-stash.sh are added to the stash would reset read-cache.c, but with
> > git stash -p --no-keep-index -- git-stash.sh, read-cache.c would not
> > be reset.
> 
> I think it's OK. You can't select (or not select) changes from the index
> anyway. TBH, I kind of wonder if "git stash -p --no-keep-index" makes
> any sense at all.

Yeah I don't have any use case for it, but maybe someone does, so I think fixing it this way now is the best way forward.

Show 11 quoted lines
> > However it does bring things more in line with git stash --
> > <pathspec>, so I think this is an improvement.
> 
> I did notice one other case while looking at this that I think is much
> more serious. The "read-tree" call in the non-patch-mode case doesn't
> use a pathspec either. So if we have our same setup where "one" and
> "two" have unstaged changes and we do:
> 
>   git stash push -k one
> 
> Then we stash "one", but the change in "two" is wiped out completely!
Good catch, that's definitely another bug :/ 
Show 9 quoted lines
> I don't think read-tree takes pathspecs, so it would have to drop the
> "-u" and replace it with checkout-index. I'm confused about why we would
> need it in the first place, though. We should have already dealt with
> the working tree files in the earlier block, so there should be nothing
> to checkout.
> 
> The "-u" goes all the way back to 7bedebcaa (stash: introduce 'stash
> save --keep-index' option, 2008-06-27). I wonder if it has always been
> unnecessary, but we never noticed because it's just a noop.

I do think the "-u" is necessary. Say we have a repository with changes in 'foo' and 'bar', where the changes in bar are added to the index.

Then 'git stash -k' would wipe out the changes in both 'foo' and 'bar' through 'git reset --hard', but we do want the changes in 'bar' to still exist on disk, which they wouldn't without the "-u".

But I'll replace the "-u" with checkout-index, so we can respect the pathspec.

Thanks!
> -Peff
Jeff King· Mar 21, 2017, 22:12 UTC · re: Thomas Gummerer · lore

Re: [PATCH/RFC 3/3] stash: pass the pathspec argument to git reset

On Tue, Mar 21, 2017 at 09:14:24PM +0000, Thomas Gummerer wrote:
Show 17 quoted lines
> > I don't think read-tree takes pathspecs, so it would have to drop the
> > "-u" and replace it with checkout-index. I'm confused about why we would
> > need it in the first place, though. We should have already dealt with
> > the working tree files in the earlier block, so there should be nothing
> > to checkout.
> > 
> > The "-u" goes all the way back to 7bedebcaa (stash: introduce 'stash
> > save --keep-index' option, 2008-06-27). I wonder if it has always been
> > unnecessary, but we never noticed because it's just a noop.
> 
> I do think the "-u" is necessary.  Say we have a repository with
> changes in 'foo' and 'bar', where the changes in bar are added to the
> index.
> 
> Then 'git stash -k' would wipe out the changes in both 'foo' and 'bar'
> through 'git reset --hard', but we do want the changes in 'bar' to
> still exist on disk, which they wouldn't without the "-u".

Yeah, you're right. It makes me wonder if the "-k" case should be skipping the "reset --hard". But as this series has shown, this procedure is sufficiently tricky that it may be a good idea to make the minimal change.

> But I'll replace the "-u" with checkout-index, so we can respect the
> pathspec.
That makes sense.
-Peff
Thomas Gummerer· Mar 19, 2017, 20:23 UTC · re: Thomas Gummerer · lore

[PATCH/RFC 2/3] stash: make push -p -q --no-keep-index quiet

Currently when using "git stash push -p -q --no-keep-index", the -q flag is not passed to the git reset which is executed when --no-keep-index is also passed in. This means that git stash is somewhat verbose in this mode even when the -q flag is passed in. This was always the case since "git stash save -p" was introduced in dda1f2a5 ("Implement 'git stash save --patch'", 2009-08-13).

Properly pass the -q flag on to git reset, to make "git stash push -p -q" as quiet as it should be.

Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---
 git-stash.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/git-stash.sh b/git-stash.sh
index 59f055e27b..c58f98f1d6 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -322,7 +322,7 @@ push_stash () {
 
 		if test "$keep_index" != "t"
 		then
-			git reset
+			git reset ${GIT_QUIET:+-q}
 		fi
 	fi
 }
-- 
2.12.0.483.gad4152297
Jeff King· Mar 20, 2017, 18:55 UTC · re: Thomas Gummerer · lore

Re: [PATCH/RFC 2/3] stash: make push -p -q --no-keep-index quiet

On Sun, Mar 19, 2017 at 08:23:50PM +0000, Thomas Gummerer wrote:
Show 9 quoted lines
> Currently when using "git stash push -p -q --no-keep-index", the -q flag
> is not passed to the git reset which is executed when --no-keep-index is
> also passed in.  This means that git stash is somewhat verbose in this
> mode even when the -q flag is passed in.  This was always the case since
> "git stash save -p" was introduced in dda1f2a5 ("Implement 'git stash
> save --patch'", 2009-08-13).
> 
> Properly pass the -q flag on to git reset, to make "git stash push -p
> -q" as quiet as it should be.

Yeah, this is an obvious bug-fix. Though given my earlier response to Junio, I wonder if we should just be doing "reset -q" unconditionally for most of these calls.

I guess this one does say more than just "HEAD is at..."; it mentions the files that you _didn't_ pick. Though that now makes it inconsistent with the pathspec case, especially if you do:

  git stash push -p --no-keep-index -- one
which will mention "two" as remaining with unstaged changes.
-Peff
Thomas Gummerer· Mar 21, 2017, 22:12 UTC · re: Thomas Gummerer · lore

[PATCH v2 0/3] Re: [BUG] "git stash -- path" reports wrong unstaged changes

Thanks Junio and Peff for comments and suggestions on the previous round.

Changes since v1:
- 1/3 now contains the changes that were previously split up into 1/3
  and 2/3, and also makes git reset --hard in the simple 'git stash'
  case quiet unconditionally.
- 2/3 is what was 3/3 in the previous round, and now includes a test
  for the expected behaviour.
- 3/3 is the fix for the bug Peff noticed for git stash -k with
  changes that are not yet in the index.
Thomas Gummerer (3):
  stash: don't show internal implementation details
  stash: pass the pathspec argument to git reset
  stash: keep untracked files intact in stash -k
 git-stash.sh           | 12 +++++++-----
 t/t3903-stash.sh       | 16 +++++++++++++++-
 t/t3904-stash-patch.sh |  8 ++++++++
 3 files changed, 30 insertions(+), 6 deletions(-)
-- 
2.12.0.401.g98d3b1bb99.dirty
Thomas Gummerer· Mar 21, 2017, 22:12 UTC · re: Thomas Gummerer · lore

[PATCH v2 2/3] stash: pass the pathspec argument to git reset

For "git stash -p --no-keep-index", the pathspec argument is currently not passed to "git reset". This means that changes that are staged but that are excluded from the pathspec still get unstaged by git stash -p.

Make sure that doesn't happen by passing the pathspec argument to the git reset in question, bringing the behaviour in line with "git stash -- <pathspec>".

Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---
 git-stash.sh           | 2 +-
 t/t3904-stash-patch.sh | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/git-stash.sh b/git-stash.sh
index ba86d84321..13711764a9 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -322,7 +322,7 @@ push_stash () {
 
 		if test "$keep_index" != "t"
 		then
-			git reset -q
+			git reset -q -- "$@"
 		fi
 	fi
 }
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index 38e730090f..83744f8c93 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -77,6 +77,14 @@ test_expect_success 'git stash --no-keep-index -p' '
 	verify_state dir/foo work index
 '
 
+test_expect_success 'stash -p --no-keep-index -- <pathspec> does not unstage other files' '
+	set_state HEAD HEADfile_work HEADfile_index &&
+	set_state dir/foo work index &&
+	echo y | git stash push -p --no-keep-index -- HEAD &&
+	verify_state HEAD committed committed &&
+	verify_state dir/foo work index
+'
+
 test_expect_success 'none of this moved HEAD' '
 	verify_saved_head
 '
-- 
2.12.0.401.g98d3b1bb99.dirty
Jeff King· Mar 21, 2017, 22:19 UTC · re: Thomas Gummerer · lore

Re: [PATCH v2 2/3] stash: pass the pathspec argument to git reset

On Tue, Mar 21, 2017 at 10:12:18PM +0000, Thomas Gummerer wrote:
Show 13 quoted lines
> For "git stash -p --no-keep-index", the pathspec argument is currently
> not passed to "git reset".  This means that changes that are staged but
> that are excluded from the pathspec still get unstaged by git stash -p.
> 
> Make sure that doesn't happen by passing the pathspec argument to the
> git reset in question, bringing the behaviour in line with "git stash --
> <pathspec>".
> 
> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
> ---
>  git-stash.sh           | 2 +-
>  t/t3904-stash-patch.sh | 8 ++++++++
>  2 files changed, 9 insertions(+), 1 deletion(-)
This one looks good to me.
-Peff
Thomas Gummerer· Mar 21, 2017, 22:12 UTC · re: Thomas Gummerer · lore

[PATCH v2 1/3] stash: don't show internal implementation details

git stash push uses other git commands internally. Currently it only passes the -q flag to those if the -q flag is passed to git stash. when using 'git stash push -p -q --no-keep-index', it doesn't even pass the flag on to the internal reset at all.

It really is enough for the user to know that the stash is created, without bothering them with the internal details of what's happening. Always pass the -q flag to the internal git clean and git reset commands, to avoid unnecessary and potentially confusing output.

Reported-by: Jeff King <peff@peff.net>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---
 git-stash.sh     | 8 ++++----
 t/t3903-stash.sh | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/git-stash.sh b/git-stash.sh
index 9c70662cc8..ba86d84321 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -299,12 +299,12 @@ push_stash () {
 	then
 		if test $# != 0
 		then
-			git reset ${GIT_QUIET:+-q} -- "$@"
+			git reset -q -- "$@"
 			git ls-files -z --modified -- "$@" |
 			git checkout-index -z --force --stdin
-			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
+			git clean --force -q -d -- "$@"
 		else
-			git reset --hard ${GIT_QUIET:+-q}
+			git reset --hard -q
 		fi
 		test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
 		if test -n "$untracked"
@@ -322,7 +322,7 @@ push_stash () {
 
 		if test "$keep_index" != "t"
 		then
-			git reset
+			git reset -q
 		fi
 	fi
 }
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 89877e4b52..ea8e5c7818 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -663,7 +663,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu
 		sane_unset GIT_MERGE_VERBOSITY &&
 		git stash apply
 	) |
-	sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..."
+	sed -e 1,1d >actual && # drop "Saved..."
 	test_i18ncmp expect actual
 '
 
-- 
2.12.0.401.g98d3b1bb99.dirty
Jeff King· Mar 21, 2017, 22:14 UTC · re: Thomas Gummerer · lore

Re: [PATCH v2 1/3] stash: don't show internal implementation details

On Tue, Mar 21, 2017 at 10:12:17PM +0000, Thomas Gummerer wrote:
Show 12 quoted lines
> git stash push uses other git commands internally.  Currently it only
> passes the -q flag to those if the -q flag is passed to git stash.  when
> using 'git stash push -p -q --no-keep-index', it doesn't even pass the
> flag on to the internal reset at all.
> 
> It really is enough for the user to know that the stash is created,
> without bothering them with the internal details of what's happening.
> Always pass the -q flag to the internal git clean and git reset
> commands, to avoid unnecessary and potentially confusing output.
> 
> Reported-by: Jeff King <peff@peff.net>
> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>

I think combining these is fine. The incorrect output with pathspecs isn't mentioned anymore, but I think that's OK.

Show 29 quoted lines
> diff --git a/git-stash.sh b/git-stash.sh
> index 9c70662cc8..ba86d84321 100755
> --- a/git-stash.sh
> +++ b/git-stash.sh
> @@ -299,12 +299,12 @@ push_stash () {
>  	then
>  		if test $# != 0
>  		then
> -			git reset ${GIT_QUIET:+-q} -- "$@"
> +			git reset -q -- "$@"
>  			git ls-files -z --modified -- "$@" |
>  			git checkout-index -z --force --stdin
> -			git clean --force ${GIT_QUIET:+-q} -d -- "$@"
> +			git clean --force -q -d -- "$@"
>  		else
> -			git reset --hard ${GIT_QUIET:+-q}
> +			git reset --hard -q
>  		fi
>  		test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
>  		if test -n "$untracked"
> @@ -322,7 +322,7 @@ push_stash () {
>  
>  		if test "$keep_index" != "t"
>  		then
> -			git reset
> +			git reset -q
>  		fi
>  	fi
>  }
These all look fine.
Show 12 quoted lines
> diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
> index 89877e4b52..ea8e5c7818 100755
> --- a/t/t3903-stash.sh
> +++ b/t/t3903-stash.sh
> @@ -663,7 +663,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu
>  		sane_unset GIT_MERGE_VERBOSITY &&
>  		git stash apply
>  	) |
> -	sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..."
> +	sed -e 1,1d >actual && # drop "Saved..."
>  	test_i18ncmp expect actual
>  '
This too, though I think "1d" would be the more usual way to say it.
-peff
Thomas Gummerer· Mar 22, 2017, 21:33 UTC · re: Jeff King · lore

Re: [PATCH v2 1/3] stash: don't show internal implementation details

On 03/21, Jeff King wrote:
Show 17 quoted lines
> On Tue, Mar 21, 2017 at 10:12:17PM +0000, Thomas Gummerer wrote:
> 
> > git stash push uses other git commands internally.  Currently it only
> > passes the -q flag to those if the -q flag is passed to git stash.  when
> > using 'git stash push -p -q --no-keep-index', it doesn't even pass the
> > flag on to the internal reset at all.
> > 
> > It really is enough for the user to know that the stash is created,
> > without bothering them with the internal details of what's happening.
> > Always pass the -q flag to the internal git clean and git reset
> > commands, to avoid unnecessary and potentially confusing output.
> > 
> > Reported-by: Jeff King <peff@peff.net>
> > Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
> 
> I think combining these is fine. The incorrect output with pathspecs
> isn't mentioned anymore, but I think that's OK.

Yeah, they felt so similar now that splitting this up in different patches would be a bit too noisy.

Show 16 quoted lines
> [...]
> 
> > diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
> > index 89877e4b52..ea8e5c7818 100755
> > --- a/t/t3903-stash.sh
> > +++ b/t/t3903-stash.sh
> > @@ -663,7 +663,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu
> >  		sane_unset GIT_MERGE_VERBOSITY &&
> >  		git stash apply
> >  	) |
> > -	sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..."
> > +	sed -e 1,1d >actual && # drop "Saved..."
> >  	test_i18ncmp expect actual
> >  '
> 
> This too, though I think "1d" would be the more usual way to say it.

Right thanks, I'll keep that in mind for another time. (I guess just changing this doesn't warrant a re-roll?)

> -peff
Jeff King· Mar 22, 2017, 21:41 UTC · re: Thomas Gummerer · lore

Re: [PATCH v2 1/3] stash: don't show internal implementation details

On Wed, Mar 22, 2017 at 09:33:47PM +0000, Thomas Gummerer wrote:
Show 9 quoted lines
> > > -	sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..."
> > > +	sed -e 1,1d >actual && # drop "Saved..."
> > >  	test_i18ncmp expect actual
> > >  '
> > 
> > This too, though I think "1d" would be the more usual way to say it.
> 
> Right thanks, I'll keep that in mind for another time. (I guess just
> changing this doesn't warrant a re-roll?)

Nah, I don't think it's important enough (and I give even odds that Junio may just tweak it while applying).

-Peff
Thomas Gummerer· Mar 21, 2017, 22:12 UTC · re: Thomas Gummerer · lore

[PATCH v2 3/3] stash: keep untracked files intact in stash -k

Currently when there are untracked changes in a file "one" and in a file "two" in the repository and the user uses:

    git stash push -k one

all changes in "two" are wiped out completely. That is clearly not the intended result. Make sure that only the files given in the pathspec are changed when git stash push -k <pathspec> is used.

Reported-by: Jeff King <peff@peff.net>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---
 git-stash.sh     |  4 +++-
 t/t3903-stash.sh | 14 ++++++++++++++
 2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/git-stash.sh b/git-stash.sh
index 13711764a9..2fb651b2b8 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -314,7 +314,9 @@ push_stash () {
 
 		if test "$keep_index" = "t" && test -n "$i_tree"
 		then
-			git read-tree --reset -u $i_tree
+			git read-tree --reset $i_tree
+			git ls-files -z --modified -- "$@" |
+			git checkout-index -z --force --stdin
 		fi
 	else
 		git apply -R < "$TMP-patch" ||
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index ea8e5c7818..6f6e31cc6d 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -907,4 +907,18 @@ test_expect_success 'stash without verb with pathspec' '
 	test_path_is_file bar
 '
 
+test_expect_success 'stash -k -- <pathspec> leaves unstaged files intact' '
+	git reset &&
+	>foo &&
+	>bar &&
+	git add foo bar &&
+	git commit -m "test" &&
+	echo "foo" >foo &&
+	echo "bar" >bar &&
+	git stash -k -- foo &&
+	test "",bar = $(cat foo),$(cat bar) &&
+	git stash pop &&
+	test foo,bar = $(cat foo),$(cat bar)
+'
+
 test_done
-- 
2.12.0.401.g98d3b1bb99.dirty
Jeff King· Mar 21, 2017, 22:38 UTC · re: Thomas Gummerer · lore

Re: [PATCH v2 3/3] stash: keep untracked files intact in stash -k

On Tue, Mar 21, 2017 at 10:12:19PM +0000, Thomas Gummerer wrote:
Show 8 quoted lines
> Currently when there are untracked changes in a file "one" and in a file
> "two" in the repository and the user uses:
> 
>     git stash push -k one
> 
> all changes in "two" are wiped out completely.  That is clearly not the
> intended result.  Make sure that only the files given in the pathspec
> are changed when git stash push -k <pathspec> is used.
Good description.
Show 13 quoted lines
> diff --git a/git-stash.sh b/git-stash.sh
> index 13711764a9..2fb651b2b8 100755
> --- a/git-stash.sh
> +++ b/git-stash.sh
> @@ -314,7 +314,9 @@ push_stash () {
>  
>  		if test "$keep_index" = "t" && test -n "$i_tree"
>  		then
> -			git read-tree --reset -u $i_tree
> +			git read-tree --reset $i_tree
> +			git ls-files -z --modified -- "$@" |
> +			git checkout-index -z --force --stdin
>  		fi

I briefly wondered if this needed "-q" to match the earlier commit, but "checkout-index" isn't really chatty, so I don't think so (and the earlier checkout-index doesn't have it either).

I also wondered if this could be done in a single command as:
  git reset -q --hard $i_tree -- "$@"

But "git reset" can't handle pathspecs with "--hard" (which is why the similar case a few lines above uses the same commands).

So this looks good to me.
Show 13 quoted lines
> +test_expect_success 'stash -k -- <pathspec> leaves unstaged files intact' '
> +	git reset &&
> +	>foo &&
> +	>bar &&
> +	git add foo bar &&
> +	git commit -m "test" &&
> +	echo "foo" >foo &&
> +	echo "bar" >bar &&
> +	git stash -k -- foo &&
> +	test "",bar = $(cat foo),$(cat bar) &&
> +	git stash pop &&
> +	test foo,bar = $(cat foo),$(cat bar)
> +'

I always get nervous when I see test arguments without quotes, but I think this is fine (and I couldn't see a shorter way of doing it with test_cmp).

-Peff
Thomas Gummerer· Mar 22, 2017, 21:46 UTC · re: Jeff King · lore

Re: [PATCH v2 3/3] stash: keep untracked files intact in stash -k

On 03/21, Jeff King wrote:
Show 12 quoted lines
> On Tue, Mar 21, 2017 at 10:12:19PM +0000, Thomas Gummerer wrote:
> 
> > Currently when there are untracked changes in a file "one" and in a file
> > "two" in the repository and the user uses:
> > 
> >     git stash push -k one
> > 
> > all changes in "two" are wiped out completely.  That is clearly not the
> > intended result.  Make sure that only the files given in the pathspec
> > are changed when git stash push -k <pathspec> is used.
> 
> Good description.
I basically just tweaked your report here :)  thanks for that!
Show 17 quoted lines
> > diff --git a/git-stash.sh b/git-stash.sh
> > index 13711764a9..2fb651b2b8 100755
> > --- a/git-stash.sh
> > +++ b/git-stash.sh
> > @@ -314,7 +314,9 @@ push_stash () {
> >  
> >  		if test "$keep_index" = "t" && test -n "$i_tree"
> >  		then
> > -			git read-tree --reset -u $i_tree
> > +			git read-tree --reset $i_tree
> > +			git ls-files -z --modified -- "$@" |
> > +			git checkout-index -z --force --stdin
> >  		fi
> 
> I briefly wondered if this needed "-q" to match the earlier commit, but
> "checkout-index" isn't really chatty, so I don't think so (and the
> earlier checkout-index doesn't have it either).
Yep, I had the same thoughts here.
Show 6 quoted lines
> I also wondered if this could be done in a single command as:
> 
>   git reset -q --hard $i_tree -- "$@"
> 
> But "git reset" can't handle pathspecs with "--hard" (which is why the
> similar case a few lines above uses the same commands).

Yeah unfortunately, that would have made the previous patch series quite a bit easier :) But here it wouldn't help anyway, as git reset without pathspecs can't handle a tree-ish either, right? And we also want to handle the case where no pathspecs are given to git stash.

Show 20 quoted lines
> 
> So this looks good to me.
> 
> > +test_expect_success 'stash -k -- <pathspec> leaves unstaged files intact' '
> > +	git reset &&
> > +	>foo &&
> > +	>bar &&
> > +	git add foo bar &&
> > +	git commit -m "test" &&
> > +	echo "foo" >foo &&
> > +	echo "bar" >bar &&
> > +	git stash -k -- foo &&
> > +	test "",bar = $(cat foo),$(cat bar) &&
> > +	git stash pop &&
> > +	test foo,bar = $(cat foo),$(cat bar)
> > +'
> 
> I always get nervous when I see test arguments without quotes, but I
> think this is fine (and I couldn't see a shorter way of doing it with
> test_cmp).

Hmm yeah I basically copied this from a different test and tweaked it a bit to fit here.

> -Peff
Jeff King· Mar 20, 2017, 18:41 UTC · re: Thomas Gummerer · lore

Re: [BUG] "git stash -- path" reports wrong unstaged changes

On Sat, Mar 18, 2017 at 06:36:58PM +0000, Thomas Gummerer wrote:
Show 18 quoted lines
> > +			if test -z "$GIT_QUIET" && ! git diff-files --quiet
> > +			then
> > +				say "$(gettext "Unstaged changes after reset:")"
> > +				git diff-files --name-status
> > +			fi
> >  		else
> >  			git reset --hard ${GIT_QUIET:+-q}
> >  		fi
> 
> This would mean the user gets something like in your case above:
> 
>     Unstaged changes after reset:
>      M	two
> 
> As a user that doesn't know the internal implementation of push_stash,
> this would make me wonder why git stash would mention a file that is
> not provided as pathspec, but not the one that was provided in the
> pathspec argument.

That's a good point. I was going for consistency with the non-pathspec case, but of course it wouldn't mention any files in the first place, because it's just done a complete "git reset --hard".

Show 6 quoted lines
> I think one option would be to to just keep quiet about the exact
> changes that git stash push makes, similar to what we do in the
> --include-untracked and in the -p case.  The other option would be to
> find the files that are affected and print them, but that would
> probably be a bit too noisy especially in cases such as
> git stash push -- docs/*.
Yeah, that's the right thing. I was just trying to be too clever above.
-Peff

← back to recent threads