threads / rfc / 44884

[RFC] stash: support filename argument

Subject: [RFC] stash: support filename argument

## tl;dr

8 messages between Jan 15, 2017 and Jan 16, 2017.

replies: 7people: 6as markdown or json

Thomas Gummerer· Jan 15, 2017, 14:25 UTC · lore

While working on a repository, it's often helpful to stash the changes of a single or multiple files, and leave others alone. Unfortunately git currently offers no such option. git stash -p can be used to work around this, but it's often impractical when there are a lot of changes over multiple files.

Add a --file option to git stash save, which allows for stashing a single file. Specifying the --file argument multiple times allows stashing more than one file at a time.

Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---

Marked as RFC and without documentation updates to first get a feeling for the user interface, and whether people are interested in this change.

Ideally I wanted the the user interface to look like something like: git stash save -- [<filename1,...>], but unfortunately that's already taken up by the stash message. So to preserve backward compatibility I used the new --file argument.

 git-stash.sh     | 45 +++++++++++++++++++++++++++++++++++++--------
 t/t3903-stash.sh | 27 +++++++++++++++++++++++++++
 2 files changed, 64 insertions(+), 8 deletions(-)
diff --git a/git-stash.sh b/git-stash.sh
index 10c284d1aa..0ef1b5b56e 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -41,7 +41,7 @@ no_changes () {
 untracked_files () {
 	excl_opt=--exclude-standard
 	test "$untracked" = "all" && excl_opt=
-	git ls-files -o -z $excl_opt
+	git ls-files -o -z $excl_opt -- $1
 }
 
 clear_stash () {
@@ -56,6 +56,23 @@ clear_stash () {
 }
 
 create_stash () {
+	files=
+	while test $# != 0
+	do
+		case "$1" in
+		--)
+			shift
+			break
+			;;
+		--files)
+			;;
+		*)
+			files="$1 $files"
+			;;
+		esac
+		shift
+	done
+
 	stash_msg="$1"
 	untracked="$2"
 
