git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v4 0/7] stash: support pathspec argument

From
Thomas Gummerer <t.gummerer@gmail.com>
Date
Feb 12, 2017, 21:54 UTC
Message-ID
<20170212215420.16701-1-t.gummerer@gmail.com>
In-Reply-To
<mailto:20170205202642.14216-1-t.gummerer@gmail.com>
Thanks Peff and Junio for the review of the last round.
Changes since v3:
- Instead of using ${1-...} to check for missing arguments and dying
  show the usage for stash instead, being more consistent with how the
  rest of stash behaves
- Let push_stash handle the --help argument from save_stash as well,
  passing it through directly.
- Fix the tests to not pipe the output to something, and thereby
  swallowing the error codes
- Update the commit message in 4/7 to acknowledge that there is a
  small regression between the two versions
- Correctly detect when there are no changes, and only show the
  message once
- Fix the accidental deletion of untracked files when a pathspec is
  used.  Also added test cases for that.
- Add documentation for git stash push to the usage text
- Respect the -q flag and the GIT_QUIET environment variable
- This also adds two new patches, one using push_stash instead of
  save_stash for git stash without any verb.  The other allows using a
  pathspec in the verb-less option of git stash.
Interdiff below:
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index be55e456fa..b0825f4aca 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -13,15 +13,15 @@ SYNOPSIS
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
-	     [-u|--include-untracked] [-a|--all] [<message>]]
-'git stash' push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
+'git stash' save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
+	     [-u|--include-untracked] [-a|--all] [<message>]
+'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
 	     [-u|--include-untracked] [-a|--all] [-m|--message <message>]]
-	     [--] [<pathspec>...]
+	     [--] [<pathspec>...]]
 'git stash' clear
 'git stash' create [<message>]
 'git stash' create [-m <message>] [-u|--include-untracked <untracked|all>]
-	     [-- <paths>...]
+	     [-- <pathspec>...]
 'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
 
 DESCRIPTION
diff --git a/git-stash.sh b/git-stash.sh
index 46367d40aa..a184b1e274 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -7,8 +7,11 @@ USAGE="list [<options>]
    or: $dashless drop [-q|--quiet] [<stash>]
    or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
    or: $dashless branch <branchname> [<stash>]
-   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
-		       [-u|--include-untracked] [-a|--all] [<message>]]
+   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+		      [-u|--include-untracked] [-a|--all] [<message>]
+   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+		       [-u|--include-untracked] [-a|--all] [-m <message>]
+		       [-- <pathspec>...]]
    or: $dashless clear"
 
 SUBDIRECTORY_OK=Yes
@@ -33,8 +36,8 @@ else
 fi
 
 no_changes () {
-	git diff-index --quiet --cached HEAD --ignore-submodules -- &&
-	git diff-files --quiet --ignore-submodules &&
+	git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" &&
+	git diff-files --quiet --ignore-submodules -- "$@" &&
 	(test -z "$untracked" || test -z "$(untracked_files)")
 }
 
@@ -64,11 +67,13 @@ create_stash () {
 		case "$1" in
 		-m|--message)
 			shift
-			stash_msg=${1?"-m needs an argument"}
+			test -z ${1+x} && usage
+			stash_msg="$1"
 			new_style=t
 			;;
 		-u|--include-untracked)
 			shift
+			test -z ${1+x} && usage
 			untracked="$1"
 			new_style=t
 			;;
@@ -104,14 +109,11 @@ create_stash () {
 	if test -z "$new_style"
 	then
 		stash_msg="$*"
-		while test $# != 0
-		do
-			shift
-		done
+		set --
 	fi
 
 	git update-index -q --refresh
-	if no_changes
+	if no_changes "$@"
 	then
 		exit 0
 	fi
@@ -270,7 +272,8 @@ push_stash () {
 			;;
 		-m|--message)
 			shift
-			stash_msg=${1?"-m needs an argument"}
+			test -z ${1+x} && usage
+			stash_msg=$1
 			;;
 		--help)
 			show_help
