Merge 'ds/sparse-add' into 'vfs-2.25.0'

Resolves conflicts from removing the clean-status requirement.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
This commit is contained in:
Derrick Stolee 2020-02-17 17:46:05 +00:00
Родитель 0ace4e89b2 ef07659926
Коммит 74dc2d495f
6 изменённых файлов: 534 добавлений и 182 удалений

Просмотреть файл

@ -50,6 +50,21 @@ To avoid interfering with other worktrees, it first enables the
+
When the `--stdin` option is provided, the patterns are read from
standard in as a newline-delimited list instead of from the arguments.
+
When `core.sparseCheckoutCone` is enabled, the input list is considered a
list of directories instead of sparse-checkout patterns. The command writes
patterns to the sparse-checkout file to include all files contained in those
directories (recursively) as well as files that are siblings of ancestor
directories. The input format matches the output of `git ls-tree --name-only`.
This includes interpreting pathnames that begin with a double quote (") as
C-style quoted strings.
'add'::
Update the sparse-checkout file to include additional patterns.
By default, these patterns are read from the command-line arguments,
but they can be read from stdin using the `--stdin` option. When
`core.sparseCheckoutCone` is enabled, the given patterns are interpreted
as directory names as in the 'set' subcommand.
'disable'::
Disable the `core.sparseCheckout` config setting, and restore the
@ -106,7 +121,7 @@ The full pattern set allows for arbitrary pattern matches and complicated
inclusion/exclusion rules. These can result in O(N*M) pattern matches when
updating the index, where N is the number of patterns and M is the number
of paths in the index. To combat this performance issue, a more restricted
pattern set is allowed when `core.spareCheckoutCone` is enabled.
pattern set is allowed when `core.sparseCheckoutCone` is enabled.
The accepted patterns in the cone pattern set are:
@ -128,9 +143,12 @@ the following patterns:
----------------
This says "include everything in root, but nothing two levels below root."
If we then add the folder `A/B/C` as a recursive pattern, the folders `A` and
`A/B` are added as parent patterns. The resulting sparse-checkout file is
now
When in cone mode, the `git sparse-checkout set` subcommand takes a list of
directories instead of a list of sparse-checkout patterns. In this mode,
the command `git sparse-checkout set A/B/C` sets the directory `A/B/C` as
a recursive pattern, the directories `A` and `A/B` are added as parent
patterns. The resulting sparse-checkout file is now
----------------
/*

Просмотреть файл

@ -1130,7 +1130,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
if (option_sparse_checkout && git_sparse_checkout_init(repo))
if (option_sparse_checkout && git_sparse_checkout_init(dir))
return 1;
remote = remote_get(option_origin);

Просмотреть файл

@ -12,11 +12,13 @@
#include "lockfile.h"
#include "resolve-undo.h"
#include "unpack-trees.h"
#include "wt-status.h"
#include "quote.h"
static const char *empty_base = "";
static char const * const builtin_sparse_checkout_usage[] = {
N_("git sparse-checkout (init|list|set|disable) <options>"),
N_("git sparse-checkout (init|list|set|add|disable) <options>"),
NULL
};
@ -76,8 +78,10 @@ static int sparse_checkout_list(int argc, const char **argv)
string_list_sort(&sl);
for (i = 0; i < sl.nr; i++)
printf("%s\n", sl.items[i].string);
for (i = 0; i < sl.nr; i++) {
quote_c_style(sl.items[i].string, NULL, stdout, 0);
printf("\n");
}
return 0;
}
@ -139,6 +143,22 @@ static int update_working_directory(struct pattern_list *pl)
return result;
}
static char *escaped_pattern(char *pattern)
{
char *p = pattern;
struct strbuf final = STRBUF_INIT;
while (*p) {
if (is_glob_special(*p))
strbuf_addch(&final, '\\');
strbuf_addch(&final, *p);
p++;
}
return strbuf_detach(&final, NULL);
}
static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
{
int i;
@ -163,10 +183,11 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
fprintf(fp, "/*\n!/*/\n");
for (i = 0; i < sl.nr; i++) {
char *pattern = sl.items[i].string;
char *pattern = escaped_pattern(sl.items[i].string);
if (strlen(pattern))
fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
free(pattern);
}
string_list_clear(&sl, 0);
@ -184,8 +205,9 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
string_list_remove_duplicates(&sl, 0);
for (i = 0; i < sl.nr; i++) {
char *pattern = sl.items[i].string;
char *pattern = escaped_pattern(sl.items[i].string);
fprintf(fp, "%s/\n", pattern);
free(pattern);
}
}
@ -198,6 +220,10 @@ static int write_patterns_and_update(struct pattern_list *pl)
int result;
sparse_filename = get_sparse_checkout_filename();
if (safe_create_leading_directories(sparse_filename))
die(_("failed to create directory for sparse-checkout file"));
fd = hold_lock_file_for_update(&lk, sparse_filename,
LOCK_DIE_ON_ERROR);
@ -364,6 +390,9 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
strbuf_trim_trailing_dir_sep(line);
if (strbuf_normalize_path(line))
die(_("could not normalize path %s"), line->buf);
if (!line->len)
return;
@ -374,7 +403,7 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
}
static char const * const builtin_sparse_checkout_set_usage[] = {
N_("git sparse-checkout set (--stdin | <patterns>)"),
N_("git sparse-checkout (set|add) (--stdin | <patterns>)"),
NULL
};
@ -382,41 +411,39 @@ static struct sparse_checkout_set_opts {
int use_stdin;
} set_opts;
static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
static void add_patterns_from_input(struct pattern_list *pl,
int argc, const char **argv)
{
int i;
struct pattern_list pl;
int result;
int changed_config = 0;
static struct option builtin_sparse_checkout_set_options[] = {
OPT_BOOL(0, "stdin", &set_opts.use_stdin,
N_("read patterns from standard in")),
OPT_END(),
};
memset(&pl, 0, sizeof(pl));
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_set_options,
builtin_sparse_checkout_set_usage,
PARSE_OPT_KEEP_UNKNOWN);
if (core_sparse_checkout_cone) {
struct strbuf line = STRBUF_INIT;
hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl.use_cone_patterns = 1;
hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl->use_cone_patterns = 1;
if (set_opts.use_stdin) {
while (!strbuf_getline(&line, stdin))
strbuf_to_cone_pattern(&line, &pl);
struct strbuf unquoted = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
if (line.buf[0] == '"') {
strbuf_reset(&unquoted);
if (unquote_c_style(&unquoted, line.buf, NULL))
die(_("unable to unquote C-style string '%s'"),
line.buf);
strbuf_swap(&unquoted, &line);
}
strbuf_to_cone_pattern(&line, pl);
}
strbuf_release(&unquoted);
} else {
for (i = 0; i < argc; i++) {
strbuf_setlen(&line, 0);
strbuf_addstr(&line, argv[i]);
strbuf_to_cone_pattern(&line, &pl);
strbuf_to_cone_pattern(&line, pl);
}
}
} else {
@ -426,13 +453,84 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
while (!strbuf_getline(&line, stdin)) {
size_t len;
char *buf = strbuf_detach(&line, &len);
add_pattern(buf, empty_base, 0, &pl, 0);
add_pattern(buf, empty_base, 0, pl, 0);
}
} else {
for (i = 0; i < argc; i++)
add_pattern(argv[i], empty_base, 0, &pl, 0);
add_pattern(argv[i], empty_base, 0, pl, 0);
}
}
}
enum modify_type {
REPLACE,
ADD,
};
static void add_patterns_cone_mode(int argc, const char **argv,
struct pattern_list *pl)
{
struct strbuf buffer = STRBUF_INIT;
struct pattern_entry *pe;
struct hashmap_iter iter;
struct pattern_list existing;
char *sparse_filename = get_sparse_checkout_filename();
add_patterns_from_input(pl, argc, argv);
memset(&existing, 0, sizeof(existing));
existing.use_cone_patterns = core_sparse_checkout_cone;
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
&existing, NULL))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) {
if (!hashmap_contains_parent(&pl->recursive_hashmap,
pe->pattern, &buffer) ||
!hashmap_contains_parent(&pl->parent_hashmap,
pe->pattern, &buffer)) {
strbuf_reset(&buffer);
strbuf_addstr(&buffer, pe->pattern);
insert_recursive_pattern(pl, &buffer);
}
}
clear_pattern_list(&existing);
strbuf_release(&buffer);
}
static void add_patterns_literal(int argc, const char **argv,
struct pattern_list *pl)
{
char *sparse_filename = get_sparse_checkout_filename();
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
pl, NULL))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
add_patterns_from_input(pl, argc, argv);
}
static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
{
int result;
int changed_config = 0;
struct pattern_list pl;
memset(&pl, 0, sizeof(pl));
switch (m) {
case ADD:
if (core_sparse_checkout_cone)
add_patterns_cone_mode(argc, argv, &pl);
else
add_patterns_literal(argc, argv, &pl);
break;
case REPLACE:
add_patterns_from_input(&pl, argc, argv);
break;
}
if (!core_apply_sparse_checkout) {
set_config(MODE_ALL_PATTERNS);
@ -449,6 +547,25 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
return result;
}
static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
enum modify_type m)
{
static struct option builtin_sparse_checkout_set_options[] = {
OPT_BOOL(0, "stdin", &set_opts.use_stdin,
N_("read patterns from standard in")),
OPT_END(),
};
repo_read_index(the_repository);
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_set_options,
builtin_sparse_checkout_set_usage,
PARSE_OPT_KEEP_UNKNOWN);
return modify_pattern_list(argc, argv, m);
}
static int sparse_checkout_disable(int argc, const char **argv)
{
struct pattern_list pl;
@ -493,7 +610,9 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[0], "init"))
return sparse_checkout_init(argc, argv);
if (!strcmp(argv[0], "set"))
return sparse_checkout_set(argc, argv, prefix);
return sparse_checkout_set(argc, argv, prefix, REPLACE);
if (!strcmp(argv[0], "add"))
return sparse_checkout_set(argc, argv, prefix, ADD);
if (!strcmp(argv[0], "disable"))
return sparse_checkout_disable(argc, argv);
}

