]> git.ipfire.org Git - thirdparty/git.git/commitdiff
status: fix nested sparse directory diff in sparse index
authorVictoria Dye <vdye@github.com>
Tue, 1 Mar 2022 20:24:25 +0000 (20:24 +0000)
committerJunio C Hamano <gitster@pobox.com>
Tue, 1 Mar 2022 20:36:00 +0000 (12:36 -0800)
Enable the 'recursive' diff option for the diff executed as part of 'git
status'. Without the 'recursive' enabled, 'git status' reports index
changes incorrectly when the following conditions were met:

* sparse index is enabled
* there is a difference between the index and HEAD in a file inside a
  *subdirectory* of a sparse directory
* the sparse directory index entry is *not* expanded in-core

Because it is not recursive by default, the diff in 'git status' reports
changes only at the level of files and directories that are immediate
children of a sparse directory, rather than recursing into directories with
changes to identify the modified file(s). As a result, 'git status' reports
the immediate subdirectory itself as "modified".

Example:

$ git init
$ mkdir -p sparse/sub
$ echo test >sparse/sub/foo
$ git add .
$ git commit -m "commit 1"
$ echo somethingelse >sparse/sub/foo
$ git add .
$ git commit -a -m "commit 2"
$ git sparse-checkout set --cone --sparse-index 'sparse'
$ git reset --soft HEAD~1
$ git status
On branch master
You are in a sparse checkout.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   sparse/sub

Enabling the 'recursive' diff option in 'wt_status_collect_changes_index'
corrects this issue by allowing the diff to recurse into subdirectories of
sparse directories to find modified files. Given the same repository setup
as the example above, the corrected result of `git status` is:

$ git status
On branch master
You are in a sparse checkout.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   sparse/sub/foo

Signed-off-by: Victoria Dye <vdye@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
t/t1092-sparse-checkout-compatibility.sh
wt-status.c

index 9ef7cd808851e7d5e99262d29082191d9f2117a2..b1dcaa0e64213491f1d08a03b2747d4a31833e1b 100755 (executable)
@@ -278,6 +278,13 @@ test_expect_success 'status with options' '
        test_all_match git status --porcelain=v2 -uno
 '
 
+test_expect_success 'status with diff in unexpanded sparse directory' '
+       init_repos &&
+       test_all_match git checkout rename-base &&
+       test_all_match git reset --soft rename-out-to-out &&
+       test_all_match git status --porcelain=v2
+'
+
 test_expect_success 'status reports sparse-checkout' '
        init_repos &&
        git -C sparse-checkout status >full &&
index 335e723a71e2537095359d3d4ee6cb77daea1e16..7da8bbe261aee0474e743825418f609d50ac86cb 100644 (file)
@@ -651,6 +651,15 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
        rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
        rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
+
+       /*
+        * The `recursive` option must be enabled to allow the diff to recurse
+        * into subdirectories of sparse directory index entries. If it is not
+        * enabled, a subdirectory containing file(s) with changes is reported
+        * as "modified", rather than the modified files themselves.
+        */
+       rev.diffopt.flags.recursive = 1;
+
        copy_pathspec(&rev.prune_data, &s->pathspec);
        run_diff_index(&rev, 1);
        object_array_clear(&rev.pending);