@@ -308,7 +311,7 @@ push_stash () {
 	fi
 
 	git update-index -q --refresh
-	if no_changes
+	if no_changes "$@"
 	then
 		say "$(gettext "No local changes to save")"
 		exit 0
@@ -325,9 +328,23 @@ push_stash () {
 	then
 		if test $# != 0
 		then
-			git ls-files -z -- "$@" | xargs -0 git reset --
-			git ls-files -z --modified -- "$@" | xargs -0 git checkout HEAD --
-			git ls-files -z --others -- "$@" | xargs -0 git clean --force --
+			saved_untracked=
+			if test -n "$(git ls-files --others -- "$@")"
+			then
+				saved_untracked=$(
+					git ls-files -z --others -- "$@" |
+					    xargs -0 git stash create -u all --)
+			fi
+			git ls-files -z -- "$@" | xargs -0 git reset ${GIT_QUIET:+-q} --
+			git ls-files -z --modified -- "$@" | xargs -0 git checkout ${GIT_QUIET:+-q} HEAD --
+			if test -n "$(git ls-files -z --others -- "$@")"
+			then
+				git ls-files -z --others -- "$@" | xargs -0 git clean --force -d ${GIT_QUIET:+-q} --
+			fi
+			if test -n "$saved_untracked"
+			then
+				git stash pop -q $saved_untracked
+			fi
 		else
 			git reset --hard ${GIT_QUIET:+-q}
 		fi
@@ -357,9 +374,6 @@ save_stash () {
 	while test $# != 0
 	do
 		case "$1" in
-		--help)
-			show_help
-			;;
 		--)
 			shift
 			break
@@ -685,17 +699,18 @@ apply_to_branch () {
 }
 
 PARSE_CACHE='--not-parsed'
-# The default command is "save" if nothing but options are given
+# The default command is "push" if nothing but options are given
 seen_non_option=
 for opt
 do
 	case "$opt" in
+	--) break ;;
 	-*) ;;
 	*) seen_non_option=t; break ;;
 	esac
 done
 
-test -n "$seen_non_option" || set "save" "$@"
+test -n "$seen_non_option" || set "push" "$@"
 
 # Main command set
 case "$1" in
@@ -746,7 +761,7 @@ branch)
 *)
 	case $# in
 	0)
-		save_stash &&
+		push_stash &&
 		say "$(gettext "(To restore them type \"git stash apply\")")"
 		;;
 	*)
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 461fe46045..22ac75377b 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -274,9 +274,7 @@ test_expect_success 'stash --invalid-option' '
 	git add file2 &&
 	test_must_fail git stash --invalid-option &&
 	test_must_fail git stash save --invalid-option &&
-	test bar5,bar6 = $(cat file),$(cat file2) &&
-	git stash -- -message-starting-with-dash &&
-	test bar,bar2 = $(cat file),$(cat file2)
+	test bar5,bar6 = $(cat file),$(cat file2)
 '
 
 test_expect_success 'stash an added file' '
@@ -780,7 +778,7 @@ test_expect_success 'push -m shows right message' '
 	git add foo &&
 	git stash push -m "test message" &&
 	echo "stash@{0}: On master: test message" >expect &&
-	git stash list | head -n 1 >actual &&
+	git stash list -1 >actual &&
 	test_cmp expect actual
 '
 
@@ -789,7 +787,7 @@ test_expect_success 'create stores correct message' '
 	git add foo &&
 	STASH_ID=$(git stash create "create test message") &&
 	echo "On master: create test message" >expect &&
-	git show --pretty=%s ${STASH_ID} | head -n1 >actual &&
+	git show --pretty=%s -s ${STASH_ID} >actual &&
 	test_cmp expect actual
 '
 
@@ -798,7 +796,7 @@ test_expect_success 'create with multiple arguments for the message' '
 	git add foo &&
 	STASH_ID=$(git stash create test untracked) &&
 	echo "On master: test untracked" >expect &&
-	git show --pretty=%s ${STASH_ID} | head -n1 >actual &&
+	git show --pretty=%s -s ${STASH_ID} >actual &&
 	test_cmp expect actual
 '
 
@@ -807,7 +805,7 @@ test_expect_success 'new style stash create stores correct message' '
 	git add foo &&
 	STASH_ID=$(git stash create -m "create test message new style") &&
 	echo "On master: create test message new style" >expect &&
-	git show --pretty=%s ${STASH_ID} | head -n1 >actual &&
+	git show --pretty=%s -s ${STASH_ID} >actual &&
 	test_cmp expect actual
 '
 