@@ -92,7 +109,7 @@ create_stash () {
 		# Untracked files are stored by themselves in a parentless commit, for
 		# ease of unpacking later.
 		u_commit=$(
-			untracked_files | (
+			untracked_files $files | (
 				GIT_INDEX_FILE="$TMPindex" &&
 				export GIT_INDEX_FILE &&
 				rm -f "$TMPindex" &&
@@ -115,7 +132,7 @@ create_stash () {
 			git read-tree --index-output="$TMPindex" -m $i_tree &&
 			GIT_INDEX_FILE="$TMPindex" &&
 			export GIT_INDEX_FILE &&
-			git diff-index --name-only -z HEAD -- >"$TMP-stagenames" &&
+			git diff-index --name-only -z HEAD -- $files >"$TMP-stagenames" &&
 			git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
 			git write-tree &&
 			rm -f "$TMPindex"
@@ -129,7 +146,7 @@ create_stash () {
 
 		# find out what the user wants
 		GIT_INDEX_FILE="$TMP-index" \
-			git add--interactive --patch=stash -- &&
+			git add--interactive --patch=stash -- $files &&
 
 		# state of the working tree
 		w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
@@ -193,6 +210,7 @@ save_stash () {
 	keep_index=
 	patch_mode=
 	untracked=
+	files=
 	while test $# != 0
 	do
 		case "$1" in
@@ -216,6 +234,10 @@ save_stash () {
 		-a|--all)
 			untracked=all
 			;;
+		--file)
+			shift
+			files="$files $1"
+			;;
 		--help)
 			show_help
 			;;
@@ -262,18 +284,25 @@ save_stash () {
 	git reflog exists $ref_stash ||
 		clear_stash || die "$(gettext "Cannot initialize stash")"
 
-	create_stash "$stash_msg" $untracked
+	create_stash --files $files -- "$stash_msg" "$untracked"
 	store_stash -m "$stash_msg" -q $w_commit ||
 	die "$(gettext "Cannot save the current status")"
 	say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
 
 	if test -z "$patch_mode"
 	then
-		git reset --hard ${GIT_QUIET:+-q}
+		if test -n "$files"
+		then
+			git reset -- $files
+			git checkout HEAD -- $(git ls-files --modified -- $files)
+			git clean --force --quiet -- $(git ls-files --others -- $files)
+		else
+			git reset --hard ${GIT_QUIET:+-q}
+		fi
 		test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
 		if test -n "$untracked"
 		then
-			git clean --force --quiet -d $CLEAN_X_OPTION
+			git clean --force --quiet -d $CLEAN_X_OPTION -- $files
 		fi
 
 		if test "$keep_index" = "t" && test -n "$i_tree"
@@ -627,7 +656,7 @@ clear)
 	;;
 create)
 	shift
-	create_stash "$*" && echo "$w_commit"
+	create_stash -- "$*" && echo "$w_commit"
 	;;
 store)
 	shift
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 2de3e18ce6..42bfca873b 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -775,4 +775,31 @@ test_expect_success 'stash is not confused by partial renames' '
 	test_path_is_missing file
 '
 
+test_expect_success 'stash --file <filename> stashes and restores the file' '
+	>foo &&
+	>bar &&
+	git add foo bar &&
+	git stash save --file foo &&
+	test_path_is_file bar &&
+	test_path_is_missing foo &&
+	git stash pop &&
+	test_path_is_file foo &&
+	test_path_is_file bar
+'
+
+test_expect_success 'stash with multiple filename arguments' '
+	>foo &&
+	>bar &&
+	>extra &&
+	git add foo bar &&
+	git stash save --file foo --file bar &&
+	test_path_is_missing bar &&
+	test_path_is_missing foo &&
+	test_path_is_file extra &&
+	git stash pop &&
+	test_path_is_file foo &&
+	test_path_is_file bar &&
+	test_path_is_file extra
+'
+
 test_done
-- 
2.11.0.258.ge05806da9e.dirty
Junio C Hamano· Jan 16, 2017, 00:21 UTC · re: Thomas Gummerer · lore

Re: [RFC] stash: support filename argument

Thomas Gummerer <t.gummerer@gmail.com> writes:
Show 21 quoted lines
> While working on a repository, it's often helpful to stash the changes
> of a single or multiple files, and leave others alone.  Unfortunately
> git currently offers no such option.  git stash -p can be used to work
> around this, but it's often impractical when there are a lot of changes
> over multiple files.
>
> Add a --file option to git stash save, which allows for stashing a
> single file.  Specifying the --file argument multiple times allows
> stashing more than one file at a time.
>
> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
> ---
>
> Marked as RFC and without documentation updates to first get a feeling
> for the user interface, and whether people are interested in this
> change.
>
> Ideally I wanted the the user interface to look like something like:
> git stash save -- [<filename1,...>], but unfortunately that's already
> taken up by the stash message.  So to preserve backward compatibility
> I used the new --file argument.

I haven't spent enough time to think if it even makes sense to "stash" partially, leaving the working tree still dirty. My initial reaction was "then stashing away the dirty WIP state to get a spiffy clean working environment becomes impossible", and I still need time to recover from that ;-) So as to the desirablity of this "feature", I have no strong opinion for or against yet.

But if we were to do this, then we should bite the bullet and declare that "stash save <message>" was a mistake. It should have been "stash save -m <message>" and we should transition to that (one easy way out would be to find another verb that is not 'save').

Then we can do "git stash save [-m <msg>] [--] [pathspec...]" which follows the usual command line convention.

Stephan Beyer· Jan 16, 2017, 00:41 UTC · re: Junio C Hamano · lore

Re: [RFC] stash: support filename argument

Hi,
On 01/16/2017 01:21 AM, Junio C Hamano wrote:
Show 6 quoted lines
> I haven't spent enough time to think if it even makes sense to
> "stash" partially, leaving the working tree still dirty.  My initial
> reaction was "then stashing away the dirty WIP state to get a spiffy
> clean working environment becomes impossible", and I still need time
> to recover from that ;-)  So as to the desirablity of this "feature",
> I have no strong opinion for or against yet.

I do remember that I simulated that feature a few times (either by adding the to-be-keep stuff (hunks, not only files) to the index and use --keep-index, and sometimes by making a temporary commit (to make sure to not lose anything) and then stash). So I think there is a valid desire of the feature.

However, going further, the next feature to be requested could be "git stash --patch" ;D This leads me to think that the mentioned simulation of partial stashes might be something everyone might come up with who has basic understanding of git features and "git stash", and might be the more flexible and probably better solution to the problem.

Best
  Stephan
Marc Strapetz· Jan 16, 2017, 08:18 UTC · re: Stephan Beyer · lore

Re: [RFC] stash: support filename argument

On 16.01.2017 01:41, Stephan Beyer wrote:
Show 14 quoted lines
> Hi,
>
> On 01/16/2017 01:21 AM, Junio C Hamano wrote:
>> I haven't spent enough time to think if it even makes sense to
>> "stash" partially, leaving the working tree still dirty.  My initial
>> reaction was "then stashing away the dirty WIP state to get a spiffy
>> clean working environment becomes impossible", and I still need time
>> to recover from that ;-)  So as to the desirablity of this "feature",
>> I have no strong opinion for or against yet.
> I do remember that I simulated that feature a few times (either by
> adding the to-be-keep stuff (hunks, not only files) to the index and use
> --keep-index, and sometimes by making a temporary commit (to make sure
> to not lose anything) and then stash). So I think there is a valid
> desire of the feature.

I can confirm this from a GUI client perspective, for which this feature makes probably even more sense than for command line. It has been requested by our users quite often compared to other features and compared to "git stash -p" support.

-Marc
Jeff King· Jan 16, 2017, 23:49 UTC · re: Stephan Beyer · lore

Re: [RFC] stash: support filename argument

On Mon, Jan 16, 2017 at 01:41:02AM +0100, Stephan Beyer wrote:
Show 5 quoted lines
> However, going further, the next feature to be requested could be "git
> stash --patch" ;D This leads me to think that the mentioned simulation
> of partial stashes might be something everyone might come up with who
> has basic understanding of git features and "git stash", and might be
> the more flexible and probably better solution to the problem.
Don't we have "git stash -p" already?[1]

I use it all the time, and it's very handy for picking apart changes. I have often wanted "git stash -p <paths>" to work, though.

-Peff
[1] I had to double check that it was not something I hacked together
    locally and forgot about. :) It's from dda1f2a5c (Implement 'git
    stash save --patch', 2009-08-13). I also worked up a "git stash
    apply -p", but I remember it was buggy, and I haven't looked at it
    in years. It's at:
      https://github.com/peff/git/commit/2c4ed43987b20cfb1605fbd7648d81e454c45c71
    if anybody is curious. I don't even recall what the problems were at
    this point.
