{"thread":{"id":"27668","subject":"[PATCH] stash: Add --clean option to stash and remove all untracked files","startedAt":"2011-06-20T23:36:26Z","lastAt":"2011-06-26T20:02:22Z","messageCount":15,"participants":["David Caldwell","Jeff King","Andrew Wong","Johannes Sixt","Paul Ebermann","Junio C Hamano","Marc Branchaud","Miles Bader","Matthieu Moy"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"170340","messageId":"1308612986-26593-1-git-send-email-david@porkrind.org","threadId":"27668","inReplyTo":null,"subject":"[PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"David Caldwell","fromEmail":"david@porkrind.org","sentAt":"2011-06-20T23:36:26Z","receivedAt":"2011-06-20T23:36:26Z","isPatch":true,"sender":{"key":"david@porkrind.org","avatar":"https://avatars.githubusercontent.com/u/45445?v=4"},"body":"The --clean option acts like the normal \"git stash save\" but also adds all\nuntracked files in the working directory to the stash and then calls \"git\nclean --force --quiet\" to restore the working directory to a pristine\nstate.\n\nI find this useful for certain projects that need to run release\nscripts. With this option I can run the release scripts from my main working\ndirectory and not have to maintain a \"clean\" directory in parallel just for\nreleasing. Basically the work-flow becomes:\n\n   $ git tag release-1.0\n   $ git stash --clean\n   $ make release\n   $ git stash pop\n\n\"git stash\" alone is not enough in this case--it leaves untracked files\nlying around (configure and automake droppings, for instance) that might\nmess up a release process that expects everything to be very clean.\n\nSigned-off-by: David Caldwell <david@porkrind.org>\n\nHi,\n\n  This is my first patch to git so I have a couple questions:\n\n  * I used 'find . -name \".git\" -prune -o -print' to get a list of all the\n    files in the working directory. That assumes \".git\" is the name\n    of the repo--is that assumption valid?\n\n  * Also, that find command does not respect .gitignore. Should it? If it\n    does then I think it would need another option to also clean up stuff\n    that is normally ignored (similar to the way \"git clean\" works with the\n    -x option). My thoughts were that if we were stashing everything, being\n    overzealous with the cleanup wouldn't hurt (and would generally be\n    exactly what you want).\n\nThanks,\n  David\n\n---\n Documentation/git-stash.txt |    8 ++-\n git-stash.sh                |   20 +++++++-\n t/t3905-stash-clean.sh      |  111 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 134 insertions(+), 5 deletions(-)\n create mode 100755 t/t3905-stash-clean.sh\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 15f051f..7c9055c 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -13,7 +13,7 @@ SYNOPSIS\n 'git stash' drop [-q|--quiet] [<stash>]\n 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n 'git stash' branch <branchname> [<stash>]\n-'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]\n+'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-c|--clean] [-q|--quiet] [<message>]]\n 'git stash' clear\n 'git stash' create\n \n@@ -42,7 +42,7 @@ is also possible).\n OPTIONS\n -------\n \n-save [-p|--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::\n+save [-p|--patch] [--[no-]keep-index] [-c|--clean] [-q|--quiet] [<message>]::\n \n \tSave your local modifications to a new 'stash', and run `git reset\n \t--hard` to revert them.  The <message> part is optional and gives\n@@ -54,6 +54,10 @@ save [-p|--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::\n If the `--keep-index` option is used, all changes already added to the\n index are left intact.\n +\n+If the `--clean` option is used, all non-tracked files are also stashed and\n+then cleaned up with `git clean`, leaving the working directory in a very\n+clean state.\n++\n With `--patch`, you can interactively select hunks from the diff\n between HEAD and the working tree to be stashed.  The stash entry is\n constructed such that its index state is the same as the index state\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 0a94036..ea34dc4 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -7,7 +7,7 @@ USAGE=\"list [<options>]\n    or: $dashless drop [-q|--quiet] [<stash>]\n    or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n    or: $dashless branch <branchname> [<stash>]\n-   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]\n+   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-c|--clean] [-q|--quiet] [<message>]]\n    or: $dashless clear\"\n \n SUBDIRECTORY_OK=Yes\n@@ -49,6 +49,7 @@ clear_stash () {\n \n create_stash () {\n \tstash_msg=\"$1\"\n+\tclean=\"$2\"\n \n \tgit update-index -q --refresh\n \tif no_changes\n@@ -86,7 +87,7 @@ create_stash () {\n \t\t\tgit read-tree --index-output=\"$TMPindex\" -m $i_tree &&\n \t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n \t\t\texport GIT_INDEX_FILE &&\n-\t\t\tgit diff --name-only -z HEAD | git update-index -z --add --remove --stdin &&\n+\t\t\t(git diff --name-only -z HEAD; test -n \"$clean\" && find . -name \".git\" -prune -o -print0) | git update-index -z --add --remove --stdin &&\n \t\t\tgit write-tree &&\n \t\t\trm -f \"$TMPindex\"\n \t\t) ) ||\n@@ -129,6 +130,7 @@ create_stash () {\n save_stash () {\n \tkeep_index=\n \tpatch_mode=\n+\tclean=\n \twhile test $# != 0\n \tdo\n \t\tcase \"$1\" in\n@@ -146,6 +148,9 @@ save_stash () {\n \t\t-q|--quiet)\n \t\t\tGIT_QUIET=t\n \t\t\t;;\n+\t\t-c|--clean)\n+\t\t\tclean=t\n+\t\t\t;;\n \t\t--)\n \t\t\tshift\n \t\t\tbreak\n@@ -162,6 +167,11 @@ save_stash () {\n \t\tshift\n \tdone\n \n+\tif test -n \"$patch_mode\" && test -n \"$clean\"\n+\tthen\n+\t    die \"Can't stash --patch and --clean at the same time\"\n+\tfi\n+\n \tstash_msg=\"$*\"\n \n \tgit update-index -q --refresh\n@@ -173,7 +183,7 @@ save_stash () {\n \ttest -f \"$GIT_DIR/logs/$ref_stash\" ||\n \t\tclear_stash || die \"Cannot initialize stash\"\n \n-\tcreate_stash \"$stash_msg\"\n+\tcreate_stash \"$stash_msg\" $clean\n \n \t# Make sure the reflog for stash is kept.\n \t: >>\"$GIT_DIR/logs/$ref_stash\"\n@@ -185,6 +195,10 @@ save_stash () {\n \tif test -z \"$patch_mode\"\n \tthen\n \t\tgit reset --hard ${GIT_QUIET:+-q}\n+\t\tif test -n \"$clean\"\n+\t\tthen\n+\t\t\tgit clean --force --quiet\n+\t\tfi\n \n \t\tif test \"$keep_index\" = \"t\" && test -n $i_tree\n \t\tthen\ndiff --git a/t/t3905-stash-clean.sh b/t/t3905-stash-clean.sh\nnew file mode 100755\nindex 0000000..301e4b0\n--- /dev/null\n+++ b/t/t3905-stash-clean.sh\n@@ -0,0 +1,111 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2011 David Caldwell\n+#\n+\n+test_description='Test git stash --clean'\n+\n+. ./test-lib.sh\n+\n+# Test code doesn't seem to clean the test dir out between runs.\n+#git clean --force --quiet\n+\n+test_expect_success 'stash save --clean 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+\tgit stash --clean &&\n+\tgit diff-files --quiet &&\n+\tgit diff-index --cached --quiet HEAD\n+'\n+\n+cat > expect <<EOF\n+?? expect\n+?? output\n+EOF\n+\n+test_expect_success 'stash save --clean cleaned the untracked files' '\n+\tgit status --porcelain > output\n+\ttest_cmp output expect\n+'\n+\n+cat > expect << EOF\n+diff --git a/file b/file\n+index 0cfbf08..00750ed 100644\n+--- a/file\n++++ b/file\n+@@ -1 +1 @@\n+-2\n++3\n+diff --git a/file2 b/file2\n+new file mode 100644\n+index 0000000..d00491f\n+--- /dev/null\n++++ b/file2\n+@@ -0,0 +1 @@\n++1\n+EOF\n+\n+test_expect_success 'stash save --clean stashed the untracked files' '\n+\tgit diff stash^2..stash > output &&\n+\ttest_cmp output expect\n+'\n+\n+test_expect_success 'stash save --patch --clean fails' '\n+\ttest_must_fail git stash --patch --clean\n+'\n+\n+git clean --force --quiet\n+\n+test_expect_success 'stash save -c dirty index' '\n+\techo 4 > file3 &&\n+\tgit add file3 &&\n+\ttest_tick &&\n+\tgit stash -c\n+'\n+\n+cat > expect <<EOF\n+diff --git a/file3 b/file3\n+new file mode 100644\n+index 0000000..b8626c4\n+--- /dev/null\n++++ b/file3\n+@@ -0,0 +1 @@\n++4\n+EOF\n+\n+test_expect_success 'stash save --clean dirty index got stashed' '\n+\tgit stash pop --index &&\n+\tgit diff --cached > output &&\n+\ttest_cmp output expect\n+'\n+\n+git reset\n+\n+test_expect_success 'stash save --clean -q is quiet' '\n+\techo 1 > file5\n+\tgit stash save --clean --quiet > output.out 2>&1 &&\n+\ttest ! -s output.out\n+'\n+\n+test_expect_success 'stash save --clean removed files' '\n+\trm -f file &&\n+\tgit stash save --clean &&\n+\techo 1 > expect &&\n+\ttest_cmp file expect\n+'\n+\n+rm -f expect\n+\n+test_expect_success 'stash save --clean removed files got stashed' '\n+\tgit stash pop &&\n+\ttest ! -f file\n+'\n+\n+test_done\n-- \n1.7.5.3\n"},{"id":"170344","messageId":"20110621003852.GB2050@sigill.intra.peff.net","threadId":"27668","inReplyTo":"1308612986-26593-1-git-send-email-david@porkrind.org","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-21T00:38:52Z","receivedAt":"2011-06-21T00:38:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jun 20, 2011 at 04:36:26PM -0700, David Caldwell wrote:\n\n> The --clean option acts like the normal \"git stash save\" but also adds all\n> untracked files in the working directory to the stash and then calls \"git\n> clean --force --quiet\" to restore the working directory to a pristine\n> state.\n\nHmm. I think I would call this something like \"--untracked\", as to me\nthe main function is saving those files, not cleaning them afterwards\n(the fact that they are cleaned is really just making the untracked-file\nhandling in line with what we do for tracked files; we put the changes\nin the stash and remove them from the working tree).\n\nBut that might just be me.\n\n> I find this useful for certain projects that need to run release\n> scripts. With this option I can run the release scripts from my main working\n> directory and not have to maintain a \"clean\" directory in parallel just for\n> releasing. Basically the work-flow becomes:\n> \n>    $ git tag release-1.0\n>    $ git stash --clean\n>    $ make release\n>    $ git stash pop\n> \n> \"git stash\" alone is not enough in this case--it leaves untracked files\n> lying around (configure and automake droppings, for instance) that might\n> mess up a release process that expects everything to be very clean.\n\nFor that workflow, do you actually want the files saved and restored\nvia \"stash pop\"? That is, aren't those untracked files just useless\ncruft that could be regenerated, and you would be just as happy to do:\n\n  $ git tag release-1.0\n  $ git stash\n  $ git clean\n  $ make release\n  $ git stash pop\n\nand have a pristine state after your pop? You end up regenerating them\nduring the next build, which could be costly. But you don't have to pay\nthe price of stashing potentially large files that are just going to end\nup regenerated anyway. For that matter, what should this do with\ngitignored files, like generated object files?\n\nAlso, wouldn't you want to \"git clean\" after your \"make release\" but\nbefore your \"git stash pop\" in case the build creates cruft that is not\noverwritten by your stash pop?\n\n> Signed-off-by: David Caldwell <david@porkrind.org>\n> \n> Hi,\n> \n>   This is my first patch to git so I have a couple questions:\n\nHi David. Welcome to the list. :)\n\nPlease put your comments on the patch (i.e., anything not destined to go\ninto the commit message) below the \"---\" marker; that helps \"git am\"\nknow which part is which.\n\n>   * I used 'find . -name \".git\" -prune -o -print' to get a list of all the\n>     files in the working directory. That assumes \".git\" is the name\n>     of the repo--is that assumption valid?\n\nGenerally yes, but somebody could do something tricky with GIT_DIR. You\nshould be using \"git ls-files -o\" instead.\n\n>   * Also, that find command does not respect .gitignore. Should it? If it\n>     does then I think it would need another option to also clean up stuff\n>     that is normally ignored (similar to the way \"git clean\" works with the\n>     -x option). My thoughts were that if we were stashing everything, being\n>     overzealous with the cleanup wouldn't hurt (and would generally be\n>     exactly what you want).\n\nI'm not sure of the answer to this. I think it would depend on your\nworkflow and your project (i.e., is your build system fragile enough\nthat you need to get rid of ignored build products between builds, or is\nit OK to leave them, which is more efficient). I would think respecting\nignore would be a sane default, but I don't know if it should be\nconfigurable, or have an extra command line option to stash everything.\n\nIf you do want to respect .gitignore, then you can add\n\"--exclude-standard\" to the \"ls-files\" command I mentioned above.\n\n> @@ -86,7 +87,7 @@ create_stash () {\n>  \t\t\tgit read-tree --index-output=\"$TMPindex\" -m $i_tree &&\n>  \t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n>  \t\t\texport GIT_INDEX_FILE &&\n> -\t\t\tgit diff --name-only -z HEAD | git update-index -z --add --remove --stdin &&\n> +\t\t\t(git diff --name-only -z HEAD; test -n \"$clean\" && find . -name \".git\" -prune -o -print0) | git update-index -z --add --remove --stdin &&\n>  \t\t\tgit write-tree &&\n>  \t\t\trm -f \"$TMPindex\"\n>  \t\t) ) ||\n\nWhen you apply this stash, what does the resulting index look like? Do\nthe untracked files remain properly untracked? That might be a good\nthing to double check in the test script.\n\n-Peff\n"},{"id":"170346","messageId":"4DFFF5AA.4030401@porkrind.org","threadId":"27668","inReplyTo":"20110621003852.GB2050@sigill.intra.peff.net","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"David Caldwell","fromEmail":"david@porkrind.org","sentAt":"2011-06-21T01:36:42Z","receivedAt":"2011-06-21T01:36:42Z","isPatch":true,"sender":{"key":"david@porkrind.org","avatar":"https://avatars.githubusercontent.com/u/45445?v=4"},"body":"On 6/20/11 5:38 PM, Jeff King wrote:\n> On Mon, Jun 20, 2011 at 04:36:26PM -0700, David Caldwell wrote:\n> \n>> The --clean option acts like the normal \"git stash save\" but also adds all\n>> untracked files in the working directory to the stash and then calls \"git\n>> clean --force --quiet\" to restore the working directory to a pristine\n>> state.\n> \n> Hmm. I think I would call this something like \"--untracked\", as to me\n> the main function is saving those files, not cleaning them afterwards\n> (the fact that they are cleaned is really just making the untracked-file\n> handling in line with what we do for tracked files; we put the changes\n> in the stash and remove them from the working tree).\n\nI see your point but I thought \"--clean\" was pretty descriptive of how\nthe working dir ended up afterward. Maybe \"git stash --everything\" (or\n\"--all\")?\n\n>> \"git stash\" alone is not enough in this case--it leaves untracked files\n>> lying around (configure and automake droppings, for instance) that might\n>> mess up a release process that expects everything to be very clean.\n> \n> For that workflow, do you actually want the files saved and restored\n> via \"stash pop\"? That is, aren't those untracked files just useless\n> cruft that could be regenerated, and you would be just as happy to do:\n> \n>   $ git tag release-1.0\n>   $ git stash\n>   $ git clean\n>   $ make release\n>   $ git stash pop\n>\n> and have a pristine state after your pop?\n\nYes, in that case you are right. My example was poor. I'm more worried\nabout junk that might actually affect the release but isn't\nauto-generated. I'm usually too wary to run \"git clean\" because of the\nrandom files I have sitting around--todo lists, random patches, new\nfiles I haven't added but are sitting around possibly affecting tests,\nthings like that. I want them back at some point, but I want the\ndirectory very clean when doing the release just to make sure I have\neverything properly committed (so I could, for example, detect the new\nsource file that I forgot to add and commit).\n\n> Also, wouldn't you want to \"git clean\" after your \"make release\" but\n> before your \"git stash pop\" in case the build creates cruft that is not\n> overwritten by your stash pop?\n\nYes, you'd definitely want the git clean before the git stash pop. I\nforgot that step.\n\n> Please put your comments on the patch (i.e., anything not destined to go\n> into the commit message) below the \"---\" marker; that helps \"git am\"\n> know which part is which.\n\nWhoops. That was a copy and paste error. Took multiple tries to get git\nsend-email to work ;-).\n\n>>   * I used 'find . -name \".git\" -prune -o -print' to get a list of all the\n>>     files in the working directory. That assumes \".git\" is the name\n>>     of the repo--is that assumption valid?\n> \n> Generally yes, but somebody could do something tricky with GIT_DIR. You\n> should be using \"git ls-files -o\" instead.\n\nAh, thanks--I didn't know about that command! I was considering using\n\"git status --porcelain\", but ls-files looks exactly like what I want.\n\n> For that matter, what should this do with gitignored files, like\n> generated object files?\n> \n>>   * Also, that find command does not respect .gitignore. Should it?\n> \n> I'm not sure of the answer to this. I think it would depend on your\n> workflow and your project (i.e., is your build system fragile enough\n> that you need to get rid of ignored build products between builds, or is\n> it OK to leave them, which is more efficient). I would think respecting\n> ignore would be a sane default, but I don't know if it should be\n> configurable, or have an extra command line option to stash everything.\n> \n> If you do want to respect .gitignore, then you can add\n> \"--exclude-standard\" to the \"ls-files\" command I mentioned above.\n\nThe more I think about it the more I think you're right that it should\nrespect .gitignore on the default case and have another option to be\nreally thorough (even if my build system isn't always fragile, it's\nsometimes nice to be overly cautious just for my own paranoid peace of\nmind).\n\nPerhaps --clean (or --untracked) respects .gitignore and --all just flat\nout does everything?\n\n>> @@ -86,7 +87,7 @@ create_stash () {\n>>  \t\t\tgit read-tree --index-output=\"$TMPindex\" -m $i_tree &&\n>>  \t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n>>  \t\t\texport GIT_INDEX_FILE &&\n>> -\t\t\tgit diff --name-only -z HEAD | git update-index -z --add --remove --stdin &&\n>> +\t\t\t(git diff --name-only -z HEAD; test -n \"$clean\" && find . -name \".git\" -prune -o -print0) | git update-index -z --add --remove --stdin &&\n>>  \t\t\tgit write-tree &&\n>>  \t\t\trm -f \"$TMPindex\"\n>>  \t\t) ) ||\n> \n> When you apply this stash, what does the resulting index look like? Do\n> the untracked files remain properly untracked? That might be a good\n> thing to double check in the test script.\n\nThey do in indeed, but you're right, that would be a good automated test\nto add.\n\nThanks, I greatly appreciate the help.\n\n-David\n"},{"id":"170350","messageId":"4E002762.3050803@sohovfx.com","threadId":"27668","inReplyTo":"4DFFF5AA.4030401@porkrind.org","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Andrew Wong","fromEmail":"andrew.w@sohovfx.com","sentAt":"2011-06-21T05:08:50Z","receivedAt":"2011-06-21T05:08:50Z","isPatch":true,"sender":{"key":"andrew.w@sohovfx.com","avatar":null},"body":"Ah, this patch will be very useful. I actually ran into several \nscenarios before where I wished stash could do exactly this.\n\nOn 11-06-20 9:36 PM, David Caldwell wrote:\n> On 6/20/11 5:38 PM, Jeff King wrote:\n>> Hmm. I think I would call this something like \"--untracked\", as to me\n>> the main function is saving those files, not cleaning them afterwards\n>> (the fact that they are cleaned is really just making the untracked-file\n>> handling in line with what we do for tracked files; we put the changes\n>> in the stash and remove them from the working tree).\n>\n> I see your point but I thought \"--clean\" was pretty descriptive of how\n> the working dir ended up afterward. Maybe \"git stash --everything\" (or\n> \"--all\")?\nI personally think \"--untracked\" (and -u) is more intuitive too, since \nit tells you what \"git stash\" is about to do. i.e. \"git stash\" is about \nto do the usual stash operation *and* also stash the \"untracked\" files.\nIt seems more clear and precise than saying \"git will stash my tree to a \nstate that is clean\", which doesn't exactly tell me how is that \ndifferent from the usual stash.To me, \"--clean\" might even sounds more \nlike \"do a stash, and then do a clean\", which doesn't make sense.\n\nI would've liked \"--all\" too, except it reminds me too much of \"git \ncommit --all\", which is just committing all tracked files.\n\nAgain, that's just my personal preference.\n"},{"id":"170354","messageId":"4E003A0F.5080601@viscovery.net","threadId":"27668","inReplyTo":"4E002762.3050803@sohovfx.com","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2011-06-21T06:28:31Z","receivedAt":"2011-06-21T06:28:31Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 6/21/2011 7:08, schrieb Andrew Wong:\n> Ah, this patch will be very useful. I actually ran into several scenarios\n> before where I wished stash could do exactly this.\n> \n> On 11-06-20 9:36 PM, David Caldwell wrote:\n>> On 6/20/11 5:38 PM, Jeff King wrote:\n>>> Hmm. I think I would call this something like \"--untracked\", as to me\n>>> the main function is saving those files, not cleaning them afterwards\n>>> (the fact that they are cleaned is really just making the untracked-file\n>>> handling in line with what we do for tracked files; we put the changes\n>>> in the stash and remove them from the working tree).\n>>\n>> I see your point but I thought \"--clean\" was pretty descriptive of how\n>> the working dir ended up afterward. Maybe \"git stash --everything\" (or\n>> \"--all\")?\n> I personally think \"--untracked\" (and -u) is more intuitive too, since it\n> tells you what \"git stash\" is about to do. i.e. \"git stash\" is about to do\n> the usual stash operation *and* also stash the \"untracked\" files.\n\nReally?\n\n   $ git stash --untracked\n\nsound like it stashes *only* untracked files. (That by itself may be a\nfeature that some people want; so far, I'm not among them.)\n\n-- Hannes\n"},{"id":"170382","messageId":"20110621141843.GA18700@sigill.intra.peff.net","threadId":"27668","inReplyTo":"4E003A0F.5080601@viscovery.net","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-21T14:18:43Z","receivedAt":"2011-06-21T14:18:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jun 21, 2011 at 08:28:31AM +0200, Johannes Sixt wrote:\n\n> > I personally think \"--untracked\" (and -u) is more intuitive too, since it\n> > tells you what \"git stash\" is about to do. i.e. \"git stash\" is about to do\n> > the usual stash operation *and* also stash the \"untracked\" files.\n> \n> Really?\n> \n>    $ git stash --untracked\n> \n> sound like it stashes *only* untracked files. (That by itself may be a\n> feature that some people want; so far, I'm not among them.)\n\nI would be happy with something that indicated \"untracked files in\naddition to the regular stash\". I just think it should be about \"add\nthese other files into the stash\", not \"end up in this directory state\".\n\nSomething like \"--untracked-too\" fits that, but is horribly ugly. I also\nthink it makes sense to have some way of stashing everything, including\nexcluded files.  That could just be \"-x\" in conjunction with whatever\nthis option is (which matches \"git clean\"), or it could be a separate\noption name (like \"--all\" or \"--ignored\").\n\nThings like \"git stash --all\" or \"git stash --thorough\" indicate that\nyou are stashing more, but it's hard to remember what the \"more\" is.\n\nSo I don't have any brilliant suggestions. Doing:\n\n  $ git stash --untracked-too --ignored-too\n\nis fairly clear, but somehow strikes me as unnecessarily ugly and\nverbose.\n\n-Peff\n"},{"id":"170396","messageId":"4E00C2BD.6000903@esperanto.de","threadId":"27668","inReplyTo":"20110621141843.GA18700@sigill.intra.peff.net","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Paul Ebermann","fromEmail":"paul.ebermann@esperanto.de","sentAt":"2011-06-21T16:11:41Z","receivedAt":"2011-06-21T16:11:41Z","isPatch":true,"sender":{"key":"paul.ebermann@esperanto.de","avatar":null},"body":"Jeff King schrieb:\n\n> I would be happy with something that indicated \"untracked files in\n> addition to the regular stash\". I just think it should be about \"add\n> these other files into the stash\", not \"end up in this directory state\".\n> \n> Something like \"--untracked-too\" fits that, but is horribly ugly. I also\n> think it makes sense to have some way of stashing everything, including\n> excluded files.  That could just be \"-x\" in conjunction with whatever\n> this option is (which matches \"git clean\"), or it could be a separate\n> option name (like \"--all\" or \"--ignored\").\n> \n> Things like \"git stash --all\" or \"git stash --thorough\" indicate that\n> you are stashing more, but it's hard to remember what the \"more\" is.\n> \n> So I don't have any brilliant suggestions. Doing:\n> \n>   $ git stash --untracked-too --ignored-too\n> \n> is fairly clear, but somehow strikes me as unnecessarily ugly and\n> verbose.\n\nI think `--also-untracked` sounds better. It is even longer, though.\n\nThere could also be `--only-untracked`, which would stack only the\nuntracked files (and let changes of tracked files there).\n\n\nPaŭlo\n"},{"id":"170403","messageId":"7vboxr9kb8.fsf@alter.siamese.dyndns.org","threadId":"27668","inReplyTo":"20110621141843.GA18700@sigill.intra.peff.net","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-21T17:16:27Z","receivedAt":"2011-06-21T17:16:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Jun 21, 2011 at 08:28:31AM +0200, Johannes Sixt wrote:\n>\n>> > I personally think \"--untracked\" (and -u) is more intuitive too, since it\n>> > tells you what \"git stash\" is about to do. i.e. \"git stash\" is about to do\n>> > the usual stash operation *and* also stash the \"untracked\" files.\n>> \n>> Really?\n>> \n>>    $ git stash --untracked\n>> \n>> sound like it stashes *only* untracked files. (That by itself may be a\n>> feature that some people want; so far, I'm not among them.)\n>\n> I would be happy with something that indicated \"untracked files in\n> addition to the regular stash\". I just think it should be about \"add\n> these other files into the stash\", not \"end up in this directory state\".\n>\n> Something like \"--untracked-too\" fits that, but is horribly ugly.\n\nThis I think depends on how you view what stash does. If you concentrate\non the \"saving\" aspect too much, your naming would start from \"untracked\"\nand end up to be that ugly thing. If you start from \"what happens to the\nworking tree\", on the other hand, --clean may not be such a bad name for\nwhat it does.\n\nBut I think the previous point you raised about untracked-but-not-ignored\nvs untracked-and-ignored is much more important. I do not think anybody\nwould want to put build products in stash and unstash them later, so in\nthat sense we can say we would only save the untracked-but-not-ignored in\nthe stash and remove them, but then \"git stash --clean\" (or whatever we\nend up calling it) would still leave non-source material in the working\ntree, which probably does not exactly the original motivation of wanting\nto get a pristine source tree without having to trust the build procedure\n(i.e. \"make clean\" may leave cruft behind).\n"},{"id":"170427","messageId":"4E010BD4.5000902@xiplink.com","threadId":"27668","inReplyTo":"4E00C2BD.6000903@esperanto.de","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2011-06-21T21:23:32Z","receivedAt":"2011-06-21T21:23:32Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 11-06-21 12:11 PM, Paul Ebermann wrote:\n> \n> I think `--also-untracked` sounds better. It is even longer, though.\n> \n> There could also be `--only-untracked`, which would stack only the\n> untracked files (and let changes of tracked files there).\n\nPerhaps -uall to match git-status's -u[<mode>] option (and also the long\nversion thereof)?\n\n\t\tM.\n"},{"id":"170452","messageId":"buotybi2tqg.fsf@dhlpc061.dev.necel.com","threadId":"27668","inReplyTo":"4E00C2BD.6000903@esperanto.de","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Miles Bader","fromEmail":"miles@gnu.org","sentAt":"2011-06-22T07:47:03Z","receivedAt":"2011-06-22T07:47:03Z","isPatch":true,"sender":{"key":"miles@gnu.org","avatar":"https://gravatar.com/avatar/01069b69593af7bff28e2f97afeb3644ae6fe2f5f56cb3a8cf34c5fb8c36efe5?d=mp&s=160"},"body":"Paul Ebermann <Paul.Ebermann@esperanto.de> writes:\n>>   $ git stash --untracked-too --ignored-too\n>> \n>> is fairly clear, but somehow strikes me as unnecessarily ugly and\n>> verbose.\n>\n> I think `--also-untracked` sounds better. It is even longer, though.\n\n\"--include-untracked\" sounds a bit smoother to my ears.\n\n-Miles\n\n-- \nWould you like fries with that?\n"},{"id":"170464","messageId":"20110622150015.GB9266@sigill.intra.peff.net","threadId":"27668","inReplyTo":"buotybi2tqg.fsf@dhlpc061.dev.necel.com","subject":"Re: [PATCH] stash: Add --clean option to stash and remove all untracked files","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-22T15:00:15Z","receivedAt":"2011-06-22T15:00:15Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 22, 2011 at 04:47:03PM +0900, Miles Bader wrote:\n\n> Paul Ebermann <Paul.Ebermann@esperanto.de> writes:\n> >>   $ git stash --untracked-too --ignored-too\n> >> \n> >> is fairly clear, but somehow strikes me as unnecessarily ugly and\n> >> verbose.\n> >\n> > I think `--also-untracked` sounds better. It is even longer, though.\n> \n> \"--include-untracked\" sounds a bit smoother to my ears.\n\nMe too. It's long, but it fits with other git commands.\n\n-Peff\n"},{"id":"170538","messageId":"1308945726-20910-1-git-send-email-david@porkrind.org","threadId":"27668","inReplyTo":"1308612986-26593-1-git-send-email-david@porkrind.org","subject":"[PATCH] stash: Add --include-untracked option to stash and remove all untracked files","fromName":"David Caldwell","fromEmail":"david@porkrind.org","sentAt":"2011-06-24T20:02:06Z","receivedAt":"2011-06-24T20:02:06Z","isPatch":true,"sender":{"key":"david@porkrind.org","avatar":"https://avatars.githubusercontent.com/u/45445?v=4"},"body":"The --include-untracked option acts like the normal \"git stash save\" but\nalso adds all untracked files in the working directory to the stash and then\ncalls \"git clean --force --quiet\" to restore the working directory to a\npristine state.\n\nThis is useful for projects that need to run release scripts. With this\noption, the release scripts can be from the main working directory so one\ndoes not have to maintain a \"clean\" directory in parallel just for\nreleasing. Basically the work-flow becomes:\n\n   $ git tag release-1.0\n   $ git stash --include-untracked\n   $ make release\n   $ git clean -f\n   $ git stash pop\n\n\"git stash\" alone is not enough in this case--it leaves untracked files\nlying around that might mess up a release process that expects everything to\nbe very clean or might let a release succeed that should actually fail (due\nto a new source file being created that hasn't been committed yet).\n\nSigned-off-by: David Caldwell <david@porkrind.org>\n---\n\nHi,\n\n  This is a new version of the \"git-stash --clean patch\" I submitted earlier\n  this week. The changes in this patch with respect to the old patch are:\n\n    * changed --clean to --include-untracked\n    * --include-untracked now respects .gitignore\n    * added --all that does not respect .gitignore\n    * changed the way untracked files get stashed so that when you \"git\n      stash apply\" they remain untracked. I did this by creating a 3rd\n      parent to the stash commit that contains only the unstashed files.\n    * Added more tests to check --all and also whether untracked files\n      remain untracked after apply.\n\n-David\n\n Documentation/git-stash.txt        |   10 ++-\n git-stash.sh                       |   68 +++++++++++++++-\n t/t3905-stash-include-untracked.sh |  155 ++++++++++++++++++++++++++++++++++++\n 3 files changed, 227 insertions(+), 6 deletions(-)\n create mode 100755 t/t3905-stash-include-untracked.sh\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 15f051f..655aeb7 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -13,7 +13,8 @@ SYNOPSIS\n 'git stash' drop [-q|--quiet] [<stash>]\n 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n 'git stash' branch <branchname> [<stash>]\n-'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]\n+'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n+             [-u|--include-untracked] [-a|--all] [<message>]]\n 'git stash' clear\n 'git stash' create\n \n@@ -42,7 +43,7 @@ is also possible).\n OPTIONS\n -------\n \n-save [-p|--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::\n+save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::\n \n \tSave your local modifications to a new 'stash', and run `git reset\n \t--hard` to revert them.  The <message> part is optional and gives\n@@ -54,6 +55,11 @@ save [-p|--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::\n If the `--keep-index` option is used, all changes already added to the\n index are left intact.\n +\n+If the `--include-untracked` option is used, all untracked files are also\n+stashed and then cleaned up with `git clean`, leaving the working directory\n+in a very clean state. If the `--all` option is used instead then the\n+ignored files are stashed and cleaned in addition to the untracked files.\n++\n With `--patch`, you can interactively select hunks from the diff\n between HEAD and the working tree to be stashed.  The stash entry is\n constructed such that its index state is the same as the index state\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 0a94036..37c0904 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -7,7 +7,8 @@ USAGE=\"list [<options>]\n    or: $dashless drop [-q|--quiet] [<stash>]\n    or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n    or: $dashless branch <branchname> [<stash>]\n-   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]\n+   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n+                       [-u|--include-untracked] [-a|--all] [<message>]]\n    or: $dashless clear\"\n \n SUBDIRECTORY_OK=Yes\n@@ -33,7 +34,14 @@ fi\n \n no_changes () {\n \tgit diff-index --quiet --cached HEAD --ignore-submodules -- &&\n-\tgit diff-files --quiet --ignore-submodules\n+\tgit diff-files --quiet --ignore-submodules &&\n+\t(test -z \"$untracked\" || test -z $(untracked_files))\n+}\n+\n+untracked_files () {\n+\texcl_opt=--exclude-standard\n+\ttest \"$untracked\" = \"all\" && excl_opt=\n+\tgit ls-files -o -z $excl_opt\n }\n \n clear_stash () {\n@@ -49,6 +57,7 @@ clear_stash () {\n \n create_stash () {\n \tstash_msg=\"$1\"\n+\tuntracked=\"$2\"\n \n \tgit update-index -q --refresh\n \tif no_changes\n@@ -78,6 +87,25 @@ create_stash () {\n \t\tgit commit-tree $i_tree -p $b_commit) ||\n \t\tdie \"Cannot save the current index state\"\n \n+\tif test -n \"$untracked\"\n+\tthen\n+\t\t# Untracked files are stored by themselves in a parentless commit, for\n+\t\t# ease of unpacking later.\n+\t\tu_commit=$(\n+\t\t\tuntracked_files | (\n+\t\t\t\texport GIT_INDEX_FILE=\"$TMPindex\"\n+\t\t\t\trm -f \"$TMPindex\" &&\n+\t\t\t\tgit update-index -z --add --remove --stdin &&\n+\t\t\t\tu_tree=$(git write-tree) &&\n+\t\t\t\tprintf 'untracked files on %s\\n' \"$msg\" | git commit-tree $u_tree  &&\n+\t\t\t\trm -f \"$TMPindex\"\n+\t\t) ) || die \"Cannot save the untracked files\"\n+\n+\t\tuntracked_commit_option=\"-p $u_commit\";\n+\telse\n+\t\tuntracked_commit_option=\n+\tfi\n+\n \tif test -z \"$patch_mode\"\n \tthen\n \n@@ -122,13 +150,14 @@ create_stash () {\n \t\tstash_msg=$(printf 'On %s: %s' \"$branch\" \"$stash_msg\")\n \tfi\n \tw_commit=$(printf '%s\\n' \"$stash_msg\" |\n-\t\tgit commit-tree $w_tree -p $b_commit -p $i_commit) ||\n+\t\tgit commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||\n \t\tdie \"Cannot record working tree state\"\n }\n \n save_stash () {\n \tkeep_index=\n \tpatch_mode=\n+\tuntracked=\n \twhile test $# != 0\n \tdo\n \t\tcase \"$1\" in\n@@ -146,6 +175,12 @@ save_stash () {\n \t\t-q|--quiet)\n \t\t\tGIT_QUIET=t\n \t\t\t;;\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@@ -162,6 +197,11 @@ save_stash () {\n \t\tshift\n \tdone\n \n+\tif test -n \"$patch_mode\" && test -n \"$untracked\"\n+\tthen\n+\t    die \"Can't use --patch and ---include-untracked or --all at the same time\"\n+\tfi\n+\n \tstash_msg=\"$*\"\n \n \tgit update-index -q --refresh\n@@ -173,7 +213,7 @@ save_stash () {\n \ttest -f \"$GIT_DIR/logs/$ref_stash\" ||\n \t\tclear_stash || die \"Cannot initialize stash\"\n \n-\tcreate_stash \"$stash_msg\"\n+\tcreate_stash \"$stash_msg\" $untracked\n \n \t# Make sure the reflog for stash is kept.\n \t: >>\"$GIT_DIR/logs/$ref_stash\"\n@@ -185,6 +225,11 @@ save_stash () {\n \tif test -z \"$patch_mode\"\n \tthen\n \t\tgit reset --hard ${GIT_QUIET:+-q}\n+\t\ttest \"$untracked\" = \"all\" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=\n+\t\tif test -n \"$untracked\"\n+\t\tthen\n+\t\t \tgit clean --force --quiet $CLEAN_X_OPTION\n+\t\tfi\n \n \t\tif test \"$keep_index\" = \"t\" && test -n $i_tree\n \t\tthen\n@@ -234,9 +279,11 @@ show_stash () {\n #   w_commit is set to the commit containing the working tree\n #   b_commit is set to the base commit\n #   i_commit is set to the commit containing the index tree\n+#   u_commit is set to the commit containing the untracked files tree\n #   w_tree is set to the working tree\n #   b_tree is set to the base tree\n #   i_tree is set to the index tree\n+#   u_tree is set to the untracked files tree\n #\n #   GIT_QUIET is set to t if -q is specified\n #   INDEX_OPTION is set to --index if --index is specified.\n@@ -261,9 +308,11 @@ parse_flags_and_rev()\n \tw_commit=\n \tb_commit=\n \ti_commit=\n+\tu_commit=\n \tw_tree=\n \tb_tree=\n \ti_tree=\n+\tu_tree=\n \n \tREV=$(git rev-parse --no-flags --symbolic \"$@\") || exit 1\n \n@@ -311,6 +360,9 @@ parse_flags_and_rev()\n \tIS_STASH_LIKE=t &&\n \ttest \"$ref_stash\" = \"$(git rev-parse --symbolic-full-name \"${REV%@*}\")\" &&\n \tIS_STASH_REF=t\n+\n+\tu_commit=$(git rev-parse --quiet --verify $REV^3 2>/dev/null) &&\n+\tu_tree=$(git rev-parse $REV^3: 2>/dev/null)\n }\n \n is_stash_like()\n@@ -353,6 +405,14 @@ apply_stash () {\n \t\tgit reset\n \tfi\n \n+\tif test -n \"$u_tree\"\n+\tthen\n+\t\tGIT_INDEX_FILE=\"$TMPindex\" git-read-tree \"$u_tree\" &&\n+\t\tGIT_INDEX_FILE=\"$TMPindex\" git checkout-index --all &&\n+\t\trm -f \"$TMPindex\" ||\n+\t\tdie 'Could not restore untracked files from stash'\n+\tfi\n+\n \teval \"\n \t\tGITHEAD_$w_tree='Stashed changes' &&\n \t\tGITHEAD_$c_tree='Updated upstream' &&\ndiff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh\nnew file mode 100755\nindex 0000000..4f2eedf\n--- /dev/null\n+++ b/t/t3905-stash-include-untracked.sh\n@@ -0,0 +1,155 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2011 David Caldwell\n+#\n+\n+test_description='Test git stash --include-untracked'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'stash save --include-untracked 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+\tgit stash --include-untracked &&\n+\tgit diff-files --quiet &&\n+\tgit diff-index --cached --quiet HEAD\n+'\n+\n+cat > expect <<EOF\n+?? expect\n+?? output\n+EOF\n+\n+test_expect_success 'stash save --include-untracked cleaned the untracked files' '\n+\tgit status --porcelain > output\n+\ttest_cmp output expect\n+'\n+\n+cat > expect.diff <<EOF\n+diff --git a/file2 b/file2\n+new file mode 100644\n+index 0000000..d00491f\n+--- /dev/null\n++++ b/file2\n+@@ -0,0 +1 @@\n++1\n+EOF\n+cat > expect.lstree <<EOF\n+file2\n+EOF\n+\n+test_expect_success 'stash save --include-untracked stashed the untracked files' '\n+\ttest \"!\" -f file2 &&\n+\tgit diff HEAD..stash^3 -- file2 > output &&\n+\ttest_cmp output expect.diff &&\n+\tgit ls-tree --name-only stash^3: > output &&\n+\ttest_cmp output expect.lstree\n+'\n+test_expect_success 'stash save --patch --include-untracked fails' '\n+\ttest_must_fail git stash --patch --include-untracked\n+'\n+\n+test_expect_success 'stash save --patch --all fails' '\n+\ttest_must_fail git stash --patch --all\n+'\n+\n+git clean --force --quiet\n+\n+cat > expect <<EOF\n+ M file\n+?? expect\n+?? file2\n+?? output\n+EOF\n+\n+test_expect_success 'stash pop after save --include-untracked leaves files untracked again' '\n+\tgit stash pop &&\n+\tgit status --porcelain > output\n+\ttest_cmp output expect\n+'\n+\n+git clean --force --quiet\n+\n+test_expect_success 'stash save -u dirty index' '\n+\techo 4 > file3 &&\n+\tgit add file3 &&\n+\ttest_tick &&\n+\tgit stash -u\n+'\n+\n+cat > expect <<EOF\n+diff --git a/file3 b/file3\n+new file mode 100644\n+index 0000000..b8626c4\n+--- /dev/null\n++++ b/file3\n+@@ -0,0 +1 @@\n++4\n+EOF\n+\n+test_expect_success 'stash save --include-untracked dirty index got stashed' '\n+\tgit stash pop --index &&\n+\tgit diff --cached > output &&\n+\ttest_cmp output expect\n+'\n+\n+git reset > /dev/null\n+\n+test_expect_success 'stash save --include-untracked -q is quiet' '\n+\techo 1 > file5 &&\n+\tgit stash save --include-untracked --quiet > output.out 2>&1 &&\n+\ttest ! -s output.out\n+'\n+\n+test_expect_success 'stash save --include-untracked removed files' '\n+\trm -f file &&\n+\tgit stash save --include-untracked &&\n+\techo 1 > expect &&\n+\ttest_cmp file expect\n+'\n+\n+rm -f expect\n+\n+test_expect_success 'stash save --include-untracked removed files got stashed' '\n+\tgit stash pop &&\n+\ttest ! -f file\n+'\n+\n+cat > .gitignore <<EOF\n+.gitignore\n+ignored\n+EOF\n+\n+test_expect_success 'stash save --include-untracked respects .gitignore' '\n+\techo ignored > ignored &&\n+\tgit stash -u &&\n+\ttest -s ignored &&\n+\ttest -s .gitignore\n+'\n+\n+test_expect_success 'stash save -u can stash with only untracked files different' '\n+\techo 4 > file4 &&\n+\tgit stash -u\n+\ttest \"!\" -f file4\n+'\n+\n+test_expect_success 'stash save --all does not respect .gitignore' '\n+\tgit stash -a &&\n+\ttest \"!\" -f ignored &&\n+\ttest \"!\" -f .gitignore\n+'\n+\n+test_expect_success 'stash save --all is stash poppable' '\n+\tgit stash pop &&\n+\ttest -s ignored &&\n+\ttest -s .gitignore\n+'\n+\n+test_done\n-- \n1.7.5.4\n"},{"id":"170539","messageId":"vpqei2jj7jl.fsf@bauges.imag.fr","threadId":"27668","inReplyTo":"1308945726-20910-1-git-send-email-david@porkrind.org","subject":"Re: [PATCH] stash: Add --include-untracked option to stash and remove all untracked files","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2011-06-24T20:31:10Z","receivedAt":"2011-06-24T20:31:10Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"David Caldwell <david@porkrind.org> writes:\n\n> The --include-untracked option acts like the normal \"git stash save\" but\n> also adds all untracked files in the working directory to the stash and then\n> calls \"git clean --force --quiet\"\n\nGreat you did it, that's indeed something I missed with git stash.\n\n> +\t(test -z \"$untracked\" || test -z $(untracked_files))\n\nShouldn't there be double quotes around $(untracked_files)?\n\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\nI first thought of --all as a complement of --include-untracked, but in\nyour proposal, a simple\n\n  git stash --all\n\nis accepted. If you go this way, maybe there should be more symetry in\noption naming. For example, --all could be --include-ignored.\n\nOr you can consider that --all is short and sweet, and go for it ;-).\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"170541","messageId":"1308963366-1697-1-git-send-email-david@porkrind.org","threadId":"27668","inReplyTo":"vpqei2jj7jl.fsf@bauges.imag.fr","subject":"[PATCH] stash: Add --include-untracked option to stash and remove all untracked files","fromName":"David Caldwell","fromEmail":"david@porkrind.org","sentAt":"2011-06-25T00:56:06Z","receivedAt":"2011-06-25T00:56:06Z","isPatch":true,"sender":{"key":"david@porkrind.org","avatar":"https://avatars.githubusercontent.com/u/45445?v=4"},"body":"The --include-untracked option acts like the normal \"git stash save\" but\nalso adds all untracked files in the working directory to the stash and then\ncalls \"git clean --force --quiet\" to restore the working directory to a\npristine state.\n\nThis is useful for projects that need to run release scripts. With this\noption, the release scripts can be from the main working directory so one\ndoes not have to maintain a \"clean\" directory in parallel just for\nreleasing. Basically the work-flow becomes:\n\n   $ git tag release-1.0\n   $ git stash --include-untracked\n   $ make release\n   $ git clean -f\n   $ git stash pop\n\n\"git stash\" alone is not enough in this case--it leaves untracked files\nlying around that might mess up a release process that expects everything to\nbe very clean or might let a release succeed that should actually fail (due\nto a new source file being created that hasn't been committed yet).\n\nSigned-off-by: David Caldwell <david@porkrind.org>\n---\nOn 6/24/11 10:31 PM +0200 Matthieu Moy wrote:\n\n> David Caldwell <david@porkrind.org> writes:\n> \n>> The --include-untracked option acts like the normal \"git stash save\" but\n>> also adds all untracked files in the working directory to the stash and\n>> then calls \"git clean --force --quiet\"\n> \n> Great you did it, that's indeed something I missed with git stash.\n> \n>> +    (test -z \"$untracked\" || test -z $(untracked_files))\n> \n> Shouldn't there be double quotes around $(untracked_files)?\n\nYes there probably should be. It is corrected in this patch.\n\n> I first thought of --all as a complement of --include-untracked, but in\n> your proposal, a simple\n> \n>   git stash --all\n> \n> is accepted. If you go this way, maybe there should be more symetry in\n> option naming. For example, --all could be --include-ignored.\n> \n> Or you can consider that --all is short and sweet, and go for it ;-).\n\nThe problem with --include-ignored is that it doesn't read to me as if the\nuntracked files are included too. It should technically be\n\"--include-untracked-and-ignored\". \"-all\" seems like a better alternative to\nme.\n\n-David\n\n Documentation/git-stash.txt        |   10 ++-\n git-stash.sh                       |   68 +++++++++++++++-\n t/t3905-stash-include-untracked.sh |  155 ++++++++++++++++++++++++++++++++++++\n 3 files changed, 227 insertions(+), 6 deletions(-)\n create mode 100755 t/t3905-stash-include-untracked.sh\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 15f051f..655aeb7 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -13,7 +13,8 @@ SYNOPSIS\n 'git stash' drop [-q|--quiet] [<stash>]\n 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n 'git stash' branch <branchname> [<stash>]\n-'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]\n+'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n+             [-u|--include-untracked] [-a|--all] [<message>]]\n 'git stash' clear\n 'git stash' create\n \n@@ -42,7 +43,7 @@ is also possible).\n OPTIONS\n -------\n \n-save [-p|--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::\n+save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::\n \n \tSave your local modifications to a new 'stash', and run `git reset\n \t--hard` to revert them.  The <message> part is optional and gives\n@@ -54,6 +55,11 @@ save [-p|--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::\n If the `--keep-index` option is used, all changes already added to the\n index are left intact.\n +\n+If the `--include-untracked` option is used, all untracked files are also\n+stashed and then cleaned up with `git clean`, leaving the working directory\n+in a very clean state. If the `--all` option is used instead then the\n+ignored files are stashed and cleaned in addition to the untracked files.\n++\n With `--patch`, you can interactively select hunks from the diff\n between HEAD and the working tree to be stashed.  The stash entry is\n constructed such that its index state is the same as the index state\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 0a94036..e6b1ffc 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -7,7 +7,8 @@ USAGE=\"list [<options>]\n    or: $dashless drop [-q|--quiet] [<stash>]\n    or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n    or: $dashless branch <branchname> [<stash>]\n-   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]\n+   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n+                       [-u|--include-untracked] [-a|--all] [<message>]]\n    or: $dashless clear\"\n \n SUBDIRECTORY_OK=Yes\n@@ -33,7 +34,14 @@ fi\n \n no_changes () {\n \tgit diff-index --quiet --cached HEAD --ignore-submodules -- &&\n-\tgit diff-files --quiet --ignore-submodules\n+\tgit diff-files --quiet --ignore-submodules &&\n+\t(test -z \"$untracked\" || test -z \"$(untracked_files)\")\n+}\n+\n+untracked_files () {\n+\texcl_opt=--exclude-standard\n+\ttest \"$untracked\" = \"all\" && excl_opt=\n+\tgit ls-files -o -z $excl_opt\n }\n \n clear_stash () {\n@@ -49,6 +57,7 @@ clear_stash () {\n \n create_stash () {\n \tstash_msg=\"$1\"\n+\tuntracked=\"$2\"\n \n \tgit update-index -q --refresh\n \tif no_changes\n@@ -78,6 +87,25 @@ create_stash () {\n \t\tgit commit-tree $i_tree -p $b_commit) ||\n \t\tdie \"Cannot save the current index state\"\n \n+\tif test -n \"$untracked\"\n+\tthen\n+\t\t# Untracked files are stored by themselves in a parentless commit, for\n+\t\t# ease of unpacking later.\n+\t\tu_commit=$(\n+\t\t\tuntracked_files | (\n+\t\t\t\texport GIT_INDEX_FILE=\"$TMPindex\"\n+\t\t\t\trm -f \"$TMPindex\" &&\n+\t\t\t\tgit update-index -z --add --remove --stdin &&\n+\t\t\t\tu_tree=$(git write-tree) &&\n+\t\t\t\tprintf 'untracked files on %s\\n' \"$msg\" | git commit-tree $u_tree  &&\n+\t\t\t\trm -f \"$TMPindex\"\n+\t\t) ) || die \"Cannot save the untracked files\"\n+\n+\t\tuntracked_commit_option=\"-p $u_commit\";\n+\telse\n+\t\tuntracked_commit_option=\n+\tfi\n+\n \tif test -z \"$patch_mode\"\n \tthen\n \n@@ -122,13 +150,14 @@ create_stash () {\n \t\tstash_msg=$(printf 'On %s: %s' \"$branch\" \"$stash_msg\")\n \tfi\n \tw_commit=$(printf '%s\\n' \"$stash_msg\" |\n-\t\tgit commit-tree $w_tree -p $b_commit -p $i_commit) ||\n+\t\tgit commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||\n \t\tdie \"Cannot record working tree state\"\n }\n \n save_stash () {\n \tkeep_index=\n \tpatch_mode=\n+\tuntracked=\n \twhile test $# != 0\n \tdo\n \t\tcase \"$1\" in\n@@ -146,6 +175,12 @@ save_stash () {\n \t\t-q|--quiet)\n \t\t\tGIT_QUIET=t\n \t\t\t;;\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@@ -162,6 +197,11 @@ save_stash () {\n \t\tshift\n \tdone\n \n+\tif test -n \"$patch_mode\" && test -n \"$untracked\"\n+\tthen\n+\t    die \"Can't use --patch and ---include-untracked or --all at the same time\"\n+\tfi\n+\n \tstash_msg=\"$*\"\n \n \tgit update-index -q --refresh\n@@ -173,7 +213,7 @@ save_stash () {\n \ttest -f \"$GIT_DIR/logs/$ref_stash\" ||\n \t\tclear_stash || die \"Cannot initialize stash\"\n \n-\tcreate_stash \"$stash_msg\"\n+\tcreate_stash \"$stash_msg\" $untracked\n \n \t# Make sure the reflog for stash is kept.\n \t: >>\"$GIT_DIR/logs/$ref_stash\"\n@@ -185,6 +225,11 @@ save_stash () {\n \tif test -z \"$patch_mode\"\n \tthen\n \t\tgit reset --hard ${GIT_QUIET:+-q}\n+\t\ttest \"$untracked\" = \"all\" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=\n+\t\tif test -n \"$untracked\"\n+\t\tthen\n+\t\t \tgit clean --force --quiet $CLEAN_X_OPTION\n+\t\tfi\n \n \t\tif test \"$keep_index\" = \"t\" && test -n $i_tree\n \t\tthen\n@@ -234,9 +279,11 @@ show_stash () {\n #   w_commit is set to the commit containing the working tree\n #   b_commit is set to the base commit\n #   i_commit is set to the commit containing the index tree\n+#   u_commit is set to the commit containing the untracked files tree\n #   w_tree is set to the working tree\n #   b_tree is set to the base tree\n #   i_tree is set to the index tree\n+#   u_tree is set to the untracked files tree\n #\n #   GIT_QUIET is set to t if -q is specified\n #   INDEX_OPTION is set to --index if --index is specified.\n@@ -261,9 +308,11 @@ parse_flags_and_rev()\n \tw_commit=\n \tb_commit=\n \ti_commit=\n+\tu_commit=\n \tw_tree=\n \tb_tree=\n \ti_tree=\n+\tu_tree=\n \n \tREV=$(git rev-parse --no-flags --symbolic \"$@\") || exit 1\n \n@@ -311,6 +360,9 @@ parse_flags_and_rev()\n \tIS_STASH_LIKE=t &&\n \ttest \"$ref_stash\" = \"$(git rev-parse --symbolic-full-name \"${REV%@*}\")\" &&\n \tIS_STASH_REF=t\n+\n+\tu_commit=$(git rev-parse --quiet --verify $REV^3 2>/dev/null) &&\n+\tu_tree=$(git rev-parse $REV^3: 2>/dev/null)\n }\n \n is_stash_like()\n@@ -353,6 +405,14 @@ apply_stash () {\n \t\tgit reset\n \tfi\n \n+\tif test -n \"$u_tree\"\n+\tthen\n+\t\tGIT_INDEX_FILE=\"$TMPindex\" git-read-tree \"$u_tree\" &&\n+\t\tGIT_INDEX_FILE=\"$TMPindex\" git checkout-index --all &&\n+\t\trm -f \"$TMPindex\" ||\n+\t\tdie 'Could not restore untracked files from stash'\n+\tfi\n+\n \teval \"\n \t\tGITHEAD_$w_tree='Stashed changes' &&\n \t\tGITHEAD_$c_tree='Updated upstream' &&\ndiff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh\nnew file mode 100755\nindex 0000000..4f2eedf\n--- /dev/null\n+++ b/t/t3905-stash-include-untracked.sh\n@@ -0,0 +1,155 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2011 David Caldwell\n+#\n+\n+test_description='Test git stash --include-untracked'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'stash save --include-untracked 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+\tgit stash --include-untracked &&\n+\tgit diff-files --quiet &&\n+\tgit diff-index --cached --quiet HEAD\n+'\n+\n+cat > expect <<EOF\n+?? expect\n+?? output\n+EOF\n+\n+test_expect_success 'stash save --include-untracked cleaned the untracked files' '\n+\tgit status --porcelain > output\n+\ttest_cmp output expect\n+'\n+\n+cat > expect.diff <<EOF\n+diff --git a/file2 b/file2\n+new file mode 100644\n+index 0000000..d00491f\n+--- /dev/null\n++++ b/file2\n+@@ -0,0 +1 @@\n++1\n+EOF\n+cat > expect.lstree <<EOF\n+file2\n+EOF\n+\n+test_expect_success 'stash save --include-untracked stashed the untracked files' '\n+\ttest \"!\" -f file2 &&\n+\tgit diff HEAD..stash^3 -- file2 > output &&\n+\ttest_cmp output expect.diff &&\n+\tgit ls-tree --name-only stash^3: > output &&\n+\ttest_cmp output expect.lstree\n+'\n+test_expect_success 'stash save --patch --include-untracked fails' '\n+\ttest_must_fail git stash --patch --include-untracked\n+'\n+\n+test_expect_success 'stash save --patch --all fails' '\n+\ttest_must_fail git stash --patch --all\n+'\n+\n+git clean --force --quiet\n+\n+cat > expect <<EOF\n+ M file\n+?? expect\n+?? file2\n+?? output\n+EOF\n+\n+test_expect_success 'stash pop after save --include-untracked leaves files untracked again' '\n+\tgit stash pop &&\n+\tgit status --porcelain > output\n+\ttest_cmp output expect\n+'\n+\n+git clean --force --quiet\n+\n+test_expect_success 'stash save -u dirty index' '\n+\techo 4 > file3 &&\n+\tgit add file3 &&\n+\ttest_tick &&\n+\tgit stash -u\n+'\n+\n+cat > expect <<EOF\n+diff --git a/file3 b/file3\n+new file mode 100644\n+index 0000000..b8626c4\n+--- /dev/null\n++++ b/file3\n+@@ -0,0 +1 @@\n++4\n+EOF\n+\n+test_expect_success 'stash save --include-untracked dirty index got stashed' '\n+\tgit stash pop --index &&\n+\tgit diff --cached > output &&\n+\ttest_cmp output expect\n+'\n+\n+git reset > /dev/null\n+\n+test_expect_success 'stash save --include-untracked -q is quiet' '\n+\techo 1 > file5 &&\n+\tgit stash save --include-untracked --quiet > output.out 2>&1 &&\n+\ttest ! -s output.out\n+'\n+\n+test_expect_success 'stash save --include-untracked removed files' '\n+\trm -f file &&\n+\tgit stash save --include-untracked &&\n+\techo 1 > expect &&\n+\ttest_cmp file expect\n+'\n+\n+rm -f expect\n+\n+test_expect_success 'stash save --include-untracked removed files got stashed' '\n+\tgit stash pop &&\n+\ttest ! -f file\n+'\n+\n+cat > .gitignore <<EOF\n+.gitignore\n+ignored\n+EOF\n+\n+test_expect_success 'stash save --include-untracked respects .gitignore' '\n+\techo ignored > ignored &&\n+\tgit stash -u &&\n+\ttest -s ignored &&\n+\ttest -s .gitignore\n+'\n+\n+test_expect_success 'stash save -u can stash with only untracked files different' '\n+\techo 4 > file4 &&\n+\tgit stash -u\n+\ttest \"!\" -f file4\n+'\n+\n+test_expect_success 'stash save --all does not respect .gitignore' '\n+\tgit stash -a &&\n+\ttest \"!\" -f ignored &&\n+\ttest \"!\" -f .gitignore\n+'\n+\n+test_expect_success 'stash save --all is stash poppable' '\n+\tgit stash pop &&\n+\ttest -s ignored &&\n+\ttest -s .gitignore\n+'\n+\n+test_done\n-- \n1.7.5.4\n"},{"id":"170555","messageId":"7vliwoz7ht.fsf@alter.siamese.dyndns.org","threadId":"27668","inReplyTo":"1308963366-1697-1-git-send-email-david@porkrind.org","subject":"Re: [PATCH] stash: Add --include-untracked option to stash and remove all untracked files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-26T20:02:22Z","receivedAt":"2011-06-26T20:02:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Caldwell <david@porkrind.org> writes:\n\n> @@ -49,6 +57,7 @@ clear_stash () {\n>  \n>  create_stash () {\n>  \tstash_msg=\"$1\"\n> +\tuntracked=\"$2\"\n\nHmm...\n\n> @@ -173,7 +213,7 @@ save_stash () {\n>  \ttest -f \"$GIT_DIR/logs/$ref_stash\" ||\n>  \t\tclear_stash || die \"Cannot initialize stash\"\n>  \n> -\tcreate_stash \"$stash_msg\"\n> +\tcreate_stash \"$stash_msg\" $untracked\n\nJust a minor nit from internal API point of view, I would prefer to see\nsomething like\n\n\tcreate_stash --untracked=all \"message\"\n\nor even\n\n\tcreate_stash --untracked=all --message=\"message\"\n\nonce you start enriching these functions with optional behaviour.\n"}]}