Thanks Junio for the review in the previous round. Changes since v2: - $IFS should now be supported by using "$@" everywhere instead of using a $files variable. - Added a new patch showing the old behaviour of git stash create is preserved. - Rephrased the documentation - Simplified the option parsing in save_stash, by leaving the actual parsing to push_stash instead. Interdiff below: diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 8306bac397..be55e456fa 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -17,7 +17,7 @@ SYNOPSIS [-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>...] + [--] [<pathspec>...] 'git stash' clear 'git stash' create [<message>] 'git stash' create [-m <message>] [-u|--include-untracked <untracked|all>] @@ -51,19 +51,21 @@ 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>...]:: +push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]:: Save your local modifications to a new 'stash' and roll them - back both in the working tree and in the index. + back to HEAD (in the working tree and in the index). The <message> part is optional and gives the description along with the stashed state. For quickly making a snapshot, you can omit _both_ "save" and <message>, but giving 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. +When pathspec is given to 'git stash push', the new stash records the +modified states only for the files that match the pathspec. The index +entries and working tree files are then rolled back to the state in +HEAD only for these files, too, leaving files that do not match the +pathspec intact. + 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 0072a38b4c..46367d40aa 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 -- $1 + git ls-files -o -z $excl_opt -- "$@" } clear_stash () { @@ -59,13 +59,12 @@ create_stash () { stash_msg= untracked= new_style= - files= while test $# != 0 do case "$1" in -m|--message) shift - stash_msg="$1" + stash_msg=${1?"-m needs an argument"} new_style=t ;; -u|--include-untracked) @@ -75,7 +74,6 @@ create_stash () { ;; --) shift - files="$@" new_style=t break ;; @@ -106,6 +104,10 @@ create_stash () { if test -z "$new_style" then stash_msg="$*" + while test $# != 0 + do + shift + done fi git update-index -q --refresh @@ -141,7 +143,7 @@ create_stash () { # Untracked files are stored by themselves in a parentless commit, for # ease of unpacking later. u_commit=$( - untracked_files $files | ( + untracked_files "$@" | ( GIT_INDEX_FILE="$TMPindex" && export GIT_INDEX_FILE && rm -f "$TMPindex" && @@ -164,7 +166,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 -- $files >"$TMP-stagenames" && + git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" && git update-index -z --add --remove --stdin <"$TMP-stagenames" && git write-tree && rm -f "$TMPindex" @@ -178,7 +180,7 @@ create_stash () { # find out what the user wants GIT_INDEX_FILE="$TMP-index" \ - git add--interactive --patch=stash -- $files && + git add--interactive --patch=stash -- "$@" && # state of the working tree w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) || @@ -268,7 +270,7 @@ push_stash () { ;; -m|--message) shift - stash_msg=$1 + stash_msg=${1?"-m needs an argument"} ;; --help) show_help @@ -300,8 +302,6 @@ 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")" @@ -316,14 +316,14 @@ push_stash () { git reflog exists $ref_stash || clear_stash || die "$(gettext "Cannot initialize stash")" - create_stash -m "$stash_msg" -u "$untracked" -- $files + create_stash -m "$stash_msg" -u "$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 - if test -n "$files" + if test $# != 0 then git ls-files -z -- "$@" | xargs -0 git reset -- git ls-files -z --modified -- "$@" | xargs -0 git checkout HEAD -- @@ -334,7 +334,7 @@ push_stash () { test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION= if test -n "$untracked" then - git clean --force --quiet -d $CLEAN_X_OPTION -- $files + git clean --force --quiet -d $CLEAN_X_OPTION -- "$@" fi if test "$keep_index" = "t" && test -n "$i_tree" @@ -357,24 +357,6 @@ save_stash () { while test $# != 0 do case "$1" in - -k|--keep-index) - push_options="-k $push_options" - ;; - --no-keep-index) - push_options="--no-keep-index $push_options" - ;; - -p|--patch) - push_options="-p $push_options" - ;; - -q|--quiet) - push_options="-q $push_options" - ;; - -u|--include-untracked) - push_options="-u $push_options" - ;; - -a|--all) - push_options="-a $push_options" - ;; --help) show_help ;; @@ -383,20 +365,8 @@ save_stash () { break ;; -*) - option="$1" - # TRANSLATORS: $option is an invalid option, like - # `--blah-blah'. The 7 spaces at the beginning of the - # second line correspond to "error: ". So you should line - # up the second line with however many characters the - # translation of "error: " takes in your language. E.g. in - # English this is: - # - # $ git stash save --blah-blah 2>&1 | head -n 2 - # error: unknown option for 'stash save': --blah-blah - # To provide a message, use git stash save -- '--blah-blah' - eval_gettextln "error: unknown option for 'stash save': \$option - To provide a message, use git stash save -- '\$option'" - usage + # pass all options through to push_stash + push_options="$push_options $1" ;; *) break diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index ca4c44aa9c..461fe46045 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -784,7 +784,7 @@ test_expect_success 'push -m shows right message' ' test_cmp expect actual ' -test_expect_success 'deprecated version of stash create stores correct message' ' +test_expect_success 'create stores correct message' ' >foo && git add foo && STASH_ID=$(git stash create "create test message") && @@ -793,6 +793,15 @@ test_expect_success 'deprecated version of stash create stores correct message' test_cmp expect actual ' +test_expect_success 'create with multiple arguments for the message' ' + >foo && + 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 && + test_cmp expect actual +' + test_expect_success 'new style stash create stores correct message' ' >foo && git add foo && diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index f372fc8ca8..9a98e31a3e 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -185,4 +185,30 @@ test_expect_success 'stash save --all is stash poppable' ' test -s .gitignore ' +test_expect_success 'stash push --include-untracked with pathspec' ' + >foo && + >bar && + git stash push --include-untracked -- foo && + test_path_is_file bar && + test_path_is_missing foo && + git stash pop && + test_path_is_file bar && + test_path_is_file foo +' + +test_expect_success 'stash push with $IFS character' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash push --include-untracked -- foo* && + test_path_is_missing "foo bar" && + test_path_is_missing 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 Thomas Gummerer (5): 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 Documentation/git-stash.txt | 17 ++++- git-stash.sh | 124 +++++++++++++++++++++++++++++++++---- t/t3903-stash.sh | 78 +++++++++++++++++++++++ t/t3905-stash-include-untracked.sh | 26 ++++++++ 4 files changed, 230 insertions(+), 15 deletions(-) -- 2.12.0.rc0.208.g81c5d00b20.dirty