]> git.ipfire.org Git - thirdparty/git.git/commitdiff
stash: handle staged changes in skip-worktree files correctly
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Wed, 30 Oct 2019 10:49:38 +0000 (10:49 +0000)
committerJunio C Hamano <gitster@pobox.com>
Sat, 2 Nov 2019 06:22:45 +0000 (15:22 +0900)
When calling `git stash` while changes were staged for files that are
marked with the `skip-worktree` bit (e.g. files that are excluded in a
sparse checkout), the files are recorded as _deleted_ instead.

The reason is that `git stash` tries to construct the tree reflecting
the worktree essentially by copying the index to a temporary one and
then updating the files from the worktree. Crucially, it calls `git
diff-index` to update also those files that are in the HEAD but have
been unstaged in the index.

However, when the temporary index is updated via `git update-index --add
--remove`, skip-worktree entries mark the files as deleted by mistake.

Let's use the newly-introduced `--ignore-skip-worktree-entries` option
of `git update-index` to prevent exactly this from happening.

Note that the regression test case deliberately avoids replicating the
scenario described above and instead tries to recreate just the symptom.

Reported by Dan Thompson.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/stash.c
git-legacy-stash.sh
t/t3903-stash.sh

index b5a301f24d7a5f8b0a5f7d51703ca4cf38e2109c..56f3b551e4af71c0ea1a82c676a24617f8234eaf 100644 (file)
@@ -1082,8 +1082,9 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
        }
 
        cp_upd_index.git_cmd = 1;
        }
 
        cp_upd_index.git_cmd = 1;
-       argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
-                        "--remove", "--stdin", NULL);
+       argv_array_pushl(&cp_upd_index.args, "update-index",
+                        "--ignore-skip-worktree-entries",
+                        "-z", "--add", "--remove", "--stdin", NULL);
        argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
                         stash_index_path.buf);
 
        argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
                         stash_index_path.buf);
 
index f60e9b3e877b24e42431654e1de19911bdfc4f25..5398a5161d253e7ba0a70a76d46fadc4d702ce8d 100755 (executable)
@@ -193,7 +193,8 @@ create_stash () {
                        GIT_INDEX_FILE="$TMPindex" &&
                        export GIT_INDEX_FILE &&
                        git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
                        GIT_INDEX_FILE="$TMPindex" &&
                        export GIT_INDEX_FILE &&
                        git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
-                       git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
+                       git update-index --ignore-skip-worktree-entries \
+                               -z --add --remove --stdin <"$TMP-stagenames" &&
                        git write-tree &&
                        rm -f "$TMPindex"
                ) ) ||
                        git write-tree &&
                        rm -f "$TMPindex"
                ) ) ||
index b8e337893f3e1e505da94b0c3f0b743ece622f10..1e977145b83766d86561f5838d87d59fb8c3b2b4 100755 (executable)
@@ -1241,4 +1241,15 @@ test_expect_success 'stash --keep-index with file deleted in index does not resu
        test_path_is_missing to-remove
 '
 
        test_path_is_missing to-remove
 '
 
+test_expect_success 'stash handles skip-worktree entries nicely' '
+       test_commit A &&
+       echo changed >A.t &&
+       git add A.t &&
+       git update-index --skip-worktree A.t &&
+       rm A.t &&
+       git stash &&
+
+       git rev-parse --verify refs/stash:A.t
+'
+
 test_done
 test_done