{"thread":{"id":"58165","subject":"Add a new config to tell Git only check files matched sparse-checkout patterns.","startedAt":"2022-07-15T07:24:05Z","lastAt":"2022-07-15T07:24:05Z","messageCount":1,"participants":["void f"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"459124","messageId":"CA+dtV-gLyXSXAkG_0bBKC6TRN5Rcu6ct=wTAjkPDuGmBDv13Og@mail.gmail.com","threadId":"58165","inReplyTo":null,"subject":"Add a new config to tell Git only check files matched sparse-checkout patterns.","fromName":"void f","fromEmail":"fvoidcn@gmail.com","sentAt":"2022-07-15T07:23:50Z","receivedAt":"2022-07-15T07:24:05Z","isPatch":false,"sender":{"key":"fvoidcn@gmail.com","avatar":null},"body":"From ce8ed96edd72d0c7d57303c0a8b5e734daefeefe Mon Sep 17 00:00:00 2001\nFrom: fvoidCN <fvoidcn@gmail.com>\nDate: Fri, 15 Jul 2022 14:33:37 +0800\nSubject: [PATCH] repo_read_index: add config\n `sparse.onlyCheckFilesMatchPatterns` to tell Git only check files matched\n sparse-checkout patterns.\n\nBy Default with sparse checkouts, all files with SKIP_WORKTREE bit\nin the index must check whether exists in the worktree. And Git will\nexpand all of that files in the index and must traverse recursively\nin the worktree. This option can be used to tell Git that just check\nfiles which can match the sparse-checkout patterns.\n\nIt is useful when you are using a visual file system in order to\non-demand loading working tree lazily. It can control which directory\nshould load by using sparse-checkout patterns. But it doesn't want to\nvisit other directory which haven't been load but just have an empty\ndirectory for the entry of the file loading switch. In default model,\nGit would traverse root direcotry recursively however all files would\nbe loaded, this behaviour would very expensive. With\n`sparse.onlyCheckFilesMatchPatterns` model, Git would just check the\npath witch only matched the sparse-checkout patterns to prevent loading\nall the directory.\n\nSigned-off-by: fvoid <fvoidcn@gmail.com>\n---\n Documentation/config/sparse.txt  | 25 ++++++++++++++++\n cache.h                          |  1 +\n config.c                         |  5 ++++\n environment.c                    |  1 +\n sparse-index.c                   | 19 ++++++++----\n t/t1090-sparse-checkout-scope.sh | 50 ++++++++++++++++++++++++++++++++\n 6 files changed, 95 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/sparse.txt b/Documentation/config/sparse.txt\nindex aff49a8d3a..f71716f446 100644\n--- a/Documentation/config/sparse.txt\n+++ b/Documentation/config/sparse.txt\n@@ -25,3 +25,28 @@ Regardless of this setting, Git does not check for\n present-despite-skipped files unless sparse checkout is enabled, so\n this config option has no effect unless `core.sparseCheckout` is\n `true`.\n+\n+sparse.onlyCheckFilesMatchPatterns::\n+    By Default with sparse checkouts, all files with SKIP_WORKTREE bit\n+    in the index must check whether exists in the worktree. And Git will\n+    expand all of that files in the index and must traverse recursively\n+    in the worktree. This option can be used to tell Git that just check\n+    files which can match the sparse-checkout patterns.\n++\n+The default is `false`, which allows Git to automatically recover\n+from the list of files in the index and working tree falling out of\n+sync.\n++\n+Set this to `true` if you just hope Git maintain the consistency\n+between the presence of working tree files and sparsity patterns\n+confining to which match the sparse-checkout patterns.For example,\n+if you are using a visual file system in order to on-demand loading\n+working tree lazily. It can control which directory should load by\n+using sparse-checkout patterns. But it doesn't want to visit other\n+directory which haven't been load but just have an empty directory\n+for the entry of the file loading switch.\n++\n+Regardless of this setting, Git does not check for\n+present-despite-skipped files unless sparse checkout is enabled, so\n+this config option has no effect unless `core.sparseCheckout` is\n+`true`.\n\\ No newline at end of file\ndiff --git a/cache.h b/cache.h\nindex ac5ab4ef9d..d1ed717403 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1074,6 +1074,7 @@ extern int protect_ntfs;\n extern int core_apply_sparse_checkout;\n extern int core_sparse_checkout_cone;\n extern int sparse_expect_files_outside_of_patterns;\n+extern int sparse_only_check_files_match_patterns;\n\n /*\n  * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).\ndiff --git a/config.c b/config.c\nindex 9b0e9c9328..5ab9719479 100644\n--- a/config.c\n+++ b/config.c\n@@ -1758,6 +1758,11 @@ static int git_default_sparse_config(const char\n*var, const char *value)\n      return 0;\n   }\n\n+  if (!strcmp(var, \"sparse.onlycheckfilesmatchpatterns\")) {\n+     sparse_only_check_files_match_patterns = git_config_bool(var, value);\n+     return 0;\n+  }\n+\n   /* Add other config variables here and to Documentation/config/sparse.txt. */\n   return 0;\n }\ndiff --git a/environment.c b/environment.c\nindex b3296ce7d1..0ee4190c7d 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -73,6 +73,7 @@ int grafts_replace_parents = 1;\n int core_apply_sparse_checkout;\n int core_sparse_checkout_cone;\n int sparse_expect_files_outside_of_patterns;\n+int sparse_only_check_files_match_patterns;\n int merge_log_config = -1;\n int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */\n unsigned long pack_size_limit_cfg;\ndiff --git a/sparse-index.c b/sparse-index.c\nindex e4a54ce194..264f4965e2 100644\n--- a/sparse-index.c\n+++ b/sparse-index.c\n@@ -502,14 +502,21 @@ void\nclear_skip_worktree_from_present_files(struct index_state *istate)\n   for (i = 0; i < istate->cache_nr; i++) {\n      struct cache_entry *ce = istate->cache[i];\n\n-     if (ce_skip_worktree(ce) &&\n-         path_found(ce->name, &last_dirname, &dir_len, &dir_found)) {\n-        if (S_ISSPARSEDIR(ce->ce_mode)) {\n-           ensure_full_index(istate);\n-           goto restart;\n+     if (ce_skip_worktree(ce)) {\n+        if (sparse_only_check_files_match_patterns &&\n+            !path_in_sparse_checkout(ce->name, istate)){\n+           continue;\n+        }\n+\n+        if (path_found(ce->name, &last_dirname, &dir_len, &dir_found)) {\n+           if (S_ISSPARSEDIR(ce->ce_mode)) {\n+              ensure_full_index(istate);\n+              goto restart;\n+           }\n+           ce->ce_flags &= ~CE_SKIP_WORKTREE;\n         }\n-        ce->ce_flags &= ~CE_SKIP_WORKTREE;\n      }\n+\n   }\n }\n\ndiff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh\nindex d1833c0f31..da8173ebe5 100755\n--- a/t/t1090-sparse-checkout-scope.sh\n+++ b/t/t1090-sparse-checkout-scope.sh\n@@ -71,6 +71,56 @@ test_expect_success 'skip-worktree on files outside\nsparse patterns' '\n   test_cmp expect actual\n '\n\n+test_expect_success 'skip-worktree on files only matched patterns. default' '\n+  git clean -f . &&\n+  git update-index  --refresh &&\n+  git sparse-checkout disable &&\n+  git sparse-checkout set --no-cone \"a*\" &&\n+  test_config sparse.expectFilesOutsideOfPatterns false &&\n+  git checkout-index --all --ignore-skip-worktree-bits &&\n+\n+  git ls-files -t >output &&\n+  ! grep ^S output >actual &&\n+  test_must_be_empty actual\n+'\n+\n+test_expect_success 'skip-worktree on files only matched patterns.\nb,c not in patterns' '\n+  git clean -f . &&\n+  git update-index  --refresh &&\n+  git sparse-checkout disable &&\n+  git sparse-checkout set --no-cone \"a*\" &&\n+  test_config sparse.onlyCheckFilesMatchPatterns true &&\n+  git checkout-index --all --ignore-skip-worktree-bits &&\n+\n+  git ls-files -t >output &&\n+  cat <<-\\EOF >expect &&\n+  H a\n+  S b\n+  S c\n+  EOF\n+  test_cmp expect output >>actual &&\n+  test_must_be_empty actual\n+'\n+\n+test_expect_success 'skip-worktree on files only matched patterns.\nfile b in patterns c is not' '\n+  git clean -f . &&\n+  git update-index  --refresh &&\n+  git sparse-checkout disable &&\n+  git sparse-checkout set --no-cone \"a*\" &&\n+  test_config sparse.onlyCheckFilesMatchPatterns true &&\n+  echo \"b*\" >>.git/info/sparse-checkout &&\n+  git checkout-index --all --ignore-skip-worktree-bits &&\n+\n+  git ls-files -t >output &&\n+  cat <<-\\EOF >expect &&\n+  H a\n+  H b\n+  S c\n+  EOF\n+  test_cmp expect output >>actual &&\n+  test_must_be_empty actual\n+'\n+\n test_expect_success 'in partial clone, sparse checkout only fetches\nneeded blobs' '\n   test_create_repo server &&\n   git clone \"file://$(pwd)/server\" client &&\n-- \n2.36.1\n"}]}