@@ -839,18 +837,63 @@ test_expect_success 'stash with multiple filename arguments' '
 '
 
 test_expect_success 'stash with file including $IFS character' '
-	>"foo	bar" &&
+	>"foo bar" &&
 	>foo &&
-	>untracked &&
+	>bar &&
 	git add foo* &&
-	git stash push -- foo* &&
-	test_path_is_missing "foo	bar" &&
-	test_path_is_missing foo &&
-	test_path_is_file untracked &&
+	git stash push -- "foo b*" &&
+	test_path_is_missing "foo bar" &&
+	test_path_is_file foo &&
+	test_path_is_file bar &&
 	git stash pop &&
-	test_path_is_file "foo	bar" &&
+	test_path_is_file "foo bar" &&
 	test_path_is_file foo &&
+	test_path_is_file bar
+'
+
+test_expect_success 'stash push -p with pathspec shows no changes only onece' '
+	>file &&
+	git add file &&
+	git stash push -p not-file >actual &&
+	echo "No local changes to save" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stash push with pathspec shows no changes when there are none' '
+	>file &&
+	git add file &&
+	git stash push not-file >actual &&
+	echo "No local changes to save" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'untracked file is not removed when using pathspecs' '
+	>untracked &&
+	git stash push untracked &&
+	test_path_is_file untracked
+'
+
+test_expect_success 'untracked files are left in place when -u is not given' '
+	>file &&
+	git add file &&
+	>untracked &&
+	git stash push file &&
 	test_path_is_file untracked
 '
 
+test_expect_success 'stash without verb with pathspec' '
+	>"foo bar" &&
+	>foo &&
+	>bar &&
+	git add foo* &&
+	git stash -- "foo b*" &&
+	test_path_is_missing "foo bar" &&
+	test_path_is_file foo &&
+	test_path_is_file bar &&
+	git stash pop &&
+	test_path_is_file "foo bar" &&
+	test_path_is_file foo &&
+	test_path_is_file bar
+'
+
 test_done
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index 9a98e31a3e..193adc7b68 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -197,16 +197,16 @@ test_expect_success 'stash push --include-untracked with pathspec' '
 '
 
 test_expect_success 'stash push with $IFS character' '
-	>"foo	bar" &&
+	>"foo bar" &&
 	>foo &&
 	>bar &&
 	git add foo* &&
-	git stash push --include-untracked -- foo* &&
-	test_path_is_missing "foo	bar" &&
-	test_path_is_missing foo &&
+	git stash push --include-untracked -- "foo b*" &&
+	test_path_is_missing "foo bar" &&
+	test_path_is_file foo &&
 	test_path_is_file bar &&
 	git stash pop &&
-	test_path_is_file "foo	bar" &&
+	test_path_is_file "foo bar" &&
 	test_path_is_file foo &&
 	test_path_is_file bar
 '

Thomas Gummerer (7):
  Documentation/stash: remove mention of git reset --hard
  stash: introduce push verb
  stash: add test for the create command line arguments
  stash: introduce new format create
  stash: teach 'push' (and 'create') to honor pathspec
  stash: use stash_push for no verb form
  stash: allow pathspecs in the no verb form

 Documentation/git-stash.txt        |  21 ++++-
 git-stash.sh                       | 157 +++++++++++++++++++++++++++++++------
 t/t3903-stash.sh                   | 127 +++++++++++++++++++++++++++++-
 t/t3905-stash-include-untracked.sh |  26 ++++++
 4 files changed, 302 insertions(+), 29 deletions(-)
