{"thread":{"id":"61944","subject":"\"git-stash --keep-index\" crashes when there are no tracked files","startedAt":"2024-08-13T17:36:33Z","lastAt":"2024-08-16T10:42:28Z","messageCount":3,"participants":["Piotr Siupa","Patrick Steinhardt"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"500812","messageId":"CAPM0=yCJ0snEznK0C+zkmGofWHE3OPWoMXSVGH6=TdM7=rZ84g@mail.gmail.com","threadId":"61944","inReplyTo":null,"subject":"\"git-stash --keep-index\" crashes when there are no tracked files","fromName":"Piotr Siupa","fromEmail":"piotrsiupa@gmail.com","sentAt":"2024-08-13T17:36:15Z","receivedAt":"2024-08-13T17:36:33Z","isPatch":false,"sender":{"key":"piotrsiupa@gmail.com","avatar":"https://gravatar.com/avatar/5970da465a147bda2b821a29e966ed328cca93067a42d05cf33ee38aac4acd6f?d=mp&s=160"},"body":"Hello,\n\nI've found a bug.\nIt's something that's unlikely to happen in a normal workflow but it\nbreaks a script I'm trying to write.\n\nHere is the complete report from \"git bugreport\":\n\nThank you for filling out a Git bug report!\nPlease answer the following questions to help us understand your issue.\n\nWhat did you do before the bug happened? (Steps to reproduce your issue)\ngit init\ngit commit --allow-empty --message='initial commit'\ntouch foo\ngit stash push --include-untracked --keep-index\n\nWhat did you expect to happen? (Expected behavior)\nA stash entry storing the untracked file \"foo\" should be created. The\nworking directory and index should become empty.\n\nWhat happened instead? (Actual behavior)\nThe command did what was expected but after that it crashed with the\nfollowing error message:\nerror: pathspec ':/' did not match any file(s) known to git\n\nWhat's different between what you expected and what actually happened?\nThere should be no error message and the exit code should be 0.\n\nAnything else you want to add:\nThis happens only if there are no files in the current HEAD. Adding\nany file to the initial commit prevents the error from occurring (even\nif it is unchanged when the stash is created).\nI suspect that the command has trouble with the corner case of\nrestoring the state of the index when there are no tracked files.\nI also tested it using executables of \"git-stash\" compiled from\ncurrent branches \"maint\" and \"next\". The behavior is the same.\n\nPlease review the rest of the bug report below.\nYou can delete any lines you don't wish to share.\n\n\n[System Info]\ngit version:\ngit version 2.34.1\ncpu: x86_64\nno commit associated with this build\nsizeof-long: 8\nsizeof-size_t: 8\nshell-path: /bin/sh\nuname: Linux 6.8.0-39-generic #39~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC\nWed Jul 10 15:35:09 UTC 2 x86_64\ncompiler info: gnuc: 11.4\nlibc info: glibc: 2.35\n$SHELL (typically, interactive shell): /bin/bash\n\n\n[Enabled Hooks]\n"},{"id":"501120","messageId":"Zr8sd01HNGwd8zJC@tanuki","threadId":"61944","inReplyTo":"CAPM0=yCJ0snEznK0C+zkmGofWHE3OPWoMXSVGH6=TdM7=rZ84g@mail.gmail.com","subject":"Re: \"git-stash --keep-index\" crashes when there are no tracked files","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-08-16T10:39:51Z","receivedAt":"2024-08-16T10:39:57Z","isPatch":false,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Aug 13, 2024 at 07:36:15PM +0200, Piotr Siupa wrote:\n> Hello,\n> \n> I've found a bug.\n> It's something that's unlikely to happen in a normal workflow but it\n> breaks a script I'm trying to write.\n> \n> Here is the complete report from \"git bugreport\":\n> \n> Thank you for filling out a Git bug report!\n> Please answer the following questions to help us understand your issue.\n> \n> What did you do before the bug happened? (Steps to reproduce your issue)\n> git init\n> git commit --allow-empty --message='initial commit'\n> touch foo\n> git stash push --include-untracked --keep-index\n> \n> What did you expect to happen? (Expected behavior)\n> A stash entry storing the untracked file \"foo\" should be created. The\n> working directory and index should become empty.\n> \n> What happened instead? (Actual behavior)\n> The command did what was expected but after that it crashed with the\n> following error message:\n> error: pathspec ':/' did not match any file(s) known to git\n\nI would clarify that this is not \"crashing\", but that's basically\nsplitting hairs. In any case, thanks for this clear bug report with a\nnice and simple reproducer. I agree that this is something that should\nbe fixed indeed.\n\nWill send a patch shortly.\n\nPatrick\n"},{"id":"501121","messageId":"6067675b5edf36e2eb28e98119a1c02d0a7ae670.1723804926.git.ps@pks.im","threadId":"61944","inReplyTo":"CAPM0=yCJ0snEznK0C+zkmGofWHE3OPWoMXSVGH6=TdM7=rZ84g@mail.gmail.com","subject":"[PATCH] builtin/stash: fix `--keep-index --include-untracked` with empty HEAD","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-08-16T10:42:25Z","receivedAt":"2024-08-16T10:42:28Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"It was reported that creating a stash with `--keep-index\n--include-untracked` causes an error when HEAD points to a commit whose\ntree is empty:\n\n    $ git stash push --keep-index --include-untracked\n    error: pathspec ':/' did not match any file(s) known to git\n\nThis error comes from `git checkout --no-overlay $i_tree -- :/`, which\nwe execute to reset the working tree to the state in our index. As the\ntree generated from the index is empty in our case, ':/' does not match\nany files and thus causes git-checkout(1) to error out.\n\nFix the issue by skipping the checkout when the index tree is empty. As\nexplained in the in-code comment, this should be the correct thing to do\nas there is nothing that we'd have to reset in the first place.\n\nReported-by: Piotr Siupa <piotrsiupa@gmail.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/stash.c  | 23 ++++++++++++++++++++++-\n t/t3903-stash.sh | 15 +++++++++++++++\n 2 files changed, 37 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex d90e072ddc..fcfd97972a 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1671,7 +1671,28 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \t\t\t}\n \t\t}\n \n-\t\tif (keep_index == 1 && !is_null_oid(&info.i_tree)) {\n+\t\t/*\n+\t\t * When keeping staged entries, we need to reset the working\n+\t\t * directory to match the state of our index. This can be\n+\t\t * skipped when the index is the empty tree, because there is\n+\t\t * nothing to reset in that case:\n+\t\t *\n+\t\t *   - When the index has any file, regardless of whether\n+\t\t *     staged or not, the tree cannot be empty by definition\n+\t\t *     and thus we enter the condition.\n+\t\t *\n+\t\t *   - When the index has no files, the only thing we need to\n+\t\t *     care about is untracked files when `--include-untracked`\n+\t\t *     is given. But as we already execute git-clean(1) further\n+\t\t *     up to delete such untracked files we don't have to do\n+\t\t *     anything here, either.\n+\t\t *\n+\t\t * We thus skip calling git-checkout(1) in this case, also\n+\t\t * because running it on an empty tree will cause it to fail\n+\t\t * due to the pathspec not matching anything.\n+\t\t */\n+\t\tif (keep_index == 1 && !is_null_oid(&info.i_tree) &&\n+\t\t    !is_empty_tree_oid(&info.i_tree, the_repository->hash_algo)) {\n \t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \t\t\tcp.git_cmd = 1;\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex e4c0937f61..c87592ee2f 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -1398,6 +1398,21 @@ test_expect_success 'stash --keep-index with file deleted in index does not resu\n \ttest_path_is_missing to-remove\n '\n \n+test_expect_success 'stash --keep-index --include-untracked with empty tree' '\n+\ttest_when_finished \"rm -rf empty\" &&\n+\tgit init empty &&\n+\t(\n+\t\tcd empty &&\n+\t\tgit commit --allow-empty --message \"empty\" &&\n+\t\techo content >file &&\n+\t\tgit stash push --keep-index --include-untracked &&\n+\t\ttest_path_is_missing file &&\n+\t\tgit stash pop &&\n+\t\techo content >expect &&\n+\t\ttest_cmp expect file\n+\t)\n+'\n+\n test_expect_success 'stash apply should succeed with unmodified file' '\n \techo base >file &&\n \tgit add file &&\n\nbase-commit: 477ce5ccd69bda5c44f53e3b230f8a9c54217ed6\n-- \n2.46.0.46.g406f326d27.dirty\n\n"}]}