Skip to content

Commit 354356f

Browse files
committed
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'
2 parents 5dc72c0 + f981587 commit 354356f

File tree

4 files changed

+115
-18
lines changed

4 files changed

+115
-18
lines changed

attr.c

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -807,35 +807,56 @@ static struct attr_stack *read_attr_from_blob(struct index_state *istate,
807807
static struct attr_stack *read_attr_from_index(struct index_state *istate,
808808
const char *path, unsigned flags)
809809
{
810+
struct attr_stack *stack = NULL;
810811
char *buf;
811812
unsigned long size;
813+
int sparse_dir_pos = -1;
812814

813815
if (!istate)
814816
return NULL;
815817

816818
/*
817-
* The .gitattributes file only applies to files within its
818-
* parent directory. In the case of cone-mode sparse-checkout,
819-
* the .gitattributes file is sparse if and only if all paths
820-
* within that directory are also sparse. Thus, don't load the
821-
* .gitattributes file since it will not matter.
822-
*
823-
* In the case of a sparse index, it is critical that we don't go
824-
* looking for a .gitattributes file, as doing so would cause the
825-
* index to expand.
819+
* When handling sparse-checkouts, .gitattributes files
820+
* may reside within a sparse directory. We distinguish
821+
* whether a path exists directly in the index or not by
822+
* evaluating if 'pos' is negative.
823+
* If 'pos' is negative, the path is not directly present
824+
* in the index and is likely within a sparse directory.
825+
* For paths not in the index, The absolute value of 'pos'
826+
* minus 1 gives us the position where the path would be
827+
* inserted in lexicographic order within the index.
828+
* We then subtract another 1 from this value
829+
* (sparse_dir_pos = -pos - 2) to find the position of the
830+
* last index entry which is lexicographically smaller than
831+
* the path. This would be the sparse directory containing
832+
* the path. By identifying the sparse directory containing
833+
* the path, we can correctly read the attributes specified
834+
* in the .gitattributes file from the tree object of the
835+
* sparse directory.
826836
*/
827-
if (!path_in_cone_mode_sparse_checkout(path, istate))
828-
return NULL;
837+
if (!path_in_cone_mode_sparse_checkout(path, istate)) {
838+
int pos = index_name_pos_sparse(istate, path, strlen(path));
829839

830-
buf = read_blob_data_from_index(istate, path, &size);
831-
if (!buf)
832-
return NULL;
833-
if (size >= ATTR_MAX_FILE_SIZE) {
834-
warning(_("ignoring overly large gitattributes blob '%s'"), path);
835-
return NULL;
840+
if (pos < 0)
841+
sparse_dir_pos = -pos - 2;
836842
}
837843

838-
return read_attr_from_buf(buf, path, flags);
844+
if (sparse_dir_pos >= 0 &&
845+
S_ISSPARSEDIR(istate->cache[sparse_dir_pos]->ce_mode) &&
846+
!strncmp(istate->cache[sparse_dir_pos]->name, path, ce_namelen(istate->cache[sparse_dir_pos]))) {
847+
const char *relative_path = path + ce_namelen(istate->cache[sparse_dir_pos]);
848+
stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags);
849+
} else {
850+
buf = read_blob_data_from_index(istate, path, &size);
851+
if (!buf)
852+
return NULL;
853+
if (size >= ATTR_MAX_FILE_SIZE) {
854+
warning(_("ignoring overly large gitattributes blob '%s'"), path);
855+
return NULL;
856+
}
857+
stack = read_attr_from_buf(buf, path, flags);
858+
}
859+
return stack;
839860
}
840861

841862
static struct attr_stack *read_attr(struct index_state *istate,

builtin/check-attr.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
122122
argc = parse_options(argc, argv, prefix, check_attr_options,
123123
check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
124124

125+
prepare_repo_settings(the_repository);
126+
the_repository->settings.command_requires_full_index = 0;
127+
125128
if (repo_read_index(the_repository) < 0) {
126129
die("invalid cache");
127130
}

t/perf/p2000-sparse-operations.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,6 @@ test_perf_on_all git diff-files -- $SPARSE_CONE/a
134134
test_perf_on_all git diff-tree HEAD
135135
test_perf_on_all git diff-tree HEAD -- $SPARSE_CONE/a
136136
test_perf_on_all "git worktree add ../temp && git worktree remove ../temp"
137+
test_perf_on_all git check-attr -a -- $SPARSE_CONE/a
137138

138139
test_done

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,4 +2259,76 @@ test_expect_success 'worktree is not expanded' '
22592259
ensure_not_expanded worktree remove .worktrees/hotfix
22602260
'
22612261

2262+
test_expect_success 'check-attr with pathspec inside sparse definition' '
2263+
init_repos &&
2264+
2265+
echo "a -crlf myAttr" >>.gitattributes &&
2266+
run_on_all cp ../.gitattributes ./deep &&
2267+
2268+
test_all_match git check-attr -a -- deep/a &&
2269+
2270+
test_all_match git add deep/.gitattributes &&
2271+
test_all_match git check-attr -a --cached -- deep/a
2272+
'
2273+
2274+
test_expect_success 'check-attr with pathspec outside sparse definition' '
2275+
init_repos &&
2276+
2277+
echo "a -crlf myAttr" >>.gitattributes &&
2278+
run_on_sparse mkdir folder1 &&
2279+
run_on_all cp ../.gitattributes ./folder1 &&
2280+
run_on_all cp a folder1/a &&
2281+
2282+
test_all_match git check-attr -a -- folder1/a &&
2283+
2284+
git -C full-checkout add folder1/.gitattributes &&
2285+
test_sparse_match git add --sparse folder1/.gitattributes &&
2286+
test_all_match git commit -m "add .gitattributes" &&
2287+
test_sparse_match git sparse-checkout reapply &&
2288+
test_all_match git check-attr -a --cached -- folder1/a
2289+
'
2290+
2291+
# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due
2292+
# to an underlying issue in oneway_diff() within diff-lib.c.
2293+
# 'do_oneway_diff()' is not called as expected for paths that could match
2294+
# inside of a sparse directory. Specifically, the 'ce_path_match()' function
2295+
# fails to recognize files inside a sparse directory (e.g., when 'folder1/'
2296+
# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to
2297+
# proceed with 'do_oneway_diff()' if the pathspec could match inside of a
2298+
# sparse directory.
2299+
test_expect_failure 'diff --check with pathspec outside sparse definition' '
2300+
init_repos &&
2301+
2302+
write_script edit-contents <<-\EOF &&
2303+
echo "a " >"$1"
2304+
EOF
2305+
2306+
test_all_match git config core.whitespace -trailing-space,-space-before-tab &&
2307+
2308+
echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes &&
2309+
run_on_all mkdir -p folder1 &&
2310+
run_on_all cp ../.gitattributes ./folder1 &&
2311+
test_all_match git add --sparse folder1/.gitattributes &&
2312+
run_on_all ../edit-contents folder1/a &&
2313+
test_all_match git add --sparse folder1/a &&
2314+
2315+
test_sparse_match git sparse-checkout reapply &&
2316+
test_all_match test_must_fail git diff --check --cached -- folder1/a
2317+
'
2318+
2319+
test_expect_success 'sparse-index is not expanded: check-attr' '
2320+
init_repos &&
2321+
2322+
echo "a -crlf myAttr" >>.gitattributes &&
2323+
mkdir ./sparse-index/folder1 &&
2324+
cp ./sparse-index/a ./sparse-index/folder1/a &&
2325+
cp .gitattributes ./sparse-index/deep &&
2326+
cp .gitattributes ./sparse-index/folder1 &&
2327+
2328+
git -C sparse-index add deep/.gitattributes &&
2329+
git -C sparse-index add --sparse folder1/.gitattributes &&
2330+
ensure_not_expanded check-attr -a --cached -- deep/a &&
2331+
ensure_not_expanded check-attr -a --cached -- folder1/a
2332+
'
2333+
22622334
test_done

0 commit comments

Comments
 (0)