{"thread":{"id":"37006","subject":"[PATCH] stash create: Add --include-untracked and --all option to stash create","startedAt":"2014-06-27T08:07:59Z","lastAt":"2014-06-27T17:37:24Z","messageCount":2,"participants":["shigeta@kanamei.co.jp","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"245056","messageId":"1403856479-37421-1-git-send-email-shigeta@kanamei.co.jp","threadId":"37006","inReplyTo":null,"subject":"[PATCH] stash create: Add --include-untracked and --all option to stash create","fromName":"","fromEmail":"shigeta@kanamei.co.jp","sentAt":"2014-06-27T08:07:59Z","receivedAt":"2014-06-27T08:07:59Z","isPatch":true,"sender":{"key":"shigeta@kanamei.co.jp","avatar":null},"body":"From: Kazumasa Shigeta <shigeta@kanamei.co.jp>\n\nThe --include-untracked option acts like the normal \"git stash save --include-untracked --all\" but it does not change anything in working directory.\nIt is inconvenient stash create does not have --include-untracked option however stash save has --include-untracked option.\nIt fails when using message that start with dash. It is not compatible with now.\n\nSigned-off-by: Kazumasa Shigeta <shigeta@kanamei.co.jp>\n---\n Documentation/git-stash.txt               |   4 +-\n git-stash.sh                              |  52 +++++-\n t/t3907-stash-create-include-untracked.sh | 286 ++++++++++++++++++++++++++++++\n 3 files changed, 336 insertions(+), 6 deletions(-)\n create mode 100755 t/t3907-stash-create-include-untracked.sh\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 375213f..7ff9ce6 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -16,7 +16,7 @@ SYNOPSIS\n 'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n \t     [-u|--include-untracked] [-a|--all] [<message>]]\n 'git stash' clear\n-'git stash' create [<message>]\n+'git stash' create [-u|--include-untracked] [-a|--all] [<message>]\n 'git stash' store [-m|--message <message>] [-q|--quiet] <commit>\n \n DESCRIPTION\n@@ -148,7 +148,7 @@ drop [-q|--quiet] [<stash>]::\n \t`<stash>` must be a valid stash log reference of the form\n \t`stash@{<revision>}`.\n \n-create::\n+create [-u|--include-untracked] [-a|--all] [<message>]::\n \n \tCreate a stash (which is a regular commit object) and return its\n \tobject name, without storing it anywhere in the ref namespace.\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 4798bcf..d636651 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -57,8 +57,35 @@ clear_stash () {\n }\n \n create_stash () {\n-\tstash_msg=\"$1\"\n-\tuntracked=\"$2\"\n+    stash_msg=\n+    untracked=\n+\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-u|--include-untracked)\n+\t\t\tuntracked=untracked\n+\t\t\t;;\n+\t\t-a|--all)\n+\t\t\tuntracked=all\n+\t\t\t;;\n+\t\t--)\n+\t\t\tshift\n+\t\t\tbreak\n+\t\t\t;;\n+\t\t-*)\n+\t\t\toption=\"$1\"\n+\t\t\teval_gettextln \"error: unknown option for 'stash create': \\$option\n+       To provide a message, use git stash create -- '\\$option'\"\n+\t\t\texit 1\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\tstash_msg=\"$*\"\n \n \tgit update-index -q --refresh\n \tif no_changes\n@@ -260,8 +287,25 @@ save_stash () {\n \tfi\n \ttest -f \"$GIT_DIR/logs/$ref_stash\" ||\n \t\tclear_stash || die \"$(gettext \"Cannot initialize stash\")\"\n+    untracked_opt=\n+    case \"$untracked\" in\n+\t\tuntracked)\n+\t\t\tuntracked_opt=\"--include-untracked\"\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tall)\n+\t\t\tuntracked_opt=\"--all\"\n+\t\t\tbreak\n+\t\t\t;;\n+    esac\n+\n+\tif test -z \"$untracked_opt\"\n+\tthen\n+\t\tcreate_stash  -- \"$stash_msg\"\n+\telse\n+\t\tcreate_stash \"$untracked_opt\" -- \"$stash_msg\"\n+\tfi\n \n-\tcreate_stash \"$stash_msg\" $untracked\n \tstore_stash -m \"$stash_msg\" -q $w_commit ||\n \tdie \"$(gettext \"Cannot save the current status\")\"\n \tsay Saved working directory and index state \"$stash_msg\"\n@@ -584,7 +628,7 @@ clear)\n \t;;\n create)\n \tshift\n-\tcreate_stash \"$*\" && echo \"$w_commit\"\n+\tcreate_stash \"$@\" && echo \"$w_commit\"\n \t;;\n store)\n \tshift\ndiff --git a/t/t3907-stash-create-include-untracked.sh b/t/t3907-stash-create-include-untracked.sh\nnew file mode 100755\nindex 0000000..c28ed0f\n--- /dev/null\n+++ b/t/t3907-stash-create-include-untracked.sh\n@@ -0,0 +1,286 @@\n+#!/bin/sh\n+#\n+# File:   t3907-stash-create-include-untracked.sh\n+# Author: shigeta\n+#\n+# Created on 2014/05/01, 13:13:15\n+\n+test_description='Test git stash create --include-untracked and --all'\n+\n+. ./test-lib.sh\n+\n+cat > .gitignore <<EOF\n+.gitignore\n+ignored\n+ignored.d/\n+EOF\n+\n+STASH_ID_U=\n+STASH_ID__INCLUDE_UNTRACKED=\n+STASH_ID_A=\n+STASH_ID__ALL=\n+\n+test_expect_success 'stash create --include-untracked and --all some dirty working directory' '\n+\techo 1 > file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m initial &&\n+\techo 2 > file &&\n+\tgit add file &&\n+\techo 3 > file &&\n+\ttest_tick &&\n+\techo 1 > file2 &&\n+\techo 1 > HEAD &&\n+\tmkdir untracked &&\n+\techo untracked >untracked/untracked &&\n+\techo ignored > ignored &&\n+\tmkdir ignored.d &&\n+\techo ignored >ignored.d/untracked\n+\tSTASH_ID_U=$(git stash create -u message1 message2 message3) &&\n+\ttest_tick &&\n+\tSTASH_ID__INCLUDE_UNTRACKED=$(git stash create --include-untracked message1 message2 message3) &&\n+\ttest_tick &&\n+\tSTASH_ID_A=$(git stash create -a message1 message2 message3) &&\n+\ttest_tick &&\n+\tSTASH_ID__ALL=$(git stash create --all message1 message2 message3)\n+'\n+\n+cat > expect <<EOF\n+MM file\n+?? HEAD\n+?? actual\n+?? expect\n+?? file2\n+?? untracked/\n+EOF\n+test_expect_success 'stash create --include-untracked do not clean the untracked files' '\n+\ttest_when_finished \"rm expect actual\"\n+\tgit status --porcelain >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'stash create --include-untracked should be equal to -u in created stash' '\n+\ttest_when_finished \"rm message.u message.untracked\"\n+\tgit diff-tree --exit-code \"$STASH_ID_U:\" \"$STASH_ID__INCLUDE_UNTRACKED:\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U^:\" \"$STASH_ID__INCLUDE_UNTRACKED^:\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U^2:\" \"$STASH_ID__INCLUDE_UNTRACKED^2:\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U^3:\" \"$STASH_ID__INCLUDE_UNTRACKED^3:\" &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID_U\" >message.u &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID__INCLUDE_UNTRACKED\" >message.untracked &&\n+\ttest_cmp message.u message.untracked\n+'\n+\n+test_expect_success 'stash create --all should be equal to -a in created stash' '\n+\ttest_when_finished \"rm message.a message.all\"\n+\tgit diff-tree --exit-code \"$STASH_ID_A:\" \"$STASH_ID__ALL:\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_A^:\" \"$STASH_ID__ALL^:\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_A^2:\" \"$STASH_ID__ALL^2:\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_A^3:\" \"$STASH_ID__ALL^3:\" &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID_A\" >message.a &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID__ALL\" >message.all &&\n+\ttest_cmp message.a message.all\n+'\n+\n+cat > expect <<EOF\n+A\t.gitignore\n+A\tignored\n+A\tignored.d\n+EOF\n+test_expect_success 'stash create -a and -u difference about ignored files' '\n+\ttest_when_finished \"rm expect actual\" &&\n+\tgit diff --exit-code \"$STASH_ID_U\" \"$STASH_ID_A\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U:\" \"$STASH_ID_A:\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U^:\" \"$STASH_ID_A^:\" &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U^2:\" \"$STASH_ID_A^2:\" &&\n+\tgit diff-tree --name-status \"$STASH_ID_U^3:\" \"$STASH_ID_A^3:\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'stash save -u should be equal to stash create -u in created stash' '\n+\ttest_when_finished \"rm createmessage savemessage\" &&\n+\ttest_when_finished \"git stash pop -q --index\" &&\n+\tgit stash save -u  message1 message2 message3 &&\n+\tgit diff --exit-code $STASH_ID_U stash &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U:\" stash: &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U^:\" stash^: &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U^2:\" stash^2: &&\n+\tgit diff-tree --exit-code \"$STASH_ID_U^3:\" stash^3: &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID_U\" >createmessage &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch stash >savemessage &&\n+\ttest_cmp createmessage savemessage\n+'\n+\n+test_expect_success 'stash create -a should be equal to stash save -a in created stash' '\n+\ttest_when_finished \"rm createmessage savemessage\" &&\n+\ttest_when_finished \"git stash pop -q --index\" &&\n+\tgit stash save -a -q message1 message2 message3 &&\n+\tgit diff --exit-code \"$STASH_ID_U\" stash &&\n+\tgit diff-tree --exit-code \"$STASH_ID_A:\" stash: &&\n+\tgit diff-tree --exit-code \"$STASH_ID_A^:\" stash^: &&\n+\tgit diff-tree --exit-code \"$STASH_ID_A^2:\" stash^2: &&\n+\tgit diff-tree --exit-code \"$STASH_ID_A^3:\" stash^3: &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID_U\" >createmessage &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch stash >savemessage &&\n+\ttest_cmp createmessage savemessage\n+'\n+\n+cat > expect.message <<EOF\n+On master: message1 message2 message3\n+\n+EOF\n+test_expect_success 'stash create -u recorded correctly  a message \"message1 message2 message3\"' '\n+\ttest_when_finished \"rm expect* actual*\" &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID_U\" >actual.message &&\n+\ttest_cmp expect.message actual.message\n+'\n+\n+cat > expect.status <<EOF\n+MM file\n+?? HEAD\n+?? expect.message\n+?? expect.status\n+?? file2\n+?? untracked/\n+EOF\n+cat > expect.message <<EOF\n+On master: message1 message2 message3\n+\n+EOF\n+test_expect_success 'no effect to nomal stash create message' '\n+\tactual_status=\"$(git status --porcelain)\" &&\n+\techo \"$actual_status\" >actual.status &&\n+\ttest_cmp expect.status actual.status &&\n+\ttest_when_finished \"rm expect* actual*\" &&\n+\tSTASH_ID=$(git stash create message1 message2 message3) &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID\" >actual.message &&\n+\ttest_cmp expect.message actual.message\n+'\n+\n+cat > expect.status <<EOF\n+MM file\n+?? HEAD\n+?? expect.status\n+?? file2\n+?? untracked/\n+EOF\n+test_expect_success 'stash create -message-starting-with-dashs should be fail' '\n+\ttest_when_finished \"rm expect* actual*\" &&\n+\tactual_status=\"$(git status --porcelain)\" &&\n+\techo \"$actual_status\" >actual.status &&\n+\ttest_cmp expect.status actual.status &&\n+\ttest_must_fail git stash create -message-starting-with-dash\n+'\n+\n+cat > expect.status <<EOF\n+MM file\n+?? HEAD\n+?? expect.status\n+?? file2\n+?? untracked/\n+EOF\n+test_expect_success 'stash create --message should be fail' '\n+\ttest_when_finished \"rm expect* actual*\" &&\n+\tactual_status=\"$(git status --porcelain)\" &&\n+\techo \"$actual_status\" >actual.status &&\n+\ttest_cmp expect.status actual.status &&\n+\ttest_must_fail git stash create --message\n+'\n+\n+cat > expect.status <<EOF\n+MM file\n+?? HEAD\n+?? expect.message\n+?? expect.status\n+?? file2\n+?? untracked/\n+EOF\n+cat > expect.message <<EOF\n+On master: -message-starting-with-dash\n+\n+EOF\n+test_expect_success 'stash create -- -message-starting-with-dash should be success' '\n+\tactual_status=\"$(git status --porcelain)\" &&\n+\techo \"$actual_status\" >actual.status &&\n+\ttest_cmp expect.status actual.status &&\n+\ttest_when_finished \"rm expect* actual*\" &&\n+\tSTASH_ID=$(git stash create -- -message-starting-with-dash ) &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID\" >actual.message &&\n+\ttest_cmp expect.message actual.message\n+'\n+\n+cat > expect.status <<EOF\n+MM file\n+?? HEAD\n+?? expect.message\n+?? expect.status\n+?? file2\n+?? untracked/\n+EOF\n+cat > expect.message <<EOF\n+On master: message1 message2 message3\n+\n+EOF\n+test_expect_success 'no effect to nomal stash save massage' '\n+\tactual_status=\"$(git status --porcelain)\" &&\n+\techo \"$actual_status\" >actual.status &&\n+\ttest_cmp expect.status actual.status &&\n+\ttest_when_finished \"rm expect* actual*\" &&\n+\ttest_when_finished \"git stash pop -q --index\" &&\n+\tgit stash save message1 message2 message3 &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch stash >actual.message &&\n+\ttest_cmp expect.message actual.message\n+'\n+\n+cat > expect.status <<EOF\n+MM file\n+?? HEAD\n+?? expect.message\n+?? expect.status\n+?? file2\n+?? untracked/\n+EOF\n+cat > expect.message <<EOF\n+On master: --include-untracked\n+\n+EOF\n+test_expect_success 'stash create -- --include-untracked recorded a stash message correctly' '\n+\tactual_status=\"$(git status --porcelain)\" &&\n+\techo \"$actual_status\" >actual.status &&\n+\ttest_cmp expect.status actual.status &&\n+\ttest_when_finished \"rm expect* actual*\"&&\n+\tSTASH_ID= &&\n+\tSTASH_ID=$(git stash create -- --include-untracked) &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID\" >actual.message &&\n+\ttest_cmp expect.message actual.message\n+'\n+\n+cat > expect.status <<EOF\n+MM file\n+?? HEAD\n+?? expect.message\n+?? expect.status\n+?? file2\n+?? untracked/\n+EOF\n+cat > expect.message <<EOF\n+On master: -message-starting-with-dash\n+\n+EOF\n+test_expect_success 'stash create --include-untracked -- -message-starting-with-dash recorded a stash message correctly' '\n+\tactual_status=\"$(git status --porcelain)\" &&\n+\techo \"$actual_status\" >actual.status &&\n+\ttest_cmp expect.status actual.status &&\n+\ttest_when_finished \"rm expect* actual*\" &&\n+\tSTASH_ID= &&\n+\tSTASH_ID=$(git stash create --include-untracked -- -message-starting-with-dash) &&\n+\tgit show  --pretty=format:\"%s%n%b\" --no-patch \"$STASH_ID\" >actual.message &&\n+\ttest_cmp expect.message actual.message\n+'\n+\n+test_expect_success 'stash create should be fail on wrong parameter' '\n+\t\ttest_must_fail git stash create -message-starting-with-dash &&\n+\t\ttest_must_fail git stash create -u -message-starting-with-dash &&\n+\t\ttest_must_fail git stash create --include-untracked -message-starting-with-dash\n+'\n+\n+test_done\n-- \n1.8.5.2 (Apple Git-48)\n"},{"id":"245081","messageId":"xmqqionm2qzv.fsf@gitster.dls.corp.google.com","threadId":"37006","inReplyTo":"1403856479-37421-1-git-send-email-shigeta@kanamei.co.jp","subject":"Re: [PATCH] stash create: Add --include-untracked and --all option to stash create","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-06-27T17:37:24Z","receivedAt":"2014-06-27T17:37:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"shigeta@kanamei.co.jp writes:\n\n> From: Kazumasa Shigeta <shigeta@kanamei.co.jp>\n>\n> The --include-untracked option acts like the normal \"git stash save --include-untracked --all\" but it does not change anything in working directory.\n> It is inconvenient stash create does not have --include-untracked option however stash save has --include-untracked option.\n\nPlease wrap long lines.\n\n> It fails when using message that start with dash. It is not compatible with now.\n\nI cannot quite parse this.  What do you exactly mean by \"It is not\ncompatible with now\"?  Do you mean \"with this patch, we break\nbackward compatibility\" and if so in what way?\n\n> diff --git a/git-stash.sh b/git-stash.sh\n> index 4798bcf..d636651 100755\n> --- a/git-stash.sh\n> +++ b/git-stash.sh\n> @@ -57,8 +57,35 @@ clear_stash () {\n>  }\n>  \n>  create_stash () {\n> -\tstash_msg=\"$1\"\n> -\tuntracked=\"$2\"\n> +    stash_msg=\n> +    untracked=\n\nBroken indentation here?  We indent with tabs, not runs of spaces.\n\n> +\twhile test $# != 0\n> +\tdo\n> +\t\tcase \"$1\" in\n> +\t\t-u|--include-untracked)\n> +\t\t\tuntracked=untracked\n> +\t\t\t;;\n> +\t\t-a|--all)\n> +\t\t\tuntracked=all\n> +\t\t\t;;\n> +\t\t--)\n> +\t\t\tshift\n> +\t\t\tbreak\n> +\t\t\t;;\n> +\t\t-*)\n> +\t\t\toption=\"$1\"\n> +\t\t\teval_gettextln \"error: unknown option for 'stash create': \\$option\n> +       To provide a message, use git stash create -- '\\$option'\"\n> +\t\t\texit 1\n> +\t\t\t;;\n> +\t\t*)\n> +\t\t\tbreak\n> +\t\t\t;;\n> +\t\tesac\n> +\t\tshift\n> +\tdone\n> +\tstash_msg=\"$*\"\n>  \n>  \tgit update-index -q --refresh\n>  \tif no_changes\n> @@ -260,8 +287,25 @@ save_stash () {\n>  \tfi\n>  \ttest -f \"$GIT_DIR/logs/$ref_stash\" ||\n>  \t\tclear_stash || die \"$(gettext \"Cannot initialize stash\")\"\n> +    untracked_opt=\n> +    case \"$untracked\" in\n> +\t\tuntracked)\n> +\t\t\tuntracked_opt=\"--include-untracked\"\n> +\t\t\tbreak\n> +\t\t\t;;\n> +\t\tall)\n> +\t\t\tuntracked_opt=\"--all\"\n> +\t\t\tbreak\n> +\t\t\t;;\n> +    esac\n> +\n> +\tif test -z \"$untracked_opt\"\n> +\tthen\n> +\t\tcreate_stash  -- \"$stash_msg\"\n> +\telse\n> +\t\tcreate_stash \"$untracked_opt\" -- \"$stash_msg\"\n> +\tfi\n> -\tcreate_stash \"$stash_msg\" $untracked\n\nWouldn't it suffice to do:\n\n\tcreate_stash ${untracked_opt:+\"$untracked_opt\"} -- \"$stash_msg\"\n\nwithout if/then/else/fi?\n\n> diff --git a/t/t3907-stash-create-include-untracked.sh b/t/t3907-stash-create-include-untracked.sh\n> new file mode 100755\n> index 0000000..c28ed0f\n> --- /dev/null\n> +++ b/t/t3907-stash-create-include-untracked.sh\n> @@ -0,0 +1,286 @@\n> +#!/bin/sh\n> +#\n> +# File:   t3907-stash-create-include-untracked.sh\n> +# Author: shigeta\n> +#\n> +# Created on 2014/05/01, 13:13:15\n\nPlease discard the above lines.  We can see what file it is in, and\n\"git log\" knows who wrote it when.  You are only risking them to\nbecome obsolete and/or irrelevant over time without adding any real\nvalue.\n\nDon't we have existing test script for \"stash\"?  If we do, shouldn't\nthis patch be appending test pieces for new mode of operation it\nintroduces to that existing script instead?\n\n> +test_description='Test git stash create --include-untracked and --all'\n> +\n> +. ./test-lib.sh\n> +\n> +cat > .gitignore <<EOF\n> +.gitignore\n> +ignored\n> +ignored.d/\n> +EOF\n\nNo SP between redirect operator and the filename.  Quote EOF if your\nhere-document does not need any variable interpolation to reduce\nmental burden from readers.  I.e.\n\n\tcat >.gitignore <<\\EOF\n        foo\n        bar\n        EOF\n\nAlso in newer test scripts we try to have as little commands to be\nexecuted outside test_expect_success set-up block as possible.  The\ngeneral structure of a new test script (if we do need to add one for\nthis patch, which I doubt) should be along the lines of...\n\n\ttest_description='...'\n        . ./test-lib.sh\n\n\ttest_expect_success setup '\n\t\tthe first test piece typically does all the set up &&\n\t\tcat >.gitignore <<-\\EOF &&\n                .gitignore\n                ...\n                EOF\n\t\tother initialisation necessary\n\t'\n\n        test_expect_success 'explain what this second test checks' '\n\t\tpiece specific setup &&\n\t\tcat >expect <<-\\EOF &&\n                expected output comes here\n                EOF\n                do the command >actual &&\n                test_cmp expect actual\n\t'\n\n        test_expect_success 'explain what this third test checks' '\n        ...\n\n        test_done\n"}]}