зеркало из https://github.com/microsoft/git.git
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:
Коммит
74dc2d495f
|
@ -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
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++;
|
||||
|
|
Загрузка…
Ссылка в новой задаче