aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2023-08-29 13:51:43 -0700
committerJunio C Hamano <gitster@pobox.com>2023-08-29 13:51:43 -0700
commit354356feffed7b3c72d62cd78fc7575efd5bad42 (patch)
tree88f147927487068d8e2e9f4bc6ccd40a3c668a53
parentThe extra batch to update credenthal helpers (diff)
parentcheck-attr: integrate with sparse-index (diff)
downloadgit-354356feffed7b3c72d62cd78fc7575efd5bad42.tar.gz
git-354356feffed7b3c72d62cd78fc7575efd5bad42.zip
Merge branch 'sl/sparse-check-attr'
Teach "git check-attr" work better with sparse-index. * sl/sparse-check-attr: check-attr: integrate with sparse-index attr.c: read attributes in a sparse directory t1092: add tests for 'git check-attr'
-rw-r--r--attr.c57
-rw-r--r--builtin/check-attr.c3
-rwxr-xr-xt/perf/p2000-sparse-operations.sh1
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh72
4 files changed, 115 insertions, 18 deletions
diff --git a/attr.c b/attr.c
index ff0a3e7b61..71c84fbcf8 100644
--- a/attr.c
+++ b/attr.c
@@ -807,35 +807,56 @@ static struct attr_stack *read_attr_from_blob(struct index_state *istate,
static struct attr_stack *read_attr_from_index(struct index_state *istate,
const char *path, unsigned flags)
{
+ struct attr_stack *stack = NULL;
char *buf;
unsigned long size;
+ int sparse_dir_pos = -1;
if (!istate)
return NULL;
/*
- * The .gitattributes file only applies to files within its
- * parent directory. In the case of cone-mode sparse-checkout,
- * the .gitattributes file is sparse if and only if all paths
- * within that directory are also sparse. Thus, don't load the
- * .gitattributes file since it will not matter.
- *
- * In the case of a sparse index, it is critical that we don't go
- * looking for a .gitattributes file, as doing so would cause the
- * index to expand.
+ * When handling sparse-checkouts, .gitattributes files
+ * may reside within a sparse directory. We distinguish
+ * whether a path exists directly in the index or not by
+ * evaluating if 'pos' is negative.
+ * If 'pos' is negative, the path is not directly present
+ * in the index and is likely within a sparse directory.
+ * For paths not in the index, The absolute value of 'pos'
+ * minus 1 gives us the position where the path would be
+ * inserted in lexicographic order within the index.
+ * We then subtract another 1 from this value
+ * (sparse_dir_pos = -pos - 2) to find the position of the
+ * last index entry which is lexicographically smaller than
+ * the path. This would be the sparse directory containing
+ * the path. By identifying the sparse directory containing
+ * the path, we can correctly read the attributes specified
+ * in the .gitattributes file from the tree object of the
+ * sparse directory.
*/
- if (!path_in_cone_mode_sparse_checkout(path, istate))
- return NULL;
+ if (!path_in_cone_mode_sparse_checkout(path, istate)) {
+ int pos = index_name_pos_sparse(istate, path, strlen(path));
- buf = read_blob_data_from_index(istate, path, &size);
- if (!buf)
- return NULL;
- if (size >= ATTR_MAX_FILE_SIZE) {
- warning(_("ignoring overly large gitattributes blob '%s'"), path);
- return NULL;
+ if (pos < 0)
+ sparse_dir_pos = -pos - 2;
}
- return read_attr_from_buf(buf, path, flags);
+ if (sparse_dir_pos >= 0 &&
+ S_ISSPARSEDIR(istate->cache[sparse_dir_pos]->ce_mode) &&
+ !strncmp(istate->cache[sparse_dir_pos]->name, path, ce_namelen(istate->cache[sparse_dir_pos]))) {
+ const char *relative_path = path + ce_namelen(istate->cache[sparse_dir_pos]);
+ stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags);
+ } else {
+ buf = read_blob_data_from_index(istate, path, &size);
+ if (!buf)
+ return NULL;
+ if (size >= ATTR_MAX_FILE_SIZE) {
+ warning(_("ignoring overly large gitattributes blob '%s'"), path);
+ return NULL;
+ }
+ stack = read_attr_from_buf(buf, path, flags);
+ }
+ return stack;
}
static struct attr_stack *read_attr(struct index_state *istate,
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index b22ff748c3..c1da1d184e 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -122,6 +122,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, check_attr_options,
check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
if (repo_read_index(the_repository) < 0) {
die("invalid cache");
}
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
index 96ed3e1d69..39e92b0841 100755
--- a/t/perf/p2000-sparse-operations.sh
+++ b/t/perf/p2000-sparse-operations.sh
@@ -134,5 +134,6 @@ test_perf_on_all git diff-files -- $SPARSE_CONE/a
test_perf_on_all git diff-tree HEAD
test_perf_on_all git diff-tree HEAD -- $SPARSE_CONE/a
test_perf_on_all "git worktree add ../temp && git worktree remove ../temp"
+test_perf_on_all git check-attr -a -- $SPARSE_CONE/a
test_done
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 8a95adf4b5..2a4f35e984 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -2259,4 +2259,76 @@ test_expect_success 'worktree is not expanded' '
ensure_not_expanded worktree remove .worktrees/hotfix
'
+test_expect_success 'check-attr with pathspec inside sparse definition' '
+ init_repos &&
+
+ echo "a -crlf myAttr" >>.gitattributes &&
+ run_on_all cp ../.gitattributes ./deep &&
+
+ test_all_match git check-attr -a -- deep/a &&
+
+ test_all_match git add deep/.gitattributes &&
+ test_all_match git check-attr -a --cached -- deep/a
+'
+
+test_expect_success 'check-attr with pathspec outside sparse definition' '
+ init_repos &&
+
+ echo "a -crlf myAttr" >>.gitattributes &&
+ run_on_sparse mkdir folder1 &&
+ run_on_all cp ../.gitattributes ./folder1 &&
+ run_on_all cp a folder1/a &&
+
+ test_all_match git check-attr -a -- folder1/a &&
+
+ git -C full-checkout add folder1/.gitattributes &&
+ test_sparse_match git add --sparse folder1/.gitattributes &&
+ test_all_match git commit -m "add .gitattributes" &&
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match git check-attr -a --cached -- folder1/a
+'
+
+# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due
+# to an underlying issue in oneway_diff() within diff-lib.c.
+# 'do_oneway_diff()' is not called as expected for paths that could match
+# inside of a sparse directory. Specifically, the 'ce_path_match()' function
+# fails to recognize files inside a sparse directory (e.g., when 'folder1/'
+# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to
+# proceed with 'do_oneway_diff()' if the pathspec could match inside of a
+# sparse directory.
+test_expect_failure 'diff --check with pathspec outside sparse definition' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo "a " >"$1"
+ EOF
+
+ test_all_match git config core.whitespace -trailing-space,-space-before-tab &&
+
+ echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes &&
+ run_on_all mkdir -p folder1 &&
+ run_on_all cp ../.gitattributes ./folder1 &&
+ test_all_match git add --sparse folder1/.gitattributes &&
+ run_on_all ../edit-contents folder1/a &&
+ test_all_match git add --sparse folder1/a &&
+
+ test_sparse_match git sparse-checkout reapply &&
+ test_all_match test_must_fail git diff --check --cached -- folder1/a
+'
+
+test_expect_success 'sparse-index is not expanded: check-attr' '
+ init_repos &&
+
+ echo "a -crlf myAttr" >>.gitattributes &&
+ mkdir ./sparse-index/folder1 &&
+ cp ./sparse-index/a ./sparse-index/folder1/a &&
+ cp .gitattributes ./sparse-index/deep &&
+ cp .gitattributes ./sparse-index/folder1 &&
+
+ git -C sparse-index add deep/.gitattributes &&
+ git -C sparse-index add --sparse folder1/.gitattributes &&
+ ensure_not_expanded check-attr -a --cached -- deep/a &&
+ ensure_not_expanded check-attr -a --cached -- folder1/a
+'
+
test_done