1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-23 11:26:09 +02:00

Merge branch 'vd/sparse-clean-etc'

"git update-index", "git checkout-index", and "git clean" are
taught to work better with the sparse checkout feature.

* vd/sparse-clean-etc:
  update-index: reduce scope of index expansion in do_reupdate
  update-index: integrate with sparse index
  update-index: add tests for sparse-checkout compatibility
  checkout-index: integrate with sparse index
  checkout-index: add --ignore-skip-worktree-bits option
  checkout-index: expand sparse checkout compatibility tests
  clean: integrate with sparse index
  reset: reorder wildcard pathspec conditions
  reset: fix validation in sparse index test
This commit is contained in:
Junio C Hamano 2022-02-17 16:25:05 -08:00
commit 2f45f3e2bc
8 changed files with 360 additions and 17 deletions

View File

@ -12,6 +12,7 @@ SYNOPSIS
'git checkout-index' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>] 'git checkout-index' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>]
[--stage=<number>|all] [--stage=<number>|all]
[--temp] [--temp]
[--ignore-skip-worktree-bits]
[-z] [--stdin] [-z] [--stdin]
[--] [<file>...] [--] [<file>...]
@ -37,8 +38,9 @@ OPTIONS
-a:: -a::
--all:: --all::
checks out all files in the index. Cannot be used checks out all files in the index except for those with the
together with explicit filenames. skip-worktree bit set (see `--ignore-skip-worktree-bits`).
Cannot be used together with explicit filenames.
-n:: -n::
--no-create:: --no-create::
@ -59,6 +61,10 @@ OPTIONS
write the content to temporary files. The temporary name write the content to temporary files. The temporary name
associations will be written to stdout. associations will be written to stdout.
--ignore-skip-worktree-bits::
Check out all files, including those with the skip-worktree bit
set.
--stdin:: --stdin::
Instead of taking list of paths from the command line, Instead of taking list of paths from the command line,
read list of paths from the standard input. Paths are read list of paths from the standard input. Paths are

View File

