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:
commit
2f45f3e2bc
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
10
read-cache.c
10
read-cache.c
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 &&
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue