Skip to content

Commit 190f9bf

Browse files
committed
Merge branch 'vd/sparse-read-tree'
"git read-tree" has been made to be aware of the sparse-index feature. * vd/sparse-read-tree: read-tree: make three-way merge sparse-aware read-tree: make two-way merge sparse-aware read-tree: narrow scope of index expansion for '--prefix' read-tree: integrate with sparse index read-tree: expand sparse checkout test coverage read-tree: explicitly disallow prefixes with a leading '/' status: fix nested sparse directory diff in sparse index sparse-index: prevent repo root from becoming sparse
2 parents 430883a + f27c170 commit 190f9bf

File tree

7 files changed

+308
-13
lines changed

7 files changed

+308
-13
lines changed

builtin/read-tree.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,22 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
160160
argc = parse_options(argc, argv, cmd_prefix, read_tree_options,
161161
read_tree_usage, 0);
162162

163-
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
164-
165163
prefix_set = opts.prefix ? 1 : 0;
166164
if (1 < opts.merge + opts.reset + prefix_set)
167165
die("Which one? -m, --reset, or --prefix?");
168166

167+
/* Prefix should not start with a directory separator */
168+
if (opts.prefix && opts.prefix[0] == '/')
169+
die("Invalid prefix, prefix cannot start with '/'");
170+
169171
if (opts.reset)
170172
opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
171173

