stash: fix handling removed files with --keep-index
authorThomas Gummerer <t.gummerer@gmail.com>
Tue, 16 Jul 2019 14:23:22 +0000 (15:23 +0100)
committerJunio C Hamano <gitster@pobox.com>
Tue, 16 Jul 2019 19:58:20 +0000 (12:58 -0700)
git stash push --keep-index is supposed to keep all changes that have
been added to the index, both in the index and on disk.

Currently this doesn't behave correctly when a file is removed from
the index. Instead of keeping it deleted on disk, --keep-index
currently restores the file.

Fix that behaviour by using 'git checkout' in no-overlay mode which
can faithfully restore the index and working tree. This also
simplifies the code.

Note that this will overwrite untracked files if the untracked file
has the same name as a file that has been deleted in the index.

Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/stash.c
t/t3903-stash.sh
index 2a8e6d09b406345519201ef64f572197a409e7a4..251120b92405af986838792cba5e5351018ad6c7 100644 (file)
@@ -1390,30 +1390,16 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
                }
 
                if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
-                       struct child_process cp_ls = CHILD_PROCESS_INIT;
-                       struct child_process cp_checkout = CHILD_PROCESS_INIT;
-                       struct strbuf out = STRBUF_INIT;
-
-                       if (reset_tree(&info.i_tree, 0, 1)) {
-                               ret = -1;
-                               goto done;
-                       }
-
-                       cp_ls.git_cmd = 1;
-                       argv_array_pushl(&cp_ls.args, "ls-files", "-z",
-                                        "--modified", "--", NULL);
-
-                       add_pathspecs(&cp_ls.args, ps);
-                       if (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {
-                               ret = -1;
-                               goto done;
-                       }
+                       struct child_process cp = CHILD_PROCESS_INIT;
 
-                       cp_checkout.git_cmd = 1;
-                       argv_array_pushl(&cp_checkout.args, "checkout-index",
-                                        "-z", "--force", "--stdin", NULL);
-                       if (pipe_command(&cp_checkout, out.buf, out.len, NULL,
-                                        0, NULL, 0)) {
+                       cp.git_cmd = 1;
+                       argv_array_pushl(&cp.args, "checkout", "--no-overlay",
+                                        oid_to_hex(&info.i_tree), "--", NULL);
+                       if (!ps->nr)
+                               argv_array_push(&cp.args, ":/");
+                       else
+                               add_pathspecs(&cp.args, ps);
+                       if (run_command(&cp)) {
                                ret = -1;
                                goto done;
                        }
index ea30d5f6a0f228971d29e257a89fca6ea594cecb..151cd497be88f2612d44614929841cc3bb3d1219 100755 (executable)
@@ -1216,4 +1216,11 @@ test_expect_success 'stash works when user.name and user.email are not set' '
        )
 '
 
+test_expect_success 'stash --keep-index with file deleted in index does not resurrect it on disk' '
+       test_commit to-remove to-remove &&
+       git rm to-remove &&
+       git stash --keep-index &&
+       test_path_is_missing to-remove
+'
+
 test_done