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

[PATCH 3/3] stash: support filename argument

From
Thomas Gummerer <t.gummerer@gmail.com>
Date
Jan 21, 2017, 20:08 UTC
Message-ID
<20170121200804.19009-4-t.gummerer@gmail.com>
In-Reply-To
<20170121200804.19009-1-t.gummerer@gmail.com>

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 an optional filename argument to git stash push, which allows for stashing a single (or multiple) files.

Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
---
 Documentation/git-stash.txt |  8 ++++++++
 git-stash.sh                | 42 ++++++++++++++++++++++++++++++++++--------
 t/t3903-stash.sh            | 27 +++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 0ad5335a3e..871a3b246c 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -15,6 +15,9 @@ SYNOPSIS
 '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]
+	     [-u|--include-untracked] [-a|--all] [-m|--message <message>]]
+	     [--] [<paths>...]
 'git stash' clear
 'git stash' create [<message>]
 'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
@@ -46,6 +49,7 @@ OPTIONS
 -------
 
 save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<paths>...]::
 
 	Save your local modifications to a new 'stash', and revert the
 	the changes in the working tree to match the index.
@@ -55,6 +59,10 @@ save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q
 	only <message> does not trigger this action to prevent a misspelled
 	subcommand from making an unwanted stash.
 +
+If the paths argument is given in 'git stash push', only these files
+are put in the new 'stash'.  In addition only the indicated files are
+changed in the working tree to match the index.
++
 If the `--keep-index` option is used, all changes already added to the
 index are left intact.
 +
diff --git a/git-stash.sh b/git-stash.sh
index d6b4ae3290..7dcce629bd 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) ||
@@ -251,6 +268,8 @@ push_stash () {
 		shift
 	done
 
+	files="$*"
+
 	if test -n "$patch_mode" && test -n "$untracked"
 	then
 		die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
@@ -265,18 +284,25 @@ push_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"
@@ -702,7 +728,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 0171b824c9..3e763ff766 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -784,4 +784,31 @@ test_expect_success 'push -m shows right message' '
 	test_cmp expect actual
 '
 
+test_expect_success 'stash -- <filename> stashes and restores the file' '
+	>foo &&
+	>bar &&
+	git add foo bar &&
+	git stash push -- 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 extra &&
+	git stash push -- foo 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.483.g087da7b7c
Previous: Thomas GummererNext: Junio C Hamano
Message 14 of 57 in “stash: support filename argument”
  1. 0/3 stash: support filename argumentThomas Gummerer, Jan 21, 2017
  2. 1/3 Documentation/stash: remove mention of git reset --hardThomas Gummerer, Jan 21, 2017
  3. Øyvind A. HolmJan 22, 2017
  4. Jakub NarębskiJan 24, 2017
  5. Jeff KingJan 24, 2017
  6. Jakub NarębskiJan 25, 2017
  7. Junio C HamanoJan 25, 2017
  8. Junio C HamanoJan 25, 2017
  9. Thomas GummererJan 28, 2017
  10. Jeff KingJan 28, 2017
  11. 2/3 stash: introduce push verbThomas Gummerer, Jan 21, 2017
  12. Junio C HamanoJan 23, 2017
  13. Thomas GummererJan 29, 2017
  14. 3/3 stash: support filename argumentThomas Gummerer, Jan 21, 2017
  15. Junio C HamanoJan 23, 2017
  16. Thomas GummererJan 29, 2017
  17. Johannes SchindelinJan 24, 2017
  18. 0/4 stash: create filename argumentThomas Gummerer, Jan 29, 2017
  19. 3/4 introduce new format for git stash createThomas Gummerer, Jan 29, 2017
  20. Junio C HamanoJan 30, 2017
  21. 2/4 stash: introduce push verbThomas Gummerer, Jan 29, 2017
  22. Junio C HamanoJan 30, 2017
  23. 1/4 Documentation/stash: remove mention of git reset --hardThomas Gummerer, Jan 29, 2017
  24. Junio C HamanoJan 30, 2017
  25. Thomas GummererFeb 5, 2017
  26. 4/4 stash: support filename argumentThomas Gummerer, Jan 29, 2017
  27. Junio C HamanoJan 30, 2017
  28. Thomas GummererFeb 5, 2017
  29. 0/5 stash: support pathspec argumentThomas Gummerer, Feb 5, 2017
  30. 2/5 stash: introduce push verbThomas Gummerer, Feb 5, 2017
  31. Jeff KingFeb 6, 2017
  32. Thomas GummererFeb 11, 2017
  33. 4/5 stash: introduce new format createThomas Gummerer, Feb 5, 2017
  34. Jeff KingFeb 6, 2017
  35. Thomas GummererFeb 11, 2017
  36. Jeff KingFeb 13, 2017
  37. Jeff KingFeb 13, 2017
  38. Thomas GummererFeb 14, 2017
  39. 3/5 stash: add test for the create command line argumentsThomas Gummerer, Feb 5, 2017
  40. Jeff KingFeb 6, 2017
  41. Thomas GummererFeb 11, 2017
  42. 1/5 Documentation/stash: remove mention of git reset --hardThomas Gummerer, Feb 5, 2017
  43. Jeff KingFeb 6, 2017
  44. 5/5 stash: teach 'push' (and 'create') to honor pathspecThomas Gummerer, Feb 5, 2017
  45. Jeff KingFeb 6, 2017
  46. Thomas GummererFeb 12, 2017
  47. Thomas GummererFeb 4, 2017
  48. Thomas GummererFeb 4, 2017
  49. Jeff KingFeb 6, 2017
  50. Thomas GummererFeb 11, 2017
  51. Jeff KingFeb 13, 2017
  52. Jeff KingFeb 13, 2017
  53. Thomas GummererFeb 13, 2017
  54. Thomas GummererFeb 13, 2017
  55. Jeff KingFeb 14, 2017
  56. Jeff KingFeb 14, 2017
  57. Thomas GummererFeb 14, 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.