Johannes Schindelin· Jan 16, 2017, 10:51 UTC · re: Junio C Hamano · lore

Re: [RFC] stash: support filename argument

Hi Junio,
On Sun, 15 Jan 2017, Junio C Hamano wrote:
> I haven't spent enough time to think if it even makes sense to
> "stash" partially, leaving the working tree still dirty.

Think no more! We already support that with --keep-index, and it is a very useful feature.

Ciao, Johannes

Johannes Schindelin· Jan 16, 2017, 13:14 UTC · re: Johannes Schindelin · lore

Re: [RFC] stash: support filename argument

Hi,
On Mon, 16 Jan 2017, Johannes Schindelin wrote:
Show 7 quoted lines
> On Sun, 15 Jan 2017, Junio C Hamano wrote:
> 
> > I haven't spent enough time to think if it even makes sense to
> > "stash" partially, leaving the working tree still dirty.
> 
> Think no more! We already support that with --keep-index, and it is a very
> useful feature.

And of course there is `git stash -p`, which is also very useful, *because* it leaves the working tree still dirty, in the desired way.

Ciao, Johannes

Thomas Gummerer· Jan 16, 2017, 22:29 UTC · re: Junio C Hamano · lore

Re: [RFC] stash: support filename argument

On 01/15, Junio C Hamano wrote:
Show 30 quoted lines
> Thomas Gummerer <t.gummerer@gmail.com> writes:
> 
> > While working on a repository, it's often helpful to stash the changes
> > of a single or multiple files, and leave others alone.  Unfortunately
> > git currently offers no such option.  git stash -p can be used to work
> > around this, but it's often impractical when there are a lot of changes
> > over multiple files.
> >
> > Add a --file option to git stash save, which allows for stashing a
> > single file.  Specifying the --file argument multiple times allows
> > stashing more than one file at a time.
> >
> > Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
> > ---
> >
> > Marked as RFC and without documentation updates to first get a feeling
> > for the user interface, and whether people are interested in this
> > change.
> >
> > Ideally I wanted the the user interface to look like something like:
> > git stash save -- [<filename1,...>], but unfortunately that's already
> > taken up by the stash message.  So to preserve backward compatibility
> > I used the new --file argument.
> 
> I haven't spent enough time to think if it even makes sense to
> "stash" partially, leaving the working tree still dirty.  My initial
> reaction was "then stashing away the dirty WIP state to get a spiffy
> clean working environment becomes impossible", and I still need time
> to recover from that ;-)  So as to the desirablity of this "feature",
> I have no strong opinion for or against yet.

As others mentioned, this would be similar to stash -p. My main usecase is stashing away a config file or changes in one test file while I forget to test something without these changes. I definitely used git stash -p on more than one occasion to simulate this behaviour before.

Show 7 quoted lines
> But if we were to do this, then we should bite the bullet and
> declare that "stash save <message>" was a mistake.  It should have
> been "stash save -m <message>" and we should transition to that (one
> easy way out would be to find another verb that is not 'save').
>
> Then we can do "git stash save [-m <msg>] [--] [pathspec...]" which
> follows the usual command line convention.

I like this interface much better than the one in my patch. I'm not sure about the verb we could use for the transition, maybe 'push'. If anyone can come up with a different suggestion I'd be happy to hear it, as I'm not very good at naming things.

-- 
Thomas

← back to recent threads