85
dir.c
Просмотреть файл

@ -636,11 +636,42 @@ int pl_hashmap_cmp(const void *unused_cmp_data,
return strncmp(ee1->pattern, ee2->pattern, min_len);
}
static char *dup_and_filter_pattern(const char *pattern)
{
char *set, *read;
size_t count = 0;
char *result = xstrdup(pattern);
set = result;
read = result;
while (*read) {
/* skip escape characters (once) */
if (*read == '\\')
read++;
*set = *read;
set++;
read++;
count++;
}
*set = 0;
if (count > 2 &&
*(set - 1) == '*' &&
*(set - 2) == '/')
*(set - 2) = 0;
return result;
}
static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
{
struct pattern_entry *translated;
char *truncated;
char *data = NULL;
const char *prev, *cur, *next;
if (!pl->use_cone_patterns)
return;
@ -657,17 +688,57 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
return;
}
if (given->patternlen > 2 &&
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
if (given->patternlen <= 2 ||
*given->pattern == '*' ||
strstr(given->pattern, "**")) {
/* Not a cone pattern. */
pl->use_cone_patterns = 0;
warning(_("unrecognized pattern: '%s'"), given->pattern);
goto clear_hashmaps;
}
truncated = xstrdup(given->pattern);
truncated[given->patternlen - 2] = 0;
prev = given->pattern;
cur = given->pattern + 1;
next = given->pattern + 2;
while (*cur) {
/* Watch for glob characters '*', '\', '[', '?' */
if (!is_glob_special(*cur))
goto increment;
/* But only if *prev != '\\' */
if (*prev == '\\')
goto increment;
/* But allow the initial '\' */
if (*cur == '\\' &&
is_glob_special(*next))
goto increment;
/* But a trailing '/' then '*' is fine */
if (*prev == '/' &&
*cur == '*' &&
*next == 0)
goto increment;
/* Not a cone pattern. */
warning(_("unrecognized pattern: '%s'"), given->pattern);
goto clear_hashmaps;
increment:
prev++;
cur++;
next++;
}
if (given->patternlen > 2 &&
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
/* Not a cone pattern. */
warning(_("unrecognized pattern: '%s'"), given->pattern);
goto clear_hashmaps;
}
truncated = dup_and_filter_pattern(given->pattern);
translated = xmalloc(sizeof(struct pattern_entry));
translated->pattern = truncated;
@ -701,7 +772,7 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
translated = xmalloc(sizeof(struct pattern_entry));
translated->pattern = xstrdup(given->pattern);
translated->pattern = dup_and_filter_pattern(given->pattern);
translated->patternlen = given->patternlen;
hashmap_entry_init(&translated->ent,
ignore_case ?

Просмотреть файл

@ -12,6 +12,13 @@ list_files() {
(cd "$1" && printf '%s\n' *)
}
check_files() {
list_files "$1" >actual &&
shift &&
printf "%s\n" $@ >expect &&
test_cmp expect actual
}
test_expect_success 'setup' '
git init repo &&
(
@ -39,7 +46,7 @@ test_expect_success 'git sparse-checkout list (empty)' '
test_expect_success 'git sparse-checkout list (populated)' '
test_when_finished rm -f repo/.git/info/sparse-checkout &&
cat >repo/.git/info/sparse-checkout <<-EOF &&
cat >repo/.git/info/sparse-checkout <<-\EOF &&
/folder1/*
/deep/
**/a
@ -52,20 +59,18 @@ test_expect_success 'git sparse-checkout list (populated)' '
test_expect_success 'git sparse-checkout init' '
git -C repo sparse-checkout init &&
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
/*
!/*/
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
test_cmp_config -C repo true core.sparsecheckout &&
list_files repo >dir &&
echo a >expect &&
test_cmp expect dir
check_files repo a
'
test_expect_success 'git sparse-checkout list after init' '
git -C repo sparse-checkout list >actual &&
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
/*
!/*/
EOF
@ -75,32 +80,24 @@ test_expect_success 'git sparse-checkout list after init' '
test_expect_success 'init with existing sparse-checkout' '
echo "*folder*" >> repo/.git/info/sparse-checkout &&
git -C repo sparse-checkout init &&
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
/*
!/*/
*folder*
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
list_files repo >dir &&
cat >expect <<-EOF &&
a
folder1
folder2
EOF
test_cmp expect dir
check_files repo a folder1 folder2
'
test_expect_success 'clone --sparse' '
git clone --sparse repo clone &&
git clone --sparse "file://$(pwd)/repo" clone &&
git -C clone sparse-checkout list >actual &&
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
/*
!/*/
EOF
test_cmp expect actual &&
list_files clone >dir &&
echo a >expect &&
test_cmp expect dir
check_files clone a
'
test_expect_success 'set enables config' '
@ -119,7 +116,7 @@ test_expect_success 'set enables config' '
test_expect_success 'set sparse-checkout using builtin' '
git -C repo sparse-checkout set "/*" "!/*/" "*folder*" &&
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
/*
!/*/
*folder*
@ -127,17 +124,11 @@ test_expect_success 'set sparse-checkout using builtin' '
git -C repo sparse-checkout list >actual &&
test_cmp expect actual &&
test_cmp expect repo/.git/info/sparse-checkout &&
list_files repo >dir &&
cat >expect <<-EOF &&
a
folder1
folder2
EOF
test_cmp expect dir
check_files repo a folder1 folder2
'
test_expect_success 'set sparse-checkout using --stdin' '
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
/*
!/*/
/folder1/
@ -147,13 +138,22 @@ test_expect_success 'set sparse-checkout using --stdin' '
git -C repo sparse-checkout list >actual &&
test_cmp expect actual &&
test_cmp expect repo/.git/info/sparse-checkout &&
list_files repo >dir &&
cat >expect <<-EOF &&
a
folder1
folder2
check_files repo "a folder1 folder2"
'
test_expect_success 'add to sparse-checkout' '
cat repo/.git/info/sparse-checkout >expect &&
cat >add <<-\EOF &&
pattern1
/folder1/
pattern2
EOF
test_cmp expect dir
cat add >>expect &&
git -C repo sparse-checkout add --stdin <add &&
git -C repo sparse-checkout list >actual &&
test_cmp expect actual &&
test_cmp expect repo/.git/info/sparse-checkout &&
check_files repo "a folder1 folder2"
'
test_expect_success 'cone mode: match patterns' '
@ -162,13 +162,7 @@ test_expect_success 'cone mode: match patterns' '
git -C repo read-tree -mu HEAD 2>err &&
test_i18ngrep ! "disabling cone patterns" err &&
git -C repo reset --hard &&
list_files repo >dir &&
cat >expect <<-EOF &&
a
folder1
folder2
EOF
test_cmp expect dir
check_files repo a folder1 folder2
'
test_expect_success 'cone mode: warn on bad pattern' '
@ -185,14 +179,7 @@ test_expect_success 'sparse-checkout disable' '
test_path_is_file repo/.git/info/sparse-checkout &&
git -C repo config --list >config &&
test_must_fail git config core.sparseCheckout &&
list_files repo >dir &&
cat >expect <<-EOF &&
a
deep
folder1
folder2
EOF
test_cmp expect dir
check_files repo a deep folder1 folder2
'
test_expect_success 'cone mode: init and set' '
@ -204,25 +191,10 @@ test_expect_success 'cone mode: init and set' '
test_cmp expect dir &&
git -C repo sparse-checkout set deep/deeper1/deepest/ 2>err &&
test_must_be_empty err &&
list_files repo >dir &&
cat >expect <<-EOF &&
a
deep
EOF
test_cmp expect dir &&
list_files repo/deep >dir &&
cat >expect <<-EOF &&
a
deeper1
EOF
test_cmp expect dir &&
list_files repo/deep/deeper1 >dir &&
cat >expect <<-EOF &&
a
deepest
EOF
test_cmp expect dir &&
cat >expect <<-EOF &&
check_files repo a deep &&
check_files repo/deep a deeper1 &&
check_files repo/deep/deeper1 a deepest &&
cat >expect <<-\EOF &&
/*
!/*/
/deep/
@ -232,22 +204,16 @@ test_expect_success 'cone mode: init and set' '
/deep/deeper1/deepest/
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
git -C repo sparse-checkout set --stdin 2>err <<-EOF &&
git -C repo sparse-checkout set --stdin 2>err <<-\EOF &&
folder1
folder2
EOF
test_must_be_empty err &&
cat >expect <<-EOF &&
a
folder1
folder2
EOF
list_files repo >dir &&
test_cmp expect dir
check_files repo a folder1 folder2
'
test_expect_success 'cone mode: list' '
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
folder1
folder2
EOF
@ -260,7 +226,7 @@ test_expect_success 'cone mode: list' '
test_expect_success 'cone mode: set with nested folders' '
git -C repo sparse-checkout set deep deep/deeper1/deepest 2>err &&
test_line_count = 0 err &&
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
/*
!/*/
/deep/
@ -268,19 +234,58 @@ test_expect_success 'cone mode: set with nested folders' '
test_cmp repo/.git/info/sparse-checkout expect
'
test_expect_success 'cone mode: add independent path' '
git -C repo sparse-checkout set deep/deeper1 &&
git -C repo sparse-checkout add folder1 &&
cat >expect <<-\EOF &&
/*
!/*/
/deep/
!/deep/*/
/deep/deeper1/
/folder1/
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
check_files repo a deep folder1
'
test_expect_success 'cone mode: add sibling path' '
git -C repo sparse-checkout set deep/deeper1 &&
git -C repo sparse-checkout add deep/deeper2 &&
cat >expect <<-\EOF &&
/*
!/*/
/deep/
!/deep/*/
/deep/deeper1/
/deep/deeper2/
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
check_files repo a deep
'
test_expect_success 'cone mode: add parent path' '
git -C repo sparse-checkout set deep/deeper1 folder1 &&
git -C repo sparse-checkout add deep &&
cat >expect <<-\EOF &&
/*
!/*/
/deep/
/folder1/
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
check_files repo a deep folder1
'
test_expect_success 'revert to old sparse-checkout on bad update' '
test_when_finished git -C repo reset --hard &&
git -C repo sparse-checkout set deep &&
echo update >repo/deep/deeper2/a &&
cp repo/.git/info/sparse-checkout expect &&
test_must_fail git -C repo sparse-checkout set deep/deeper1 2>err &&
test_i18ngrep "Cannot update sparse checkout" err &&
test_cmp repo/.git/info/sparse-checkout expect &&
list_files repo/deep >dir &&
cat >expect <<-EOF &&
a
deeper1
deeper2
EOF
test_cmp dir expect
check_files repo/deep a deeper1 deeper2
'
test_expect_success 'revert to old sparse-checkout on empty update' '
@ -313,18 +318,13 @@ test_expect_success '.gitignore should not warn about cone mode' '
test_expect_success 'cone mode: set with core.ignoreCase=true' '
git -C repo sparse-checkout init --cone &&
git -C repo -c core.ignoreCase=true sparse-checkout set folder1 &&
cat >expect <<-EOF &&
cat >expect <<-\EOF &&
/*
!/*/
/folder1/
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
list_files repo >dir &&
cat >expect <<-EOF &&
a
folder1
EOF
test_cmp expect dir
check_files repo a folder1
'
test_expect_success 'interaction with submodules' '
@ -338,21 +338,165 @@ test_expect_success 'interaction with submodules' '
git sparse-checkout init --cone &&
git sparse-checkout set folder1
) &&
list_files super >dir &&
check_files super a folder1 modules &&
check_files super/modules/child a deep folder1 folder2
'
test_expect_success 'different sparse-checkouts with worktrees' '
git -C repo worktree add --detach ../worktree &&
check_files worktree "a deep folder1 folder2" &&
git -C worktree sparse-checkout init --cone &&
git -C repo sparse-checkout set folder1 &&
git -C worktree sparse-checkout set deep/deeper1 &&
check_files repo a folder1 &&
check_files worktree a deep
'
test_expect_success 'set using filename keeps file on-disk' '
git -C repo sparse-checkout set a deep &&
cat >expect <<-\EOF &&
a
folder1
modules
/*
!/*/
/a/
/deep/
EOF
test_cmp expect dir &&
list_files super/modules/child >dir &&
test_cmp expect repo/.git/info/sparse-checkout &&
check_files repo a deep
'
check_read_tree_errors () {
REPO=$1
FILES=$2
ERRORS=$3
git -C $REPO -c core.sparseCheckoutCone=false read-tree -mu HEAD 2>err &&
test_must_be_empty err &&
check_files $REPO "$FILES" &&
git -C $REPO read-tree -mu HEAD 2>err &&
if test -z "$ERRORS"
then
test_must_be_empty err
else
test_i18ngrep "$ERRORS" err
fi &&
check_files $REPO $FILES
}
test_expect_success 'pattern-checks: /A/**' '
cat >repo/.git/info/sparse-checkout <<-\EOF &&
/*
!/*/
/folder1/**
EOF
check_read_tree_errors repo "a folder1" "disabling cone pattern matching"
'
test_expect_success 'pattern-checks: /A/**/B/' '
cat >repo/.git/info/sparse-checkout <<-\EOF &&
/*
!/*/
/deep/**/deepest
EOF
check_read_tree_errors repo "a deep" "disabling cone pattern matching" &&
check_files repo/deep "deeper1" &&
check_files repo/deep/deeper1 "deepest"
'
test_expect_success 'pattern-checks: too short' '
cat >repo/.git/info/sparse-checkout <<-\EOF &&
/*
!/*/
/a
EOF
check_read_tree_errors repo "a" "disabling cone pattern matching"
'
test_expect_success 'pattern-checks: trailing "*"' '
cat >repo/.git/info/sparse-checkout <<-\EOF &&
/*
!/*/
/a*
EOF
check_read_tree_errors repo "a" "disabling cone pattern matching"
'
test_expect_success 'pattern-checks: starting "*"' '
cat >repo/.git/info/sparse-checkout <<-\EOF &&
/*
!/*/
*eep/
EOF
check_read_tree_errors repo "a deep" "disabling cone pattern matching"
'
test_expect_success 'pattern-checks: contained glob characters' '
for c in "[a]" "\\" "?" "*"
do
cat >repo/.git/info/sparse-checkout <<-EOF &&
/*
!/*/
something$c-else/
EOF
check_read_tree_errors repo "a" "disabling cone pattern matching"
done
'
test_expect_success BSLASHPSPEC 'pattern-checks: escaped characters' '
git clone repo escaped &&
TREEOID=$(git -C escaped rev-parse HEAD:folder1) &&
NEWTREE=$(git -C escaped mktree <<-EOF
$(git -C escaped ls-tree HEAD)
040000 tree $TREEOID zbad\\dir
040000 tree $TREEOID zdoes*exist
040000 tree $TREEOID zglob[!a]?
EOF
) &&
COMMIT=$(git -C escaped commit-tree $NEWTREE -p HEAD) &&
git -C escaped reset --hard $COMMIT &&
check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? &&
git -C escaped sparse-checkout init --cone &&
git -C escaped sparse-checkout set zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" &&
cat >expect <<-\EOF &&
a
deep
folder1
folder2
/*
!/*/
/zbad\\dir/
!/zbad\\dir/*/
/zbad\\dir/bogus/
/zdoes\*exist/
/zdoes\*not\*exist/
/zglob\[!a]\?/
EOF
test_cmp expect dir
test_cmp expect escaped/.git/info/sparse-checkout &&
check_read_tree_errors escaped "a zbad\\dir zdoes*exist zglob[!a]?" &&
git -C escaped ls-tree -d --name-only HEAD >list-expect &&
git -C escaped sparse-checkout set --stdin <list-expect &&
cat >expect <<-\EOF &&
/*
!/*/
/deep/
/folder1/
/folder2/
/zbad\\dir/
/zdoes\*exist/
/zglob\[!a]\?/
EOF
test_cmp expect escaped/.git/info/sparse-checkout &&
check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? &&
git -C escaped sparse-checkout list >list-actual &&
test_cmp list-expect list-actual
'
test_expect_success MINGW 'cone mode replaces backslashes with slashes' '
git -C repo sparse-checkout set deep\\deeper1 &&
cat >expect <<-\EOF &&
/*
!/*/
/deep/
!/deep/*/
/deep/deeper1/
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
check_files repo a deep &&
check_files repo/deep a deeper1
'
test_done

Просмотреть файл

@ -1445,7 +1445,7 @@ static int clear_ce_flags_1(struct index_state *istate,
name, &dtype, pl, istate);
if (ret == UNDECIDED)
ret = default_match;
if (ret == MATCHED)
if (ret == MATCHED || ret == MATCHED_RECURSIVE)
ce->ce_flags &= ~clear_mask;
cache++;
progress_nr++;