-- 
2.11.0.301.g86e6ecc671.dirty
Next: Thomas Gummerer
Message 1 of 61 in “stash: support pathspec argument”
  1. 0/7 stash: support pathspec argumentThomas Gummerer, Feb 12, 2017
  2. 1/7 Documentation/stash: remove mention of git reset --hardThomas Gummerer, Feb 12, 2017
  3. 2/7 stash: introduce push verbThomas Gummerer, Feb 12, 2017
  4. 6/7 stash: use stash_push for no verb formThomas Gummerer, Feb 12, 2017
  5. 5/7 stash: teach 'push' (and 'create') to honor pathspecThomas Gummerer, Feb 12, 2017
  6. 4/7 stash: introduce new format createThomas Gummerer, Feb 12, 2017
  7. 3/7 stash: add test for the create command line argumentsThomas Gummerer, Feb 12, 2017
  8. 7/7 stash: allow pathspecs in the no verb formThomas Gummerer, Feb 12, 2017
  9. Thomas GummererFeb 12, 2017
  10. 0/6 stash: support pathspec argumentThomas Gummerer, Feb 17, 2017
  11. 2/6 stash: add test for the create command line argumentsThomas Gummerer, Feb 17, 2017
  12. 4/6 stash: teach 'push' (and 'create_stash') to honor pathspecThomas Gummerer, Feb 17, 2017
  13. 5/6 stash: use stash_push for no verb formThomas Gummerer, Feb 17, 2017
  14. 6/6 stash: allow pathspecs in the no verb formThomas Gummerer, Feb 17, 2017
  15. Jeff KingFeb 17, 2017
  16. Thomas GummererFeb 19, 2017
  17. 1/6 stash: introduce push verbThomas Gummerer, Feb 17, 2017
  18. 3/6 stash: refactor stash_createThomas Gummerer, Feb 17, 2017
  19. Jeff KingFeb 17, 2017
  20. Thomas GummererFeb 19, 2017
  21. Junio C HamanoFeb 17, 2017
  22. Thomas GummererFeb 17, 2017
  23. Junio C HamanoFeb 17, 2017
  24. 0/6 stash: support pathspec argumentThomas Gummerer, Feb 19, 2017
  25. 5/6 stash: use stash_push for no verb formThomas Gummerer, Feb 19, 2017
  26. 2/6 stash: add test for the create command line argumentsThomas Gummerer, Feb 19, 2017
  27. 3/6 stash: refactor stash_createThomas Gummerer, Feb 19, 2017
  28. 1/6 stash: introduce push verbThomas Gummerer, Feb 19, 2017
  29. 4/6 stash: teach 'push' (and 'create_stash') to honor pathspecThomas Gummerer, Feb 19, 2017
  30. Junio C HamanoFeb 21, 2017
  31. Thomas GummererFeb 25, 2017
  32. 6/6 stash: allow pathspecs in the no verb formThomas Gummerer, Feb 19, 2017
  33. Jeff KingFeb 20, 2017
  34. Junio C HamanoFeb 20, 2017
  35. Junio C HamanoFeb 20, 2017
  36. Thomas GummererFeb 25, 2017
  37. 0/6 stash: support pathspec argumentThomas Gummerer, Feb 25, 2017
  38. 2/6 stash: add test for the create command line argumentsThomas Gummerer, Feb 25, 2017
  39. 3/6 stash: refactor stash_createThomas Gummerer, Feb 25, 2017
  40. 6/6 stash: allow pathspecs in the no verb formThomas Gummerer, Feb 25, 2017
  41. 1/6 stash: introduce push verbThomas Gummerer, Feb 25, 2017
  42. 4/6 stash: teach 'push' (and 'create_stash') to honor pathspecThomas Gummerer, Feb 25, 2017
  43. Junio C HamanoFeb 27, 2017
  44. Thomas GummererFeb 27, 2017
  45. Junio C HamanoFeb 27, 2017
  46. Junio C HamanoFeb 27, 2017
  47. Junio C HamanoFeb 27, 2017
  48. Thomas GummererFeb 27, 2017
  49. 5/6 stash: use stash_push for no verb formThomas Gummerer, Feb 25, 2017
  50. 0/6 stash: support pathspec argumentThomas Gummerer, Feb 28, 2017
  51. 3/6 stash: refactor stash_createThomas Gummerer, Feb 28, 2017
  52. 6/6 stash: allow pathspecs in the no verb formThomas Gummerer, Feb 28, 2017
  53. 1/6 stash: introduce push verbThomas Gummerer, Feb 28, 2017
  54. 4/6 stash: teach 'push' (and 'create_stash') to honor pathspecThomas Gummerer, Feb 28, 2017
  55. Junio C HamanoFeb 28, 2017
  56. Thomas GummererMar 1, 2017
  57. Junio C HamanoMar 1, 2017
  58. 2/6 stash: add test for the create command line argumentsThomas Gummerer, Feb 28, 2017
  59. 5/6 stash: use stash_push for no verb formThomas Gummerer, Feb 28, 2017
  60. Thomas GummererFeb 14, 2017
  61. Thomas GummererFeb 17, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.