174+
prepare_repo_settings(the_repository);
175+
the_repository->settings.command_requires_full_index = 0;
176+
177+
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
178+
172179
/*
173180
* NEEDSWORK
174181
*
@@ -210,6 +217,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
210217
if (opts.merge && !opts.index_only)
211218
setup_work_tree();
212219

220+
if (opts.skip_sparse_checkout)
221+
ensure_full_index(&the_index);
222+
213223
if (opts.merge) {
214224
switch (stage - 1) {
215225
case 0:

dir.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,10 +1463,11 @@ static int path_in_sparse_checkout_1(const char *path,
14631463
const char *end, *slash;
14641464

14651465
/*
1466-
* We default to accepting a path if there are no patterns or
1467-
* they are of the wrong type.
1466+
* We default to accepting a path if the path is empty, there are no
1467+
* patterns, or the patterns are of the wrong type.
14681468
*/
1469-
if (init_sparse_checkout_patterns(istate) ||
1469+
if (!*path ||
1470+
init_sparse_checkout_patterns(istate) ||
14701471
(require_cone_mode &&
14711472
!istate->sparse_checkout_patterns->use_cone_patterns))
14721473
return 1;

t/perf/p2000-sparse-operations.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ test_perf_on_all git diff
117117
test_perf_on_all git diff --cached
118118
test_perf_on_all git blame $SPARSE_CONE/a
119119
test_perf_on_all git blame $SPARSE_CONE/f3/a
120+
test_perf_on_all git read-tree -mu HEAD
120121
test_perf_on_all git checkout-index -f --all
121122
test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
122123

t/t1003-read-tree-prefix.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,14 @@ test_expect_success 'read-tree --prefix' '
2525
cmp expect actual
2626
'
2727

28+
test_expect_success 'read-tree --prefix with leading slash exits with error' '
29+
git rm -rf . &&
30+
test_must_fail git read-tree --prefix=/two/ $tree &&
31+
git read-tree --prefix=two/ $tree &&
32+
33+
git rm -rf . &&
34+
test_must_fail git read-tree --prefix=/ $tree &&
35+
git read-tree --prefix= $tree
36+
'
37+
2838
test_done

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,24 @@ test_expect_success 'expanded in-memory index matches full index' '
244244
test_sparse_match git ls-files --stage
245245
'
246246

247+
test_expect_success 'root directory cannot be sparse' '
248+
init_repos &&
249+
250+
# Remove all in-cone files and directories from the index, collapse index
251+
# with `git sparse-checkout reapply`
252+
git -C sparse-index rm -r . &&
253+
git -C sparse-index sparse-checkout reapply &&
254+
255+
# Verify sparse directories still present, root directory is not sparse
256+
cat >expect <<-EOF &&
257+
folder1/
258+
folder2/
259+
x/
260+
EOF
261+
git -C sparse-index ls-files --sparse >actual &&
262+
test_cmp expect actual
263+
'
264+
247265
test_expect_success 'status with options' '
248266
init_repos &&
249267
test_sparse_match ls &&
@@ -260,6 +278,13 @@ test_expect_success 'status with options' '
260278
test_all_match git status --porcelain=v2 -uno
261279
'
262280

281+
test_expect_success 'status with diff in unexpanded sparse directory' '
282+
init_repos &&
283+
test_all_match git checkout rename-base &&
284+
test_all_match git reset --soft rename-out-to-out &&
285+
test_all_match git status --porcelain=v2
286+
'
287+
263288
test_expect_success 'status reports sparse-checkout' '
264289
init_repos &&
265290
git -C sparse-checkout status >full &&
@@ -794,6 +819,93 @@ test_expect_success 'update-index --cacheinfo' '
794819
test_cmp expect sparse-checkout-out
795820
'
796821

822+
for MERGE_TREES in "base HEAD update-folder2" \
823+
"update-folder1 update-folder2" \
824+
"update-folder2"
825+
do
826+
test_expect_success "'read-tree -mu $MERGE_TREES' with files outside sparse definition" '
827+
init_repos &&
828+
829+
# Although the index matches, without --no-sparse-checkout, outside-of-
830+
# definition files will not exist on disk for sparse checkouts
831+
test_all_match git read-tree -mu $MERGE_TREES &&
832+
test_all_match git status --porcelain=v2 &&
833+
test_path_is_missing sparse-checkout/folder2 &&
834+
test_path_is_missing sparse-index/folder2 &&
835+
836+
test_all_match git read-tree --reset -u HEAD &&
837+
test_all_match git status --porcelain=v2 &&
838+
839+
test_all_match git read-tree -mu --no-sparse-checkout $MERGE_TREES &&
840+
test_all_match git status --porcelain=v2 &&
841+
test_cmp sparse-checkout/folder2/a sparse-index/folder2/a &&
842+
test_cmp sparse-checkout/folder2/a full-checkout/folder2/a
843+
844+
'
845+
done
846+
847+
test_expect_success 'read-tree --merge with edit/edit conflicts in sparse directories' '
848+
init_repos &&
849+
850+
# Merge of multiple changes to same directory (but not same files) should
851+
# succeed
852+
test_all_match git read-tree -mu base rename-base update-folder1 &&
853+
test_all_match git status --porcelain=v2 &&
854+
855+
test_all_match git reset --hard &&
856+
857+
test_all_match git read-tree -mu rename-base update-folder2 &&
858+
test_all_match git status --porcelain=v2 &&
859+
860+
test_all_match git reset --hard &&
861+
862+
test_all_match test_must_fail git read-tree -mu base update-folder1 rename-out-to-in &&
863+
test_all_match test_must_fail git read-tree -mu rename-out-to-in update-folder1
864+
'
865+
866+
test_expect_success 'read-tree --prefix' '
867+
init_repos &&
868+
869+
# If files differing between the index and target <commit-ish> exist
870+
# inside the prefix, `read-tree --prefix` should fail
871+
test_all_match test_must_fail git read-tree --prefix=deep/ deepest &&
872+
test_all_match test_must_fail git read-tree --prefix=folder1/ update-folder1 &&
873+
874+
# If no differing index entries exist matching the prefix,
875+
# `read-tree --prefix` updates the index successfully
876+
test_all_match git rm -rf deep/deeper1/deepest/ &&
877+
test_all_match git read-tree --prefix=deep/deeper1/deepest -u deepest &&
878+
test_all_match git status --porcelain=v2 &&
879+
880+
test_all_match git rm -rf --sparse folder1/ &&
881+
test_all_match git read-tree --prefix=folder1/ -u update-folder1 &&
882+
test_all_match git status --porcelain=v2 &&
883+
884+
test_all_match git rm -rf --sparse folder2/0 &&
885+
test_all_match git read-tree --prefix=folder2/0/ -u rename-out-to-out &&
886+
test_all_match git status --porcelain=v2
887+
'
888+
889+
test_expect_success 'read-tree --merge with directory-file conflicts' '
890+
init_repos &&
891+
892+
test_all_match git checkout -b test-branch rename-base &&
893+
894+
# Although the index matches, without --no-sparse-checkout, outside-of-
895+
# definition files will not exist on disk for sparse checkouts
896+
test_sparse_match git read-tree -mu rename-out-to-out &&
897+
test_sparse_match git status --porcelain=v2 &&
898+
test_path_is_missing sparse-checkout/folder2 &&
899+
test_path_is_missing sparse-index/folder2 &&
900+
901+
test_sparse_match git read-tree --reset -u HEAD &&
902+
test_sparse_match git status --porcelain=v2 &&
903+
904+
test_sparse_match git read-tree -mu --no-sparse-checkout rename-out-to-out &&
905+
test_sparse_match git status --porcelain=v2 &&
906+
test_cmp sparse-checkout/folder2/0/1 sparse-index/folder2/0/1
907+
'
908+
797909
test_expect_success 'merge, cherry-pick, and rebase' '
798910
init_repos &&
799911
@@ -1297,6 +1409,27 @@ test_expect_success 'sparse index is not expanded: fetch/pull' '
12971409
ensure_not_expanded pull full base
12981410
'
12991411

1412+
test_expect_success 'sparse index is not expanded: read-tree' '
1413+
init_repos &&
1414+
1415+
ensure_not_expanded checkout -b test-branch update-folder1 &&
1416+
for MERGE_TREES in "base HEAD update-folder2" \
1417+
"base HEAD rename-base" \
1418+
"base update-folder2" \
1419+
"base rename-base" \
1420+
"update-folder2"
1421+
do
1422+
ensure_not_expanded read-tree -mu $MERGE_TREES &&
1423+
ensure_not_expanded reset --hard || return 1
1424+
done &&
1425+
1426+
rm -rf sparse-index/deep/deeper2 &&
1427+
ensure_not_expanded add . &&
1428+
ensure_not_expanded commit -m "test" &&
1429+
1430+
ensure_not_expanded read-tree --prefix=deep/deeper2 -u deepest
1431+
'
1432+
13001433
test_expect_success 'ls-files' '
13011434
init_repos &&
13021435

0 commit comments

Comments
 (0)