@ -7,6 +7,7 @@
#define USE_THE_INDEX_COMPATIBILITY_MACROS #define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "builtin.h" #include "builtin.h"
#include "config.h" #include "config.h"
#include "dir.h"
#include "lockfile.h" #include "lockfile.h"
#include "quote.h" #include "quote.h"
#include "cache-tree.h" #include "cache-tree.h"
@ -17,6 +18,7 @@
#define CHECKOUT_ALL 4 #define CHECKOUT_ALL 4
static int nul_term_line; static int nul_term_line;
static int checkout_stage; /* default to checkout stage0 */ static int checkout_stage; /* default to checkout stage0 */
static int ignore_skip_worktree; /* default to 0 */
static int to_tempfile; static int to_tempfile;
static char topath[4][TEMPORARY_FILENAME_LENGTH + 1]; static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
@ -65,6 +67,8 @@ static int checkout_file(const char *name, const char *prefix)
int namelen = strlen(name); int namelen = strlen(name);
int pos = cache_name_pos(name, namelen); int pos = cache_name_pos(name, namelen);
int has_same_name = 0; int has_same_name = 0;
int is_file = 0;
int is_skipped = 1;
int did_checkout = 0; int did_checkout = 0;
int errs = 0; int errs = 0;
@ -78,6 +82,12 @@ static int checkout_file(const char *name, const char *prefix)
break; break;
has_same_name = 1; has_same_name = 1;
pos++; pos++;
if (S_ISSPARSEDIR(ce->ce_mode))
break;
is_file = 1;
if (!ignore_skip_worktree && ce_skip_worktree(ce))
break;
is_skipped = 0;
if (ce_stage(ce) != checkout_stage if (ce_stage(ce) != checkout_stage
&& (CHECKOUT_ALL != checkout_stage || !ce_stage(ce))) && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
continue; continue;
@ -106,6 +116,11 @@ static int checkout_file(const char *name, const char *prefix)
fprintf(stderr, "git checkout-index: %s ", name); fprintf(stderr, "git checkout-index: %s ", name);
if (!has_same_name) if (!has_same_name)
fprintf(stderr, "is not in the cache"); fprintf(stderr, "is not in the cache");
else if (!is_file)
fprintf(stderr, "is a sparse directory");
else if (is_skipped)
fprintf(stderr, "has skip-worktree enabled; "
"use '--ignore-skip-worktree-bits' to checkout");
else if (checkout_stage) else if (checkout_stage)
fprintf(stderr, "does not exist at stage %d", fprintf(stderr, "does not exist at stage %d",
checkout_stage); checkout_stage);
@ -121,10 +136,27 @@ static int checkout_all(const char *prefix, int prefix_length)
int i, errs = 0; int i, errs = 0;
struct cache_entry *last_ce = NULL; struct cache_entry *last_ce = NULL;
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);
for (i = 0; i < active_nr ; i++) { for (i = 0; i < active_nr ; i++) {
struct cache_entry *ce = active_cache[i]; struct cache_entry *ce = active_cache[i];
if (S_ISSPARSEDIR(ce->ce_mode)) {
if (!ce_skip_worktree(ce))
BUG("sparse directory '%s' does not have skip-worktree set", ce->name);
/*
* If the current entry is a sparse directory and skip-worktree
* entries are being checked out, expand the index and continue
* the loop on the current index position (now pointing to the
* first entry inside the expanded sparse directory).
*/
if (ignore_skip_worktree) {
ensure_full_index(&the_index);
ce = active_cache[i];
}
}
if (!ignore_skip_worktree && ce_skip_worktree(ce))
continue;
if (ce_stage(ce) != checkout_stage if (ce_stage(ce) != checkout_stage
&& (CHECKOUT_ALL != checkout_stage || !ce_stage(ce))) && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
continue; continue;
@ -185,6 +217,8 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
struct option builtin_checkout_index_options[] = { struct option builtin_checkout_index_options[] = {
OPT_BOOL('a', "all", &all, OPT_BOOL('a', "all", &all,
N_("check out all files in the index")), N_("check out all files in the index")),
OPT_BOOL(0, "ignore-skip-worktree-bits", &ignore_skip_worktree,
N_("do not skip files with skip-worktree set")),
OPT__FORCE(&force, N_("force overwrite of existing files"), 0), OPT__FORCE(&force, N_("force overwrite of existing files"), 0),
OPT__QUIET(&quiet, OPT__QUIET(&quiet,
N_("no warning for existing files and files not in index")), N_("no warning for existing files and files not in index")),
@ -212,6 +246,9 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
git_config(git_default_config, NULL); git_config(git_default_config, NULL);
prefix_length = prefix ? strlen(prefix) : 0; prefix_length = prefix ? strlen(prefix) : 0;
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
if (read_cache() < 0) { if (read_cache() < 0) {
die("invalid cache"); die("invalid cache");
} }

View File

@ -1009,6 +1009,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
dir.flags |= DIR_KEEP_UNTRACKED_CONTENTS; dir.flags |= DIR_KEEP_UNTRACKED_CONTENTS;
} }
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
if (read_cache() < 0) if (read_cache() < 0)
die(_("index file corrupt")); die(_("index file corrupt"));

View File

@ -204,10 +204,16 @@ static int pathspec_needs_expanded_index(const struct pathspec *pathspec)
/* /*
* Special case: if the pattern is a path inside the cone * Special case: if the pattern is a path inside the cone
* followed by only wildcards, the pattern cannot match * followed by only wildcards, the pattern cannot match
* partial sparse directories, so we don't expand the index. * partial sparse directories, so we know we don't need to
* expand the index.
*
* Examples:
* - in-cone/foo***: doesn't need expanded index
* - not-in-cone/bar*: may need expanded index
* - **.c: may need expanded index
*/ */
if (path_in_cone_mode_sparse_checkout(item.original, &the_index) && if (strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len &&
strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len) path_in_cone_mode_sparse_checkout(item.original, &the_index))
continue; continue;
for (pos = 0; pos < active_nr; pos++) { for (pos = 0; pos < active_nr; pos++) {

View File

@ -606,7 +606,7 @@ static struct cache_entry *read_one_ent(const char *which,
error("%s: not in %s branch.", path, which); error("%s: not in %s branch.", path, which);
return NULL; return NULL;
} }
if (mode == S_IFDIR) { if (!the_index.sparse_index && mode == S_IFDIR) {
if (which) if (which)
error("%s: not a blob in %s branch.", path, which); error("%s: not a blob in %s branch.", path, which);
return NULL; return NULL;
@ -743,8 +743,6 @@ static int do_reupdate(int ac, const char **av,
*/ */
has_head = 0; has_head = 0;
redo: redo:
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&the_index);
for (pos = 0; pos < active_nr; pos++) { for (pos = 0; pos < active_nr; pos++) {
const struct cache_entry *ce = active_cache[pos]; const struct cache_entry *ce = active_cache[pos];
struct cache_entry *old = NULL; struct cache_entry *old = NULL;
@ -761,6 +759,16 @@ static int do_reupdate(int ac, const char **av,
discard_cache_entry(old); discard_cache_entry(old);
continue; /* unchanged */ continue; /* unchanged */
} }
/* At this point, we know the contents of the sparse directory are
* modified with respect to HEAD, so we expand the index and restart
* to process each path individually
*/
if (S_ISSPARSEDIR(ce->ce_mode)) {
ensure_full_index(&the_index);
goto redo;
}
/* Be careful. The working tree may not have the /* Be careful. The working tree may not have the
* path anymore, in which case, under 'allow_remove', * path anymore, in which case, under 'allow_remove',
* or worse yet 'allow_replace', active_nr may decrease. * or worse yet 'allow_replace', active_nr may decrease.
@ -1088,6 +1096,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
git_config(git_default_config, NULL); git_config(git_default_config, NULL);
prepare_repo_settings(r);
the_repository->settings.command_requires_full_index = 0;
/* we will diagnose later if it turns out that we need to update it */ /* we will diagnose later if it turns out that we need to update it */
newfd = hold_locked_index(&lock_file, 0); newfd = hold_locked_index(&lock_file, 0);
if (newfd < 0) if (newfd < 0)

View File

@ -1340,9 +1340,6 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK; int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
int new_only = option & ADD_CACHE_NEW_ONLY; int new_only = option & ADD_CACHE_NEW_ONLY;
if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
cache_tree_invalidate_path(istate, ce->name);
/* /*
* If this entry's path sorts after the last entry in the index, * If this entry's path sorts after the last entry in the index,
* we can avoid searching for it. * we can avoid searching for it.
@ -1353,6 +1350,13 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
else else
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce), EXPAND_SPARSE); pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce), EXPAND_SPARSE);
/*
* Cache tree path should be invalidated only after index_name_stage_pos,
* in case it expands a sparse index.
*/
if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
cache_tree_invalidate_path(istate, ce->name);
/* existing match? Just replace it. */ /* existing match? Just replace it. */
if (pos >= 0) { if (pos >= 0) {
if (!new_only) if (!new_only)

View File

@ -117,5 +117,7 @@ test_perf_on_all git diff
test_perf_on_all git diff --cached test_perf_on_all git diff --cached
test_perf_on_all git blame $SPARSE_CONE/a test_perf_on_all git blame $SPARSE_CONE/a
test_perf_on_all git blame $SPARSE_CONE/f3/a test_perf_on_all git blame $SPARSE_CONE/f3/a
test_perf_on_all git checkout-index -f --all
test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
test_done test_done

View File

@ -593,13 +593,11 @@ test_expect_success 'reset with pathspecs outside sparse definition' '
test_sparse_match git reset update-folder1 -- folder1 && test_sparse_match git reset update-folder1 -- folder1 &&
git -C full-checkout reset update-folder1 -- folder1 && git -C full-checkout reset update-folder1 -- folder1 &&
test_sparse_match git status --porcelain=v2 && test_all_match git ls-files -s -- folder1 &&
test_all_match git rev-parse HEAD:folder1 &&
test_sparse_match git reset update-folder2 -- folder2/a && test_sparse_match git reset update-folder2 -- folder2/a &&
git -C full-checkout reset update-folder2 -- folder2/a && git -C full-checkout reset update-folder2 -- folder2/a &&
test_sparse_match git status --porcelain=v2 && test_all_match git ls-files -s -- folder2/a
test_all_match git rev-parse HEAD:folder2/a
' '
test_expect_success 'reset with wildcard pathspec' ' test_expect_success 'reset with wildcard pathspec' '
@ -629,6 +627,173 @@ test_expect_success 'reset with wildcard pathspec' '
test_all_match git ls-files -s -- folder1 test_all_match git ls-files -s -- folder1
' '
test_expect_success 'update-index modify outside sparse definition' '
init_repos &&
write_script edit-contents <<-\EOF &&
echo text >>$1
EOF
# Create & modify folder1/a
# Note that this setup is a manual way of reaching the erroneous
# condition in which a `skip-worktree` enabled, outside-of-cone file
# exists on disk. It is used here to ensure `update-index` is stable
# and behaves predictably if such a condition occurs.
run_on_sparse mkdir -p folder1 &&
run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
run_on_all ../edit-contents folder1/a &&
# If file has skip-worktree enabled, update-index does not modify the
# index entry
test_sparse_match git update-index folder1/a &&
test_sparse_match git status --porcelain=v2 &&
test_must_be_empty sparse-checkout-out &&
# When skip-worktree is disabled (even on files outside sparse cone), file
# is updated in the index
test_sparse_match git update-index --no-skip-worktree folder1/a &&
test_all_match git status --porcelain=v2 &&
test_all_match git update-index folder1/a &&
test_all_match git status --porcelain=v2
'
test_expect_success 'update-index --add outside sparse definition' '
init_repos &&
write_script edit-contents <<-\EOF &&
echo text >>$1
EOF
# Create folder1, add new file
run_on_sparse mkdir -p folder1 &&
run_on_all ../edit-contents folder1/b &&
# The *untracked* out-of-cone file is added to the index because it does
# not have a `skip-worktree` bit to signal that it should be ignored
# (unlike in `git add`, which will fail due to the file being outside
# the sparse checkout definition).
test_all_match git update-index --add folder1/b &&
test_all_match git status --porcelain=v2
'
# NEEDSWORK: `--remove`, unlike the rest of `update-index`, does not ignore
# `skip-worktree` entries by default and will remove them from the index.
# The `--ignore-skip-worktree-entries` flag must be used in conjunction with
# `--remove` to ignore the `skip-worktree` entries and prevent their removal
# from the index.
test_expect_success 'update-index --remove outside sparse definition' '
init_repos &&
# When --ignore-skip-worktree-entries is _not_ specified:
# out-of-cone, not-on-disk files are removed from the index
test_sparse_match git update-index --remove folder1/a &&
cat >expect <<-EOF &&
D folder1/a
EOF
test_sparse_match git diff --cached --name-status &&
test_cmp expect sparse-checkout-out &&
# Reset the state
test_all_match git reset --hard &&
# When --ignore-skip-worktree-entries is specified, out-of-cone
# (skip-worktree) files are ignored
test_sparse_match git update-index --remove --ignore-skip-worktree-entries folder1/a &&
test_sparse_match git diff --cached --name-status &&
test_must_be_empty sparse-checkout-out &&
# Reset the state
test_all_match git reset --hard &&
# --force-remove supercedes --ignore-skip-worktree-entries, removing
# a skip-worktree file from the index (and disk) when both are specified
# with --remove
test_sparse_match git update-index --force-remove --ignore-skip-worktree-entries folder1/a &&
cat >expect <<-EOF &&
D folder1/a
EOF
test_sparse_match git diff --cached --name-status &&
test_cmp expect sparse-checkout-out
'
test_expect_success 'update-index with directories' '
init_repos &&
# update-index will exit silently when provided with a directory name
# containing a trailing slash
test_all_match git update-index deep/ folder1/ &&
grep "Ignoring path deep/" sparse-checkout-err &&
grep "Ignoring path folder1/" sparse-checkout-err &&
# When update-index is given a directory name WITHOUT a trailing slash, it will
# behave in different ways depending on the status of the directory on disk:
# * if it exists, the command exits with an error ("add individual files instead")
# * if it does NOT exist (e.g., in a sparse-checkout), it is assumed to be a
# file and either triggers an error ("does not exist and --remove not passed")
# or is ignored completely (when using --remove)
test_all_match test_must_fail git update-index deep &&
run_on_all test_must_fail git update-index folder1 &&
test_must_fail git -C full-checkout update-index --remove folder1 &&
test_sparse_match git update-index --remove folder1 &&
test_all_match git status --porcelain=v2
'
test_expect_success 'update-index --again file outside sparse definition' '
init_repos &&
test_all_match git checkout -b test-reupdate &&
# Update HEAD without modifying the index to introduce a difference in
# folder1/a
test_sparse_match git reset --soft update-folder1 &&
# Because folder1/a differs in the index vs HEAD,
# `git update-index --no-skip-worktree --again` will effectively perform
# `git update-index --no-skip-worktree folder1/a` and remove the skip-worktree
# flag from folder1/a
test_sparse_match git update-index --no-skip-worktree --again &&
test_sparse_match git status --porcelain=v2 &&
cat >expect <<-EOF &&
D folder1/a
EOF
test_sparse_match git diff --name-status &&
test_cmp expect sparse-checkout-out
'
test_expect_success 'update-index --cacheinfo' '
init_repos &&
deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
folder2_oid=$(git -C full-checkout rev-parse update-folder2:folder2) &&
folder1_a_oid=$(git -C full-checkout rev-parse update-folder1:folder1/a) &&
test_all_match git update-index --cacheinfo 100644 $deep_a_oid deep/a &&
test_all_match git status --porcelain=v2 &&
# Cannot add sparse directory, even in sparse index case
test_all_match test_must_fail git update-index --add --cacheinfo 040000 $folder2_oid folder2/ &&
# Sparse match only: the new outside-of-cone entry is added *without* skip-worktree,
# so `git status` reports it as "deleted" in the worktree
test_sparse_match git update-index --add --cacheinfo 100644 $folder1_a_oid folder1/a &&
test_sparse_match git status --porcelain=v2 &&
cat >expect <<-EOF &&
MD folder1/a
EOF
test_sparse_match git status --short -- folder1/a &&
test_cmp expect sparse-checkout-out &&
# To return folder1/a to "normal" for a sparse checkout (ignored &
# outside-of-cone), add the skip-worktree flag.
test_sparse_match git update-index --skip-worktree folder1/a &&
cat >expect <<-EOF &&
S folder1/a
EOF
test_sparse_match git ls-files -t -- folder1/a &&
test_cmp expect sparse-checkout-out
'
test_expect_success 'merge, cherry-pick, and rebase' ' test_expect_success 'merge, cherry-pick, and rebase' '
init_repos && init_repos &&
@ -754,6 +919,74 @@ test_expect_success 'cherry-pick with conflicts' '
test_all_match test_must_fail git cherry-pick to-cherry-pick test_all_match test_must_fail git cherry-pick to-cherry-pick
' '
test_expect_success 'checkout-index inside sparse definition' '
init_repos &&
run_on_all rm -f deep/a &&
test_all_match git checkout-index -- deep/a &&
test_all_match git status --porcelain=v2 &&
echo test >>new-a &&
run_on_all cp ../new-a a &&
test_all_match test_must_fail git checkout-index -- a &&
test_all_match git checkout-index -f -- a &&
test_all_match git status --porcelain=v2
'
test_expect_success 'checkout-index outside sparse definition' '
init_repos &&
# Without --ignore-skip-worktree-bits, outside-of-cone files will trigger
# an error
test_sparse_match test_must_fail git checkout-index -- folder1/a &&
test_i18ngrep "folder1/a has skip-worktree enabled" sparse-checkout-err &&
test_path_is_missing folder1/a &&
# With --ignore-skip-worktree-bits, outside-of-cone files are checked out
test_sparse_match git checkout-index --ignore-skip-worktree-bits -- folder1/a &&
test_cmp sparse-checkout/folder1/a sparse-index/folder1/a &&
test_cmp sparse-checkout/folder1/a full-checkout/folder1/a &&
run_on_sparse rm -rf folder1 &&
echo test >new-a &&
run_on_sparse mkdir -p folder1 &&
run_on_all cp ../new-a folder1/a &&
test_all_match test_must_fail git checkout-index --ignore-skip-worktree-bits -- folder1/a &&
test_all_match git checkout-index -f --ignore-skip-worktree-bits -- folder1/a &&
test_cmp sparse-checkout/folder1/a sparse-index/folder1/a &&
test_cmp sparse-checkout/folder1/a full-checkout/folder1/a
'
test_expect_success 'checkout-index with folders' '
init_repos &&
# Inside checkout definition
test_all_match test_must_fail git checkout-index -f -- deep/ &&
# Outside checkout definition
# Note: although all tests fail (as expected), the messaging differs. For
# non-sparse index checkouts, the error is that the "file" does not appear
# in the index; for sparse checkouts, the error is explicitly that the
# entry is a sparse directory.
run_on_all test_must_fail git checkout-index -f -- folder1/ &&
test_cmp full-checkout-err sparse-checkout-err &&
! test_cmp full-checkout-err sparse-index-err &&
grep "is a sparse directory" sparse-index-err
'
test_expect_success 'checkout-index --all' '
init_repos &&
test_all_match git checkout-index --all &&
test_sparse_match test_path_is_missing folder1 &&
# --ignore-skip-worktree-bits will cause `skip-worktree` files to be
# checked out, causing the outside-of-cone `folder1` to exist on-disk
test_all_match git checkout-index --ignore-skip-worktree-bits --all &&
test_all_match test_path_exists folder1
'
test_expect_success 'clean' ' test_expect_success 'clean' '
init_repos && init_repos &&
@ -763,23 +996,42 @@ test_expect_success 'clean' '
test_all_match git commit -m "ignore bogus files" && test_all_match git commit -m "ignore bogus files" &&
run_on_sparse mkdir folder1 && run_on_sparse mkdir folder1 &&
run_on_all mkdir -p deep/untracked-deep &&
run_on_all touch folder1/bogus && run_on_all touch folder1/bogus &&
run_on_all touch folder1/untracked &&
run_on_all touch deep/untracked-deep/bogus &&
run_on_all touch deep/untracked-deep/untracked &&
test_all_match git status --porcelain=v2 && test_all_match git status --porcelain=v2 &&
test_all_match git clean -f && test_all_match git clean -f &&
test_all_match git status --porcelain=v2 && test_all_match git status --porcelain=v2 &&
test_sparse_match ls && test_sparse_match ls &&
test_sparse_match ls folder1 && test_sparse_match ls folder1 &&
run_on_all test_path_exists folder1/bogus &&
run_on_all test_path_is_missing folder1/untracked &&
run_on_all test_path_exists deep/untracked-deep/bogus &&
run_on_all test_path_exists deep/untracked-deep/untracked &&
test_all_match git clean -fd &&
test_all_match git status --porcelain=v2 &&
test_sparse_match ls &&
test_sparse_match ls folder1 &&
run_on_all test_path_exists folder1/bogus &&
run_on_all test_path_exists deep/untracked-deep/bogus &&
run_on_all test_path_is_missing deep/untracked-deep/untracked &&
test_all_match git clean -xf && test_all_match git clean -xf &&
test_all_match git status --porcelain=v2 && test_all_match git status --porcelain=v2 &&
test_sparse_match ls && test_sparse_match ls &&
test_sparse_match ls folder1 && test_sparse_match ls folder1 &&
run_on_all test_path_is_missing folder1/bogus &&
run_on_all test_path_exists deep/untracked-deep/bogus &&
test_all_match git clean -xdf && test_all_match git clean -xdf &&
test_all_match git status --porcelain=v2 && test_all_match git status --porcelain=v2 &&
test_sparse_match ls && test_sparse_match ls &&
test_sparse_match ls folder1 && test_sparse_match ls folder1 &&
run_on_all test_path_is_missing deep/untracked-deep/bogus &&
test_sparse_match test_path_is_dir folder1 test_sparse_match test_path_is_dir folder1
' '
@ -898,6 +1150,8 @@ test_expect_success 'sparse-index is not expanded' '
echo >>sparse-index/untracked.txt && echo >>sparse-index/untracked.txt &&
ensure_not_expanded add . && ensure_not_expanded add . &&
ensure_not_expanded checkout-index -f a &&
ensure_not_expanded checkout-index -f --all &&
for ref in update-deep update-folder1 update-folder2 update-deep for ref in update-deep update-folder1 update-folder2 update-deep
do do
echo >>sparse-index/README.md && echo >>sparse-index/README.md &&
@ -926,6 +1180,8 @@ test_expect_success 'sparse-index is not expanded' '
# Wildcard identifies only full sparse directories, no index expansion # Wildcard identifies only full sparse directories, no index expansion
ensure_not_expanded reset deepest -- folder\* && ensure_not_expanded reset deepest -- folder\* &&
ensure_not_expanded clean -fd &&
ensure_not_expanded checkout -f update-deep && ensure_not_expanded checkout -f update-deep &&
test_config -C sparse-index pull.twohead ort && test_config -C sparse-index pull.twohead ort &&
( (
@ -1001,6 +1257,24 @@ test_expect_success 'sparse index is not expanded: diff' '
ensure_not_expanded diff --cached ensure_not_expanded diff --cached
' '
test_expect_success 'sparse index is not expanded: update-index' '
init_repos &&
deep_a_oid=$(git -C full-checkout rev-parse update-deep:deep/a) &&
ensure_not_expanded update-index --cacheinfo 100644 $deep_a_oid deep/a &&
echo "test" >sparse-index/README.md &&
echo "test2" >sparse-index/a &&
rm -f sparse-index/deep/a &&
ensure_not_expanded update-index --add README.md &&
ensure_not_expanded update-index a &&
ensure_not_expanded update-index --remove deep/a &&
ensure_not_expanded reset --soft update-deep &&
ensure_not_expanded update-index --add --remove --again
'
test_expect_success 'sparse index is not expanded: blame' ' test_expect_success 'sparse index is not expanded: blame' '
init_repos && init_repos &&