Skip to content

Commit

Permalink
Merge branch 'ds/commit-and-checkout-with-sparse-index' into seen
Browse files Browse the repository at this point in the history
"git checkout" and "git commit" learn to work without unnecessarily
expanding sparse indexes.

* ds/commit-and-checkout-with-sparse-index:
  unpack-trees: resolve sparse-directory/file conflicts
  t1092: document bad 'git checkout' behavior
  checkout: stop expanding sparse indexes
  sparse-index: recompute cache-tree
  commit: integrate with sparse-index
  p2000: compress repo names
  p2000: add 'git checkout -' test and decrease depth
  • Loading branch information
gitster committed Jul 20, 2021
2 parents 4655697 + e05cdb1 commit eedf36e
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 30 deletions.
8 changes: 3 additions & 5 deletions builtin/checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,6 @@ static int checkout_worktree(const struct checkout_opts *opts,
if (pc_workers > 1)
init_parallel_checkout();

/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);

for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
if (ce->ce_flags & CE_MATCHED) {
Expand Down Expand Up @@ -537,8 +534,6 @@ static int checkout_paths(const struct checkout_opts *opts,
* Make sure all pathspecs participated in locating the paths
* to be checked out.
*/
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);
for (pos = 0; pos < active_nr; pos++)
if (opts->overlay_mode)
mark_ce_for_checkout_overlay(active_cache[pos],
Expand Down Expand Up @@ -1600,6 +1595,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,

git_config(git_checkout_config, opts);

prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;

opts->track = BRANCH_TRACK_UNSPECIFIED;

if (!opts->accept_pathspec && !opts->accept_ref)
Expand Down
3 changes: 3 additions & 0 deletions builtin/commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1693,6 +1693,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_commit_usage, builtin_commit_options);

prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;

status_init_config(&s, git_commit_config);
s.commit_template = 1;
status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
Expand Down
2 changes: 0 additions & 2 deletions cache-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,6 @@ int cache_tree_update(struct index_state *istate, int flags)
if (i)
return i;

ensure_full_index(istate);

if (!istate->cache_tree)
istate->cache_tree = cache_tree();

Expand Down
2 changes: 2 additions & 0 deletions sparse-index.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ int convert_to_sparse(struct index_state *istate)
if (index_has_unmerged_entries(istate))
return 0;

/* Clear and recompute the cache-tree */
cache_tree_free(&istate->cache_tree);
if (cache_tree_update(istate, 0)) {
warning(_("unable to update cache-tree, staying full"));
return -1;
Expand Down
47 changes: 30 additions & 17 deletions t/perf/p2000-sparse-operations.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ test_description="test performance of Git operations using the index"

test_perf_default_repo

SPARSE_CONE=f2/f4/f1
SPARSE_CONE=f2/f4

test_expect_success 'setup repo and indexes' '
git reset --hard HEAD &&
Expand All @@ -27,7 +27,7 @@ test_expect_success 'setup repo and indexes' '
OLD_COMMIT=$(git rev-parse HEAD) &&
OLD_TREE=$(git rev-parse HEAD^{tree}) &&
for i in $(test_seq 1 4)
for i in $(test_seq 1 3)
do
cat >in <<-EOF &&
100755 blob $BLOB a
Expand All @@ -43,45 +43,57 @@ test_expect_success 'setup repo and indexes' '
done &&
git sparse-checkout init --cone &&
git branch -f wide $OLD_COMMIT &&
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . full-index-v3 &&
git sparse-checkout set $SPARSE_CONE &&
git checkout -b wide $OLD_COMMIT &&
for l2 in f1 f2 f3 f4
do
echo more bogus >>$SPARSE_CONE/$l2/a &&
git commit -a -m "edit $SPARSE_CONE/$l2/a" || return 1
done &&
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . full-v3 &&
(
cd full-index-v3 &&
cd full-v3 &&
git sparse-checkout init --cone &&
git sparse-checkout set $SPARSE_CONE &&
git config index.version 3 &&
git update-index --index-version=3
git update-index --index-version=3 &&
git checkout HEAD~4
) &&
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . full-index-v4 &&
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . full-v4 &&
(
cd full-index-v4 &&
cd full-v4 &&
git sparse-checkout init --cone &&
git sparse-checkout set $SPARSE_CONE &&
git config index.version 4 &&
git update-index --index-version=4
git update-index --index-version=4 &&
git checkout HEAD~4
) &&
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . sparse-index-v3 &&
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . sparse-v3 &&
(
cd sparse-index-v3 &&
cd sparse-v3 &&
git sparse-checkout init --cone --sparse-index &&
git sparse-checkout set $SPARSE_CONE &&
git config index.version 3 &&
git update-index --index-version=3
git update-index --index-version=3 &&
git checkout HEAD~4
) &&
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . sparse-index-v4 &&
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . sparse-v4 &&
(
cd sparse-index-v4 &&
cd sparse-v4 &&
git sparse-checkout init --cone --sparse-index &&
git sparse-checkout set $SPARSE_CONE &&
git config index.version 4 &&
git update-index --index-version=4
git update-index --index-version=4 &&
git checkout HEAD~4
)
'

test_perf_on_all () {
command="$@"
for repo in full-index-v3 full-index-v4 \
sparse-index-v3 sparse-index-v4
for repo in full-v3 full-v4 \
sparse-v3 sparse-v4
do
test_perf "$command ($repo)" "
(
Expand All @@ -97,5 +109,6 @@ test_perf_on_all git status
test_perf_on_all git add -A
test_perf_on_all git add .
test_perf_on_all git commit -a -m A
test_perf_on_all git checkout -f -

test_done
197 changes: 191 additions & 6 deletions t/t1092-sparse-checkout-compatibility.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,25 @@ test_expect_success 'setup' '
git add . &&
git commit -m "rename deep/deeper1/... to folder1/..." &&
git checkout -b df-conflict-1 base &&
rm -rf folder1 &&
echo content >folder1 &&
git add . &&
git commit -m "dir to file" &&
git checkout -b df-conflict-2 base &&
rm -rf folder2 &&
echo content >folder2 &&
git add . &&
git commit -m "dir to file" &&
git checkout -b fd-conflict base &&
rm a &&
mkdir a &&
echo content >a/a &&
git add . &&
git commit -m "file to dir" &&
git checkout -b deepest base &&
echo "updated deepest" >deep/deeper1/deepest/a &&
git commit -a -m "update deepest" &&
Expand Down Expand Up @@ -262,6 +281,34 @@ test_expect_success 'add, commit, checkout' '
test_all_match git checkout -
'

test_expect_success 'commit including unstaged changes' '
init_repos &&
write_script edit-file <<-\EOF &&
echo $1 >$2
EOF
run_on_all ../edit-file 1 a &&
run_on_all ../edit-file 1 deep/a &&
test_all_match git commit -m "-a" -a &&
test_all_match git status --porcelain=v2 &&
run_on_all ../edit-file 2 a &&
run_on_all ../edit-file 2 deep/a &&
test_all_match git commit -m "--include" --include deep/a &&
test_all_match git status --porcelain=v2 &&
test_all_match git commit -m "--include" --include a &&
test_all_match git status --porcelain=v2 &&
run_on_all ../edit-file 3 a &&
run_on_all ../edit-file 3 deep/a &&
test_all_match git commit -m "--amend" -a --amend &&
test_all_match git status --porcelain=v2
'

test_expect_success 'status/add: outside sparse cone' '
init_repos &&
Expand Down Expand Up @@ -330,10 +377,16 @@ test_expect_success 'diff --staged' '
test_all_match git diff --staged
'

# NEEDSWORK: sparse-checkout behaves differently from full-checkout when
# running this test with 'df-conflict-2' after 'df-conflict-1'.
test_expect_success 'diff with renames and conflicts' '
init_repos &&
for branch in rename-out-to-out rename-out-to-in rename-in-to-out
for branch in rename-out-to-out \
rename-out-to-in \
rename-in-to-out \
df-conflict-1 \
fd-conflict
do
test_all_match git checkout rename-base &&
test_all_match git checkout $branch -- . &&
Expand All @@ -346,7 +399,12 @@ test_expect_success 'diff with renames and conflicts' '
test_expect_success 'diff with directory/file conflicts' '
init_repos &&
for branch in rename-out-to-out rename-out-to-in rename-in-to-out
for branch in rename-out-to-out \
rename-out-to-in \
rename-in-to-out \
df-conflict-1 \
df-conflict-2 \
fd-conflict
do
git -C full-checkout reset --hard &&
test_sparse_match git reset --hard &&
Expand Down Expand Up @@ -514,14 +572,33 @@ test_expect_success 'sparse-index is expanded and converted back' '
test_region index ensure_full_index trace2.txt
'

test_expect_success 'sparse-index is not expanded' '
init_repos &&
ensure_not_expanded () {
rm -f trace2.txt &&
echo >>sparse-index/untracked.txt &&
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
git -C sparse-index status &&
git -C sparse-index "$@" &&
test_region ! index ensure_full_index trace2.txt
}

test_expect_success 'sparse-index is not expanded' '
init_repos &&
ensure_not_expanded status &&
ensure_not_expanded commit --allow-empty -m empty &&
echo >>sparse-index/a &&
ensure_not_expanded commit -a -m a &&
echo >>sparse-index/a &&
ensure_not_expanded commit --include a -m a &&
echo >>sparse-index/deep/deeper1/a &&
ensure_not_expanded commit --include deep/deeper1/a -m deeper &&
ensure_not_expanded checkout rename-out-to-out &&
ensure_not_expanded checkout - &&
ensure_not_expanded switch rename-out-to-out &&
ensure_not_expanded switch - &&
git -C sparse-index reset --hard &&
ensure_not_expanded checkout rename-out-to-out -- deep/deeper1 &&
git -C sparse-index reset --hard &&
ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1
'

# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
Expand Down Expand Up @@ -559,4 +636,112 @@ test_expect_success 'add everything with deep new file' '
test_all_match git status --porcelain=v2
'

# NEEDSWORK: 'git checkout' behaves incorrectly in the case of
# directory/file conflicts, even without sparse-checkout. Use this
# test only as a documentation of the incorrect behavior, not a
# measure of how it _should_ behave.
test_expect_success 'checkout behaves oddly with df-conflict-1' '
init_repos &&
test_sparse_match git sparse-checkout disable &&
write_script edit-content <<-\EOF &&
echo content >>folder1/larger-content
git add folder1
EOF
run_on_all ../edit-content &&
test_all_match git status --porcelain=v2 &&
git -C sparse-checkout sparse-checkout init --cone &&
git -C sparse-index sparse-checkout init --cone --sparse-index &&
test_all_match git status --porcelain=v2 &&
# This checkout command should fail, because we have a staged
# change to folder1/larger-content, but the destination changes
# folder1 to a file.
git -C full-checkout checkout df-conflict-1 \
1>full-checkout-out \
2>full-checkout-err &&
git -C sparse-checkout checkout df-conflict-1 \
1>sparse-checkout-out \
2>sparse-checkout-err &&
git -C sparse-index checkout df-conflict-1 \
1>sparse-index-out \
2>sparse-index-err &&
# Instead, the checkout deletes the folder1 file and adds the
# folder1/larger-content file, leaving all other paths that were
# in folder1/ as deleted (without any warning).
cat >expect <<-EOF &&
D folder1
A folder1/larger-content
EOF
test_cmp expect full-checkout-out &&
test_cmp expect sparse-checkout-out &&
# The sparse-index reports no output
test_must_be_empty sparse-index-out &&
# stderr: Switched to branch df-conflict-1
test_cmp full-checkout-err sparse-checkout-err &&
test_cmp full-checkout-err sparse-checkout-err
'

# NEEDSWORK: 'git checkout' behaves incorrectly in the case of
# directory/file conflicts, even without sparse-checkout. Use this
# test only as a documentation of the incorrect behavior, not a
# measure of how it _should_ behave.
test_expect_success 'checkout behaves oddly with df-conflict-2' '
init_repos &&
test_sparse_match git sparse-checkout disable &&
write_script edit-content <<-\EOF &&
echo content >>folder2/larger-content
git add folder2
EOF
run_on_all ../edit-content &&
test_all_match git status --porcelain=v2 &&
git -C sparse-checkout sparse-checkout init --cone &&
git -C sparse-index sparse-checkout init --cone --sparse-index &&
test_all_match git status --porcelain=v2 &&
# This checkout command should fail, because we have a staged
# change to folder1/larger-content, but the destination changes
# folder1 to a file.
git -C full-checkout checkout df-conflict-2 \
1>full-checkout-out \
2>full-checkout-err &&
git -C sparse-checkout checkout df-conflict-2 \
1>sparse-checkout-out \
2>sparse-checkout-err &&
git -C sparse-index checkout df-conflict-2 \
1>sparse-index-out \
2>sparse-index-err &&
# The full checkout deviates from the df-conflict-1 case here!
# It drops the change to folder1/larger-content and leaves the
# folder1 path as-is on disk. The sparse-index behaves the same.
test_must_be_empty full-checkout-out &&
test_must_be_empty sparse-index-out &&
# In the sparse-checkout case, the checkout deletes the folder1
# file and adds the folder1/larger-content file, leaving all other
# paths that were in folder1/ as deleted (without any warning).
cat >expect <<-EOF &&
D folder2
A folder2/larger-content
EOF
test_cmp expect sparse-checkout-out &&
# Switched to branch df-conflict-1
test_cmp full-checkout-err sparse-checkout-err &&
test_cmp full-checkout-err sparse-index-err
'

test_done
Loading

0 comments on commit eedf36e

Please sign in to comment.