Merge branch 'as/check-ignore'
Add a new command "git check-ignore" for debugging .gitignore files. The variable names may want to get cleaned up but that can be done in-tree. * as/check-ignore: clean.c, ls-files.c: respect encapsulation of exclude_list_groups t0008: avoid brace expansion add git-check-ignore sub-command setup.c: document get_pathspec() add.c: extract new die_if_path_beyond_symlink() for reuse add.c: extract check_path_for_gitlink() from treat_gitlinks() for reuse pathspec.c: rename newly public functions for clarity add.c: move pathspec matchers into new pathspec.c for reuse add.c: remove unused argument from validate_pathspec() dir.c: improve docs for match_pathspec() and match_pathspec_depth() dir.c: provide clear_directory() for reclaiming dir_struct memory dir.c: keep track of where patterns came from dir.c: use a single struct exclude_list per source of excludes Conflicts: builtin/ls-files.c dir.c
This commit is contained in:
commit
a39b15b4f6
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,6 +22,7 @@
|
||||
/git-bundle
|
||||
/git-cat-file
|
||||
/git-check-attr
|
||||
/git-check-ignore
|
||||
/git-check-ref-format
|
||||
/git-checkout
|
||||
/git-checkout-index
|
||||
|
89
Documentation/git-check-ignore.txt
Normal file
89
Documentation/git-check-ignore.txt
Normal file
@ -0,0 +1,89 @@
|
||||
git-check-ignore(1)
|
||||
===================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-check-ignore - Debug gitignore / exclude files
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git check-ignore' [options] pathname...
|
||||
'git check-ignore' [options] --stdin < <list-of-paths>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
For each pathname given via the command-line or from a file via
|
||||
`--stdin`, show the pattern from .gitignore (or other input files to
|
||||
the exclude mechanism) that decides if the pathname is excluded or
|
||||
included. Later patterns within a file take precedence over earlier
|
||||
ones.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-q, --quiet::
|
||||
Don't output anything, just set exit status. This is only
|
||||
valid with a single pathname.
|
||||
|
||||
-v, --verbose::
|
||||
Also output details about the matching pattern (if any)
|
||||
for each given pathname.
|
||||
|
||||
--stdin::
|
||||
Read file names from stdin instead of from the command-line.
|
||||
|
||||
-z::
|
||||
The output format is modified to be machine-parseable (see
|
||||
below). If `--stdin` is also given, input paths are separated
|
||||
with a NUL character instead of a linefeed character.
|
||||
|
||||
OUTPUT
|
||||
------
|
||||
|
||||
By default, any of the given pathnames which match an ignore pattern
|
||||
will be output, one per line. If no pattern matches a given path,
|
||||
nothing will be output for that path; this means that path will not be
|
||||
ignored.
|
||||
|
||||
If `--verbose` is specified, the output is a series of lines of the form:
|
||||
|
||||
<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>
|
||||
|
||||
<pathname> is the path of a file being queried, <pattern> is the
|
||||
matching pattern, <source> is the pattern's source file, and <linenum>
|
||||
is the line number of the pattern within that source. If the pattern
|
||||
contained a `!` prefix or `/` suffix, it will be preserved in the
|
||||
output. <source> will be an absolute path when referring to the file
|
||||
configured by `core.excludesfile`, or relative to the repository root
|
||||
when referring to `.git/info/exclude` or a per-directory exclude file.
|
||||
|
||||
If `-z` is specified, the pathnames in the output are delimited by the
|
||||
null character; if `--verbose` is also specified then null characters
|
||||
are also used instead of colons and hard tabs:
|
||||
|
||||
<source> <NULL> <linenum> <NULL> <pattern> <NULL> <pathname> <NULL>
|
||||
|
||||
|
||||
EXIT STATUS
|
||||
-----------
|
||||
|
||||
0::
|
||||
One or more of the provided paths is ignored.
|
||||
|
||||
1::
|
||||
None of the provided paths are ignored.
|
||||
|
||||
128::
|
||||
A fatal error was encountered.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkgit:gitignore[5]
|
||||
linkgit:gitconfig[5]
|
||||
linkgit:git-ls-files[5]
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
@ -184,8 +184,10 @@ The second .gitignore prevents git from ignoring
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkgit:git-rm[1], linkgit:git-update-index[1],
|
||||
linkgit:gitrepository-layout[5]
|
||||
linkgit:git-rm[1],
|
||||
linkgit:git-update-index[1],
|
||||
linkgit:gitrepository-layout[5],
|
||||
linkgit:git-check-ignore[1]
|
||||
|
||||
GIT
|
||||
---
|
||||
|
@ -67,11 +67,13 @@ marked. If you to exclude files, make sure you have loaded index first.
|
||||
* Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
|
||||
sizeof(dir))`.
|
||||
|
||||
* Call `add_exclude()` to add single exclude pattern,
|
||||
`add_excludes_from_file()` to add patterns from a file
|
||||
(e.g. `.git/info/exclude`), and/or set `dir.exclude_per_dir`. A
|
||||
short-hand function `setup_standard_excludes()` can be used to set up
|
||||
the standard set of exclude settings.
|
||||
* To add single exclude pattern, call `add_exclude_list()` and then
|
||||
`add_exclude()`.
|
||||
|
||||
* To add patterns from a file (e.g. `.git/info/exclude`), call
|
||||
`add_excludes_from_file()` , and/or set `dir.exclude_per_dir`. A
|
||||
short-hand function `setup_standard_excludes()` can be used to set
|
||||
up the standard set of exclude settings.
|
||||
|
||||
* Set options described in the Data Structure section above.
|
||||
|
||||
@ -79,4 +81,6 @@ marked. If you to exclude files, make sure you have loaded index first.
|
||||
|
||||
* Use `dir.entries[]`.
|
||||
|
||||
* Call `clear_directory()` when none of the contained elements are no longer in use.
|
||||
|
||||
(JC)
|
||||
|
3
Makefile
3
Makefile
@ -666,6 +666,7 @@ LIB_H += pack-revindex.h
|
||||
LIB_H += pack.h
|
||||
LIB_H += parse-options.h
|
||||
LIB_H += patch-ids.h
|
||||
LIB_H += pathspec.h
|
||||
LIB_H += pkt-line.h
|
||||
LIB_H += progress.h
|
||||
LIB_H += prompt.h
|
||||
@ -789,6 +790,7 @@ LIB_OBJS += parse-options-cb.o
|
||||
LIB_OBJS += patch-delta.o
|
||||
LIB_OBJS += patch-ids.o
|
||||
LIB_OBJS += path.o
|
||||
LIB_OBJS += pathspec.o
|
||||
LIB_OBJS += pkt-line.o
|
||||
LIB_OBJS += preload-index.o
|
||||
LIB_OBJS += pretty.o
|
||||
@ -854,6 +856,7 @@ BUILTIN_OBJS += builtin/branch.o
|
||||
BUILTIN_OBJS += builtin/bundle.o
|
||||
BUILTIN_OBJS += builtin/cat-file.o
|
||||
BUILTIN_OBJS += builtin/check-attr.o
|
||||
BUILTIN_OBJS += builtin/check-ignore.o
|
||||
BUILTIN_OBJS += builtin/check-ref-format.o
|
||||
BUILTIN_OBJS += builtin/checkout-index.o
|
||||
BUILTIN_OBJS += builtin/checkout.o
|
||||
|
@ -52,6 +52,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_checkout(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "dir.h"
|
||||
#include "pathspec.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "cache-tree.h"
|
||||
#include "run-command.h"
|
||||
@ -97,39 +98,6 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
|
||||
return !!data.add_errors;
|
||||
}
|
||||
|
||||
static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
|
||||
{
|
||||
int num_unmatched = 0, i;
|
||||
|
||||
/*
|
||||
* Since we are walking the index as if we were walking the directory,
|
||||
* we have to mark the matched pathspec as seen; otherwise we will
|
||||
* mistakenly think that the user gave a pathspec that did not match
|
||||
* anything.
|
||||
*/
|
||||
for (i = 0; i < specs; i++)
|
||||
if (!seen[i])
|
||||
num_unmatched++;
|
||||
if (!num_unmatched)
|
||||
return;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
|
||||
}
|
||||
}
|
||||
|
||||
static char *find_used_pathspec(const char **pathspec)
|
||||
{
|
||||
char *seen;
|
||||
int i;
|
||||
|
||||
for (i = 0; pathspec[i]; i++)
|
||||
; /* just counting */
|
||||
seen = xcalloc(i, 1);
|
||||
fill_pathspec_matches(pathspec, seen, i);
|
||||
return seen;
|
||||
}
|
||||
|
||||
static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
|
||||
{
|
||||
char *seen;
|
||||
@ -149,10 +117,14 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int
|
||||
*dst++ = entry;
|
||||
}
|
||||
dir->nr = dst - dir->entries;
|
||||
fill_pathspec_matches(pathspec, seen, specs);
|
||||
add_pathspec_matches_against_index(pathspec, seen, specs);
|
||||
return seen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks the index to see whether any path in pathspec refers to
|
||||
* something inside a submodule. If so, dies with an error message.
|
||||
*/
|
||||
static void treat_gitlinks(const char **pathspec)
|
||||
{
|
||||
int i;
|
||||
@ -160,24 +132,8 @@ static void treat_gitlinks(const char **pathspec)
|
||||
if (!pathspec || !*pathspec)
|
||||
return;
|
||||
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (S_ISGITLINK(ce->ce_mode)) {
|
||||
int len = ce_namelen(ce), j;
|
||||
for (j = 0; pathspec[j]; j++) {
|
||||
int len2 = strlen(pathspec[j]);
|
||||
if (len2 <= len || pathspec[j][len] != '/' ||
|
||||
memcmp(ce->name, pathspec[j], len))
|
||||
continue;
|
||||
if (len2 == len + 1)
|
||||
/* strip trailing slash */
|
||||
pathspec[j] = xstrndup(ce->name, len);
|
||||
else
|
||||
die (_("Path '%s' is in submodule '%.*s'"),
|
||||
pathspec[j], len, ce->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; pathspec[i]; i++)
|
||||
pathspec[i] = check_path_for_gitlink(pathspec[i]);
|
||||
}
|
||||
|
||||
static void refresh(int verbose, const char **pathspec)
|
||||
@ -197,17 +153,19 @@ static void refresh(int verbose, const char **pathspec)
|
||||
free(seen);
|
||||
}
|
||||
|
||||
static const char **validate_pathspec(int argc, const char **argv, const char *prefix)
|
||||
/*
|
||||
* Normalizes argv relative to prefix, via get_pathspec(), and then
|
||||
* runs die_if_path_beyond_symlink() on each path in the normalized
|
||||
* list.
|
||||
*/
|
||||
static const char **validate_pathspec(const char **argv, const char *prefix)
|
||||
{
|
||||
const char **pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
if (pathspec) {
|
||||
const char **p;
|
||||
for (p = pathspec; *p; p++) {
|
||||
if (has_symlink_leading_path(*p, strlen(*p))) {
|
||||
int len = prefix ? strlen(prefix) : 0;
|
||||
die(_("'%s' is beyond a symbolic link"), *p + len);
|
||||
}
|
||||
die_if_path_beyond_symlink(*p, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,7 +206,7 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)
|
||||
const char **pathspec = NULL;
|
||||
|
||||
if (argc) {
|
||||
pathspec = validate_pathspec(argc, argv, prefix);
|
||||
pathspec = validate_pathspec(argv, prefix);
|
||||
if (!pathspec)
|
||||
return -1;
|
||||
}
|
||||
@ -415,7 +373,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
|
||||
return 0;
|
||||
}
|
||||
pathspec = validate_pathspec(argc, argv, prefix);
|
||||
pathspec = validate_pathspec(argv, prefix);
|
||||
|
||||
if (read_cache() < 0)
|
||||
die(_("index file corrupt"));
|
||||
@ -448,7 +406,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
|
||||
path_exclude_check_init(&check, &dir);
|
||||
if (!seen)
|
||||
seen = find_used_pathspec(pathspec);
|
||||
seen = find_pathspecs_matching_against_index(pathspec);
|
||||
for (i = 0; pathspec[i]; i++) {
|
||||
if (!seen[i] && pathspec[i][0]
|
||||
&& !file_exists(pathspec[i])) {
|
||||
|
173
builtin/check-ignore.c
Normal file
173
builtin/check-ignore.c
Normal file
@ -0,0 +1,173 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "dir.h"
|
||||
#include "quote.h"
|
||||
#include "pathspec.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static int quiet, verbose, stdin_paths;
|
||||
static const char * const check_ignore_usage[] = {
|
||||
"git check-ignore [options] pathname...",
|
||||
"git check-ignore [options] --stdin < <list-of-paths>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int null_term_line;
|
||||
|
||||
static const struct option check_ignore_options[] = {
|
||||
OPT__QUIET(&quiet, N_("suppress progress reporting")),
|
||||
OPT__VERBOSE(&verbose, N_("be verbose")),
|
||||
OPT_GROUP(""),
|
||||
OPT_BOOLEAN(0, "stdin", &stdin_paths,
|
||||
N_("read file names from stdin")),
|
||||
OPT_BOOLEAN('z', NULL, &null_term_line,
|
||||
N_("input paths are terminated by a null character")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static void output_exclude(const char *path, struct exclude *exclude)
|
||||
{
|
||||
char *bang = exclude->flags & EXC_FLAG_NEGATIVE ? "!" : "";
|
||||
char *slash = exclude->flags & EXC_FLAG_MUSTBEDIR ? "/" : "";
|
||||
if (!null_term_line) {
|
||||
if (!verbose) {
|
||||
write_name_quoted(path, stdout, '\n');
|
||||
} else {
|
||||
quote_c_style(exclude->el->src, NULL, stdout, 0);
|
||||
printf(":%d:%s%s%s\t",
|
||||
exclude->srcpos,
|
||||
bang, exclude->pattern, slash);
|
||||
quote_c_style(path, NULL, stdout, 0);
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
} else {
|
||||
if (!verbose) {
|
||||
printf("%s%c", path, '\0');
|
||||
} else {
|
||||
printf("%s%c%d%c%s%s%s%c%s%c",
|
||||
exclude->el->src, '\0',
|
||||
exclude->srcpos, '\0',
|
||||
bang, exclude->pattern, slash, '\0',
|
||||
path, '\0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int check_ignore(const char *prefix, const char **pathspec)
|
||||
{
|
||||
struct dir_struct dir;
|
||||
const char *path, *full_path;
|
||||
char *seen;
|
||||
int num_ignored = 0, dtype = DT_UNKNOWN, i;
|
||||
struct path_exclude_check check;
|
||||
struct exclude *exclude;
|
||||
|
||||
/* read_cache() is only necessary so we can watch out for submodules. */
|
||||
if (read_cache() < 0)
|
||||
die(_("index file corrupt"));
|
||||
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
dir.flags |= DIR_COLLECT_IGNORED;
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
if (!pathspec || !*pathspec) {
|
||||
if (!quiet)
|
||||
fprintf(stderr, "no pathspec given.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
path_exclude_check_init(&check, &dir);
|
||||
/*
|
||||
* look for pathspecs matching entries in the index, since these
|
||||
* should not be ignored, in order to be consistent with
|
||||
* 'git status', 'git add' etc.
|
||||
*/
|
||||
seen = find_pathspecs_matching_against_index(pathspec);
|
||||
for (i = 0; pathspec[i]; i++) {
|
||||
path = pathspec[i];
|
||||
full_path = prefix_path(prefix, prefix
|
||||
? strlen(prefix) : 0, path);
|
||||
full_path = check_path_for_gitlink(full_path);
|
||||
die_if_path_beyond_symlink(full_path, prefix);
|
||||
if (!seen[i] && path[0]) {
|
||||
exclude = last_exclude_matching_path(&check, full_path,
|
||||
-1, &dtype);
|
||||
if (exclude) {
|
||||
if (!quiet)
|
||||
output_exclude(path, exclude);
|
||||
num_ignored++;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(seen);
|
||||
clear_directory(&dir);
|
||||
path_exclude_check_clear(&check);
|
||||
|
||||
return num_ignored;
|
||||
}
|
||||
|
||||
static int check_ignore_stdin_paths(const char *prefix)
|
||||
{
|
||||
struct strbuf buf, nbuf;
|
||||
char **pathspec = NULL;
|
||||
size_t nr = 0, alloc = 0;
|
||||
int line_termination = null_term_line ? 0 : '\n';
|
||||
int num_ignored;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_init(&nbuf, 0);
|
||||
while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
|
||||
if (line_termination && buf.buf[0] == '"') {
|
||||
strbuf_reset(&nbuf);
|
||||
if (unquote_c_style(&nbuf, buf.buf, NULL))
|
||||
die("line is badly quoted");
|
||||
strbuf_swap(&buf, &nbuf);
|
||||
}
|
||||
ALLOC_GROW(pathspec, nr + 1, alloc);
|
||||
pathspec[nr] = xcalloc(strlen(buf.buf) + 1, sizeof(*buf.buf));
|
||||
strcpy(pathspec[nr++], buf.buf);
|
||||
}
|
||||
ALLOC_GROW(pathspec, nr + 1, alloc);
|
||||
pathspec[nr] = NULL;
|
||||
num_ignored = check_ignore(prefix, (const char **)pathspec);
|
||||
maybe_flush_or_die(stdout, "attribute to stdout");
|
||||
strbuf_release(&buf);
|
||||
strbuf_release(&nbuf);
|
||||
free(pathspec);
|
||||
return num_ignored;
|
||||
}
|
||||
|
||||
int cmd_check_ignore(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int num_ignored;
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, check_ignore_options,
|
||||
check_ignore_usage, 0);
|
||||
|
||||
if (stdin_paths) {
|
||||
if (argc > 0)
|
||||
die(_("cannot specify pathnames with --stdin"));
|
||||
} else {
|
||||
if (null_term_line)
|
||||
die(_("-z only makes sense with --stdin"));
|
||||
if (argc == 0)
|
||||
die(_("no path specified"));
|
||||
}
|
||||
if (quiet) {
|
||||
if (argc > 1)
|
||||
die(_("--quiet is only valid with a single pathname"));
|
||||
if (verbose)
|
||||
die(_("cannot have both --quiet and --verbose"));
|
||||
}
|
||||
|
||||
if (stdin_paths) {
|
||||
num_ignored = check_ignore_stdin_paths(prefix);
|
||||
} else {
|
||||
num_ignored = check_ignore(prefix, argv);
|
||||
maybe_flush_or_die(stdout, "ignore to stdout");
|
||||
}
|
||||
|
||||
return !num_ignored;
|
||||
}
|
@ -153,6 +153,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
static const char **pathspec;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
|
||||
struct exclude_list *el;
|
||||
const char *qname;
|
||||
char *seen = NULL;
|
||||
struct option options[] = {
|
||||
@ -205,9 +206,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
||||
if (!ignored)
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
|
||||
for (i = 0; i < exclude_list.nr; i++)
|
||||
add_exclude(exclude_list.items[i].string, "", 0,
|
||||
&dir.exclude_list[EXC_CMDL]);
|
||||
add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1));
|
||||
|
||||
pathspec = get_pathspec(prefix, argv);
|
||||
|
||||
|
@ -35,6 +35,7 @@ static int error_unmatch;
|
||||
static char *ps_matched;
|
||||
static const char *with_tree;
|
||||
static int exc_given;
|
||||
static int exclude_args;
|
||||
|
||||
static const char *tag_cached = "";
|
||||
static const char *tag_unmerged = "";
|
||||
@ -420,10 +421,10 @@ static int option_parse_z(const struct option *opt,
|
||||
static int option_parse_exclude(const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
struct exclude_list *list = opt->value;
|
||||
struct string_list *exclude_list = opt->value;
|
||||
|
||||
exc_given = 1;
|
||||
add_exclude(arg, "", 0, list);
|
||||
string_list_append(exclude_list, arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -452,9 +453,11 @@ static int option_parse_exclude_standard(const struct option *opt,
|
||||
|
||||
int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||
{
|
||||
int require_work_tree = 0, show_tag = 0;
|
||||
int require_work_tree = 0, show_tag = 0, i;
|
||||
const char *max_prefix;
|
||||
struct dir_struct dir;
|
||||
struct exclude_list *el;
|
||||
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
|
||||
struct option builtin_ls_files_options[] = {
|
||||
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
|
||||
N_("paths are separated with NUL character"),
|
||||
@ -488,7 +491,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||
N_("show unmerged files in the output")),
|
||||
OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
|
||||
N_("show resolve-undo information")),
|
||||
{ OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], N_("pattern"),
|
||||
{ OPTION_CALLBACK, 'x', "exclude", &exclude_list, N_("pattern"),
|
||||
N_("skip files matching pattern"),
|
||||
0, option_parse_exclude },
|
||||
{ OPTION_CALLBACK, 'X', "exclude-from", &dir, N_("file"),
|
||||
@ -525,6 +528,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||
|
||||
argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
|
||||
ls_files_usage, 0);
|
||||
el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
|
||||
for (i = 0; i < exclude_list.nr; i++) {
|
||||
add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
|
||||
}
|
||||
if (show_tag || show_valid_bit) {
|
||||
tag_cached = "H ";
|
||||
tag_unmerged = "M ";
|
||||
|
@ -12,6 +12,7 @@ git-branch mainporcelain common
|
||||
git-bundle mainporcelain
|
||||
git-cat-file plumbinginterrogators
|
||||
git-check-attr purehelpers
|
||||
git-check-ignore purehelpers
|
||||
git-checkout mainporcelain common
|
||||
git-checkout-index plumbingmanipulators
|
||||
git-check-ref-format purehelpers
|
||||
|
@ -563,6 +563,7 @@ __git_list_porcelain_commands ()
|
||||
archimport) : import;;
|
||||
cat-file) : plumbing;;
|
||||
check-attr) : plumbing;;
|
||||
check-ignore) : plumbing;;
|
||||
check-ref-format) : plumbing;;
|
||||
checkout-index) : plumbing;;
|
||||
commit-tree) : plumbing;;
|
||||
|
152
dir.c
152
dir.c
@ -194,12 +194,19 @@ static int match_one(const char *match, const char *name, int namelen)
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a name and a list of pathspecs, see if the name matches
|
||||
* any of the pathspecs. The caller is also interested in seeing
|
||||
* all pathspec matches some names it calls this function with
|
||||
* (otherwise the user could have mistyped the unmatched pathspec),
|
||||
* and a mark is left in seen[] array for pathspec element that
|
||||
* actually matched anything.
|
||||
* Given a name and a list of pathspecs, returns the nature of the
|
||||
* closest (i.e. most specific) match of the name to any of the
|
||||
* pathspecs.
|
||||
*
|
||||
* The caller typically calls this multiple times with the same
|
||||
* pathspec and seen[] array but with different name/namelen
|
||||
* (e.g. entries from the index) and is interested in seeing if and
|
||||
* how each pathspec matches all the names it calls this function
|
||||
* with. A mark is left in the seen[] array for each pathspec element
|
||||
* indicating the closest type of match that element achieved, so if
|
||||
* seen[n] remains zero after multiple invocations, that means the nth
|
||||
* pathspec did not match any names, which could indicate that the
|
||||
* user mistyped the nth pathspec.
|
||||
*/
|
||||
int match_pathspec(const char **pathspec, const char *name, int namelen,
|
||||
int prefix, char *seen)
|
||||
@ -269,12 +276,19 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a name and a list of pathspecs, see if the name matches
|
||||
* any of the pathspecs. The caller is also interested in seeing
|
||||
* all pathspec matches some names it calls this function with
|
||||
* (otherwise the user could have mistyped the unmatched pathspec),
|
||||
* and a mark is left in seen[] array for pathspec element that
|
||||
* actually matched anything.
|
||||
* Given a name and a list of pathspecs, returns the nature of the
|
||||
* closest (i.e. most specific) match of the name to any of the
|
||||
* pathspecs.
|
||||
*
|
||||
* The caller typically calls this multiple times with the same
|
||||
* pathspec and seen[] array but with different name/namelen
|
||||
* (e.g. entries from the index) and is interested in seeing if and
|
||||
* how each pathspec matches all the names it calls this function
|
||||
* with. A mark is left in the seen[] array for each pathspec element
|
||||
* indicating the closest type of match that element achieved, so if
|
||||
* seen[n] remains zero after multiple invocations, that means the nth
|
||||
* pathspec did not match any names, which could indicate that the
|
||||
* user mistyped the nth pathspec.
|
||||
*/
|
||||
int match_pathspec_depth(const struct pathspec *ps,
|
||||
const char *name, int namelen,
|
||||
@ -379,7 +393,7 @@ void parse_exclude_pattern(const char **pattern,
|
||||
}
|
||||
|
||||
void add_exclude(const char *string, const char *base,
|
||||
int baselen, struct exclude_list *el)
|
||||
int baselen, struct exclude_list *el, int srcpos)
|
||||
{
|
||||
struct exclude *x;
|
||||
int patternlen;
|
||||
@ -403,8 +417,10 @@ void add_exclude(const char *string, const char *base,
|
||||
x->base = base;
|
||||
x->baselen = baselen;
|
||||
x->flags = flags;
|
||||
x->srcpos = srcpos;
|
||||
ALLOC_GROW(el->excludes, el->nr + 1, el->alloc);
|
||||
el->excludes[el->nr++] = x;
|
||||
x->el = el;
|
||||
}
|
||||
|
||||
static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
|
||||
@ -441,20 +457,21 @@ void clear_exclude_list(struct exclude_list *el)
|
||||
for (i = 0; i < el->nr; i++)
|
||||
free(el->excludes[i]);
|
||||
free(el->excludes);
|
||||
free(el->filebuf);
|
||||
|
||||
el->nr = 0;
|
||||
el->excludes = NULL;
|
||||
el->filebuf = NULL;
|
||||
}
|
||||
|
||||
int add_excludes_from_file_to_list(const char *fname,
|
||||
const char *base,
|
||||
int baselen,
|
||||
char **buf_p,
|
||||
struct exclude_list *el,
|
||||
int check_index)
|
||||
{
|
||||
struct stat st;
|
||||
int fd, i;
|
||||
int fd, i, lineno = 1;
|
||||
size_t size = 0;
|
||||
char *buf, *entry;
|
||||
|
||||
@ -492,25 +509,43 @@ int add_excludes_from_file_to_list(const char *fname,
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (buf_p)
|
||||
*buf_p = buf;
|
||||
el->filebuf = buf;
|
||||
entry = buf;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (buf[i] == '\n') {
|
||||
if (entry != buf + i && entry[0] != '#') {
|
||||
buf[i - (i && buf[i-1] == '\r')] = 0;
|
||||
add_exclude(entry, base, baselen, el);
|
||||
add_exclude(entry, base, baselen, el, lineno);
|
||||
}
|
||||
lineno++;
|
||||
entry = buf + i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct exclude_list *add_exclude_list(struct dir_struct *dir,
|
||||
int group_type, const char *src)
|
||||
{
|
||||
struct exclude_list *el;
|
||||
struct exclude_list_group *group;
|
||||
|
||||
group = &dir->exclude_list_group[group_type];
|
||||
ALLOC_GROW(group->el, group->nr + 1, group->alloc);
|
||||
el = &group->el[group->nr++];
|
||||
memset(el, 0, sizeof(*el));
|
||||
el->src = src;
|
||||
return el;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to set up core.excludesfile and .git/info/exclude lists.
|
||||
*/
|
||||
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
|
||||
{
|
||||
if (add_excludes_from_file_to_list(fname, "", 0, NULL,
|
||||
&dir->exclude_list[EXC_FILE], 0) < 0)
|
||||
struct exclude_list *el;
|
||||
el = add_exclude_list(dir, EXC_FILE, fname);
|
||||
if (add_excludes_from_file_to_list(fname, "", 0, el, 0) < 0)
|
||||
die("cannot use %s as an exclude file", fname);
|
||||
}
|
||||
|
||||
@ -520,6 +555,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)
|
||||
*/
|
||||
static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
|
||||
{
|
||||
struct exclude_list_group *group;
|
||||
struct exclude_list *el;
|
||||
struct exclude_stack *stk = NULL;
|
||||
int current;
|
||||
@ -528,17 +564,21 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
|
||||
(baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
|
||||
return; /* too long a path -- ignore */
|
||||
|
||||
/* Pop the directories that are not the prefix of the path being checked. */
|
||||
el = &dir->exclude_list[EXC_DIRS];
|
||||
group = &dir->exclude_list_group[EXC_DIRS];
|
||||
|
||||
/* Pop the exclude lists from the EXCL_DIRS exclude_list_group
|
||||
* which originate from directories not in the prefix of the
|
||||
* path being checked. */
|
||||
while ((stk = dir->exclude_stack) != NULL) {
|
||||
if (stk->baselen <= baselen &&
|
||||
!strncmp(dir->basebuf, base, stk->baselen))
|
||||
break;
|
||||
el = &group->el[dir->exclude_stack->exclude_ix];
|
||||
dir->exclude_stack = stk->prev;
|
||||
while (stk->exclude_ix < el->nr)
|
||||
free(el->excludes[--el->nr]);
|
||||
free(stk->filebuf);
|
||||
free((char *)el->src); /* see strdup() below */
|
||||
clear_exclude_list(el);
|
||||
free(stk);
|
||||
group->nr--;
|
||||
}
|
||||
|
||||
/* Read from the parent directories and push them down. */
|
||||
@ -559,13 +599,22 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
|
||||
}
|
||||
stk->prev = dir->exclude_stack;
|
||||
stk->baselen = cp - base;
|
||||
stk->exclude_ix = el->nr;
|
||||
memcpy(dir->basebuf + current, base + current,
|
||||
stk->baselen - current);
|
||||
strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
|
||||
/*
|
||||
* dir->basebuf gets reused by the traversal, but we
|
||||
* need fname to remain unchanged to ensure the src
|
||||
* member of each struct exclude correctly
|
||||
* back-references its source file. Other invocations
|
||||
* of add_exclude_list provide stable strings, so we
|
||||
* strdup() and free() here in the caller.
|
||||
*/
|
||||
el = add_exclude_list(dir, EXC_DIRS, strdup(dir->basebuf));
|
||||
stk->exclude_ix = group->nr - 1;
|
||||
add_excludes_from_file_to_list(dir->basebuf,
|
||||
dir->basebuf, stk->baselen,
|
||||
&stk->filebuf, el, 1);
|
||||
el, 1);
|
||||
dir->exclude_stack = stk;
|
||||
current = stk->baselen;
|
||||
}
|
||||
@ -712,18 +761,23 @@ static struct exclude *last_exclude_matching(struct dir_struct *dir,
|
||||
int *dtype_p)
|
||||
{
|
||||
int pathlen = strlen(pathname);
|
||||
int st;
|
||||
int i, j;
|
||||
struct exclude_list_group *group;
|
||||
struct exclude *exclude;
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
basename = (basename) ? basename+1 : pathname;
|
||||
|
||||
prep_exclude(dir, pathname, basename-pathname);
|
||||
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
|
||||
exclude = last_exclude_matching_from_list(
|
||||
pathname, pathlen, basename, dtype_p,
|
||||
&dir->exclude_list[st]);
|
||||
if (exclude)
|
||||
return exclude;
|
||||
|
||||
for (i = EXC_CMDL; i <= EXC_FILE; i++) {
|
||||
group = &dir->exclude_list_group[i];
|
||||
for (j = group->nr - 1; j >= 0; j--) {
|
||||
exclude = last_exclude_matching_from_list(
|
||||
pathname, pathlen, basename, dtype_p,
|
||||
&group->el[j]);
|
||||
if (exclude)
|
||||
return exclude;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -1627,3 +1681,33 @@ int limit_pathspec_to_literal(void)
|
||||
flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
|
||||
return flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees memory within dir which was allocated for exclude lists and
|
||||
* the exclude_stack. Does not free dir itself.
|
||||
*/
|
||||
void clear_directory(struct dir_struct *dir)
|
||||
{
|
||||
int i, j;
|
||||
struct exclude_list_group *group;
|
||||
struct exclude_list *el;
|
||||
struct exclude_stack *stk;
|
||||
|
||||
for (i = EXC_CMDL; i <= EXC_FILE; i++) {
|
||||
group = &dir->exclude_list_group[i];
|
||||
for (j = 0; j < group->nr; j++) {
|
||||
el = &group->el[j];
|
||||
if (i == EXC_DIRS)
|
||||
free((char *)el->src);
|
||||
clear_exclude_list(el);
|
||||
}
|
||||
free(group->el);
|
||||
}
|
||||
|
||||
stk = dir->exclude_stack;
|
||||
while (stk) {
|
||||
struct exclude_stack *prev = stk->prev;
|
||||
free(stk);
|
||||
stk = prev;
|
||||
}
|
||||
}
|
||||
|
62
dir.h
62
dir.h
@ -16,21 +16,41 @@ struct dir_entry {
|
||||
#define EXC_FLAG_NEGATIVE 16
|
||||
|
||||
/*
|
||||
* Each .gitignore file will be parsed into patterns which are then
|
||||
* appended to the relevant exclude_list (either EXC_DIRS or
|
||||
* EXC_FILE). exclude_lists are also used to represent the list of
|
||||
* --exclude values passed via CLI args (EXC_CMDL).
|
||||
* Each excludes file will be parsed into a fresh exclude_list which
|
||||
* is appended to the relevant exclude_list_group (either EXC_DIRS or
|
||||
* EXC_FILE). An exclude_list within the EXC_CMDL exclude_list_group
|
||||
* can also be used to represent the list of --exclude values passed
|
||||
* via CLI args.
|
||||
*/
|
||||
struct exclude_list {
|
||||
int nr;
|
||||
int alloc;
|
||||
|
||||
/* remember pointer to exclude file contents so we can free() */
|
||||
char *filebuf;
|
||||
|
||||
/* origin of list, e.g. path to filename, or descriptive string */
|
||||
const char *src;
|
||||
|
||||
struct exclude {
|
||||
/*
|
||||
* This allows callers of last_exclude_matching() etc.
|
||||
* to determine the origin of the matching pattern.
|
||||
*/
|
||||
struct exclude_list *el;
|
||||
|
||||
const char *pattern;
|
||||
int patternlen;
|
||||
int nowildcardlen;
|
||||
const char *base;
|
||||
int baselen;
|
||||
int flags;
|
||||
|
||||
/*
|
||||
* Counting starts from 1 for line numbers in ignore files,
|
||||
* and from -1 decrementing for patterns from CLI args.
|
||||
*/
|
||||
int srcpos;
|
||||
} **excludes;
|
||||
};
|
||||
|
||||
@ -42,9 +62,13 @@ struct exclude_list {
|
||||
*/
|
||||
struct exclude_stack {
|
||||
struct exclude_stack *prev; /* the struct exclude_stack for the parent directory */
|
||||
char *filebuf; /* remember pointer to per-directory exclude file contents so we can free() */
|
||||
int baselen;
|
||||
int exclude_ix;
|
||||
int exclude_ix; /* index of exclude_list within EXC_DIRS exclude_list_group */
|
||||
};
|
||||
|
||||
struct exclude_list_group {
|
||||
int nr, alloc;
|
||||
struct exclude_list *el;
|
||||
};
|
||||
|
||||
struct dir_struct {
|
||||
@ -62,16 +86,23 @@ struct dir_struct {
|
||||
|
||||
/* Exclude info */
|
||||
const char *exclude_per_dir;
|
||||
struct exclude_list exclude_list[3];
|
||||
|
||||
/*
|
||||
* We maintain three exclude pattern lists:
|
||||
* We maintain three groups of exclude pattern lists:
|
||||
*
|
||||
* EXC_CMDL lists patterns explicitly given on the command line.
|
||||
* EXC_DIRS lists patterns obtained from per-directory ignore files.
|
||||
* EXC_FILE lists patterns from fallback ignore files.
|
||||
* EXC_FILE lists patterns from fallback ignore files, e.g.
|
||||
* - .git/info/exclude
|
||||
* - core.excludesfile
|
||||
*
|
||||
* Each group contains multiple exclude lists, a single list
|
||||
* per source.
|
||||
*/
|
||||
#define EXC_CMDL 0
|
||||
#define EXC_DIRS 1
|
||||
#define EXC_FILE 2
|
||||
struct exclude_list_group exclude_list_group[3];
|
||||
|
||||
/*
|
||||
* Temporary variables which are used during loading of the
|
||||
@ -85,6 +116,12 @@ struct dir_struct {
|
||||
char basebuf[PATH_MAX];
|
||||
};
|
||||
|
||||
/*
|
||||
* The ordering of these constants is significant, with
|
||||
* higher-numbered match types signifying "closer" (i.e. more
|
||||
* specific) matches which will override lower-numbered match types
|
||||
* when populating the seen[] array.
|
||||
*/
|
||||
#define MATCHED_RECURSIVELY 1
|
||||
#define MATCHED_FNMATCH 2
|
||||
#define MATCHED_EXACTLY 3
|
||||
@ -129,13 +166,16 @@ extern struct exclude *last_exclude_matching_path(struct path_exclude_check *, c
|
||||
extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);
|
||||
|
||||
|
||||
extern struct exclude_list *add_exclude_list(struct dir_struct *dir,
|
||||
int group_type, const char *src);
|
||||
extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
|
||||
char **buf_p, struct exclude_list *el, int check_index);
|
||||
struct exclude_list *el, int check_index);
|
||||
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
|
||||
extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen);
|
||||
extern void add_exclude(const char *string, const char *base,
|
||||
int baselen, struct exclude_list *el);
|
||||
int baselen, struct exclude_list *el, int srcpos);
|
||||
extern void clear_exclude_list(struct exclude_list *el);
|
||||
extern void clear_directory(struct dir_struct *dir);
|
||||
extern int file_exists(const char *);
|
||||
|
||||
extern int is_inside_dir(const char *dir);
|
||||
|
1
git.c
1
git.c
@ -313,6 +313,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
|
||||
{ "cat-file", cmd_cat_file, RUN_SETUP },
|
||||
{ "check-attr", cmd_check_attr, RUN_SETUP },
|
||||
{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "check-ref-format", cmd_check_ref_format },
|
||||
{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "checkout-index", cmd_checkout_index,
|
||||
|
101
pathspec.c
Normal file
101
pathspec.c
Normal file
@ -0,0 +1,101 @@
|
||||
#include "cache.h"
|
||||
#include "dir.h"
|
||||
#include "pathspec.h"
|
||||
|
||||
/*
|
||||
* Finds which of the given pathspecs match items in the index.
|
||||
*
|
||||
* For each pathspec, sets the corresponding entry in the seen[] array
|
||||
* (which should be specs items long, i.e. the same size as pathspec)
|
||||
* to the nature of the "closest" (i.e. most specific) match found for
|
||||
* that pathspec in the index, if it was a closer type of match than
|
||||
* the existing entry. As an optimization, matching is skipped
|
||||
* altogether if seen[] already only contains non-zero entries.
|
||||
*
|
||||
* If seen[] has not already been written to, it may make sense
|
||||
* to use find_pathspecs_matching_against_index() instead.
|
||||
*/
|
||||
void add_pathspec_matches_against_index(const char **pathspec,
|
||||
char *seen, int specs)
|
||||
{
|
||||
int num_unmatched = 0, i;
|
||||
|
||||
/*
|
||||
* Since we are walking the index as if we were walking the directory,
|
||||
* we have to mark the matched pathspec as seen; otherwise we will
|
||||
* mistakenly think that the user gave a pathspec that did not match
|
||||
* anything.
|
||||
*/
|
||||
for (i = 0; i < specs; i++)
|
||||
if (!seen[i])
|
||||
num_unmatched++;
|
||||
if (!num_unmatched)
|
||||
return;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds which of the given pathspecs match items in the index.
|
||||
*
|
||||
* This is a one-shot wrapper around add_pathspec_matches_against_index()
|
||||
* which allocates, populates, and returns a seen[] array indicating the
|
||||
* nature of the "closest" (i.e. most specific) matches which each of the
|
||||
* given pathspecs achieves against all items in the index.
|
||||
*/
|
||||
char *find_pathspecs_matching_against_index(const char **pathspec)
|
||||
{
|
||||
char *seen;
|
||||
int i;
|
||||
|
||||
for (i = 0; pathspec[i]; i++)
|
||||
; /* just counting */
|
||||
seen = xcalloc(i, 1);
|
||||
add_pathspec_matches_against_index(pathspec, seen, i);
|
||||
return seen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the index to see whether path refers to a submodule, or
|
||||
* something inside a submodule. If the former, returns the path with
|
||||
* any trailing slash stripped. If the latter, dies with an error
|
||||
* message.
|
||||
*/
|
||||
const char *check_path_for_gitlink(const char *path)
|
||||
{
|
||||
int i, path_len = strlen(path);
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (S_ISGITLINK(ce->ce_mode)) {
|
||||
int ce_len = ce_namelen(ce);
|
||||
if (path_len <= ce_len || path[ce_len] != '/' ||
|
||||
memcmp(ce->name, path, ce_len))
|
||||
/* path does not refer to this
|
||||
* submodule or anything inside it */
|
||||
continue;
|
||||
if (path_len == ce_len + 1) {
|
||||
/* path refers to submodule;
|
||||
* strip trailing slash */
|
||||
return xstrndup(ce->name, ce_len);
|
||||
} else {
|
||||
die (_("Path '%s' is in submodule '%.*s'"),
|
||||
path, ce_len, ce->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dies if the given path refers to a file inside a symlinked
|
||||
* directory in the index.
|
||||
*/
|
||||
void die_if_path_beyond_symlink(const char *path, const char *prefix)
|
||||
{
|
||||
if (has_symlink_leading_path(path, strlen(path))) {
|
||||
int len = prefix ? strlen(prefix) : 0;
|
||||
die(_("'%s' is beyond a symbolic link"), path + len);
|
||||
}
|
||||
}
|
9
pathspec.h
Normal file
9
pathspec.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef PATHSPEC_H
|
||||
#define PATHSPEC_H
|
||||
|
||||
extern char *find_pathspecs_matching_against_index(const char **pathspec);
|
||||
extern void add_pathspec_matches_against_index(const char **pathspec, char *seen, int specs);
|
||||
extern const char *check_path_for_gitlink(const char *path);
|
||||
extern void die_if_path_beyond_symlink(const char *path, const char *prefix);
|
||||
|
||||
#endif /* PATHSPEC_H */
|
19
setup.c
19
setup.c
@ -246,6 +246,25 @@ static const char *prefix_pathspec(const char *prefix, int prefixlen, const char
|
||||
return prefix_path(prefix, prefixlen, copyfrom);
|
||||
}
|
||||
|
||||
/*
|
||||
* N.B. get_pathspec() is deprecated in favor of the "struct pathspec"
|
||||
* based interface - see pathspec_magic above.
|
||||
*
|
||||
* Arguments:
|
||||
* - prefix - a path relative to the root of the working tree
|
||||
* - pathspec - a list of paths underneath the prefix path
|
||||
*
|
||||
* Iterates over pathspec, prepending each path with prefix,
|
||||
* and return the resulting list.
|
||||
*
|
||||
* If pathspec is empty, return a singleton list containing prefix.
|
||||
*
|
||||
* If pathspec and prefix are both empty, return an empty list.
|
||||
*
|
||||
* This is typically used by built-in commands such as add.c, in order
|
||||
* to normalize argv arguments provided to the built-in into a list of
|
||||
* paths to process, all relative to the root of the working tree.
|
||||
*/
|
||||
const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||
{
|
||||
const char *entry = *pathspec;
|
||||
|
637
t/t0008-ignores.sh
Executable file
637
t/t0008-ignores.sh
Executable file
@ -0,0 +1,637 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description=check-ignore
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
init_vars () {
|
||||
global_excludes="$(pwd)/global-excludes"
|
||||
}
|
||||
|
||||
enable_global_excludes () {
|
||||
init_vars &&
|
||||
git config core.excludesfile "$global_excludes"
|
||||
}
|
||||
|
||||
expect_in () {
|
||||
dest="$HOME/expected-$1" text="$2"
|
||||
if test -z "$text"
|
||||
then
|
||||
>"$dest" # avoid newline
|
||||
else
|
||||
echo "$text" >"$dest"
|
||||
fi
|
||||
}
|
||||
|
||||
expect () {
|
||||
expect_in stdout "$1"
|
||||
}
|
||||
|
||||
expect_from_stdin () {
|
||||
cat >"$HOME/expected-stdout"
|
||||
}
|
||||
|
||||
test_stderr () {
|
||||
expected="$1"
|
||||
expect_in stderr "$1" &&
|
||||
test_cmp "$HOME/expected-stderr" "$HOME/stderr"
|
||||
}
|
||||
|
||||
stderr_contains () {
|
||||
regexp="$1"
|
||||
if grep "$regexp" "$HOME/stderr"
|
||||
then
|
||||
return 0
|
||||
else
|
||||
echo "didn't find /$regexp/ in $HOME/stderr"
|
||||
cat "$HOME/stderr"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stderr_empty_on_success () {
|
||||
expect_code="$1"
|
||||
if test $expect_code = 0
|
||||
then
|
||||
test_stderr ""
|
||||
else
|
||||
# If we expect failure then stderr might or might not be empty
|
||||
# due to --quiet - the caller can check its contents
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
test_check_ignore () {
|
||||
args="$1" expect_code="${2:-0}" global_args="$3"
|
||||
|
||||
init_vars &&
|
||||
rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" &&
|
||||
echo git $global_args check-ignore $quiet_opt $verbose_opt $args \
|
||||
>"$HOME/cmd" &&
|
||||
test_expect_code "$expect_code" \
|
||||
git $global_args check-ignore $quiet_opt $verbose_opt $args \
|
||||
>"$HOME/stdout" 2>"$HOME/stderr" &&
|
||||
test_cmp "$HOME/expected-stdout" "$HOME/stdout" &&
|
||||
stderr_empty_on_success "$expect_code"
|
||||
}
|
||||
|
||||
test_expect_success_multi () {
|
||||
prereq=
|
||||
if test $# -eq 4
|
||||
then
|
||||
prereq=$1
|
||||
shift
|
||||
fi
|
||||
testname="$1" expect_verbose="$2" code="$3"
|
||||
|
||||
expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
|
||||
|
||||
test_expect_success $prereq "$testname" '
|
||||
expect "$expect" &&
|
||||
eval "$code"
|
||||
'
|
||||
|
||||
for quiet_opt in '-q' '--quiet'
|
||||
do
|
||||
test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
|
||||
expect '' &&
|
||||
$code
|
||||
"
|
||||
done
|
||||
quiet_opt=
|
||||
|
||||
for verbose_opt in '-v' '--verbose'
|
||||
do
|
||||
test_expect_success $prereq "$testname${verbose_opt:+ with $verbose_opt}" "
|
||||
expect '$expect_verbose' &&
|
||||
$code
|
||||
"
|
||||
done
|
||||
verbose_opt=
|
||||
}
|
||||
|
||||
test_expect_success 'setup' '
|
||||
init_vars &&
|
||||
mkdir -p a/b/ignored-dir a/submodule b &&
|
||||
if test_have_prereq SYMLINKS
|
||||
then
|
||||
ln -s b a/symlink
|
||||
fi &&
|
||||
(
|
||||
cd a/submodule &&
|
||||
git init &&
|
||||
echo a >a &&
|
||||
git add a &&
|
||||
git commit -m"commit in submodule"
|
||||
) &&
|
||||
git add a/submodule &&
|
||||
cat <<-\EOF >.gitignore &&
|
||||
one
|
||||
ignored-*
|
||||
EOF
|
||||
for dir in . a
|
||||
do
|
||||
: >$dir/not-ignored &&
|
||||
: >$dir/ignored-and-untracked &&
|
||||
: >$dir/ignored-but-in-index
|
||||
done &&
|
||||
git add -f ignored-but-in-index a/ignored-but-in-index &&
|
||||
cat <<-\EOF >a/.gitignore &&
|
||||
two*
|
||||
*three
|
||||
EOF
|
||||
cat <<-\EOF >a/b/.gitignore &&
|
||||
four
|
||||
five
|
||||
# this comment should affect the line numbers
|
||||
six
|
||||
ignored-dir/
|
||||
# and so should this blank line:
|
||||
|
||||
!on*
|
||||
!two
|
||||
EOF
|
||||
echo "seven" >a/b/ignored-dir/.gitignore &&
|
||||
test -n "$HOME" &&
|
||||
cat <<-\EOF >"$global_excludes" &&
|
||||
globalone
|
||||
!globaltwo
|
||||
globalthree
|
||||
EOF
|
||||
cat <<-\EOF >>.git/info/exclude
|
||||
per-repo
|
||||
EOF
|
||||
'
|
||||
|
||||
############################################################################
|
||||
#
|
||||
# test invalid inputs
|
||||
|
||||
test_expect_success_multi 'empty command line' '' '
|
||||
test_check_ignore "" 128 &&
|
||||
stderr_contains "fatal: no path specified"
|
||||
'
|
||||
|
||||
test_expect_success_multi '--stdin with empty STDIN' '' '
|
||||
test_check_ignore "--stdin" 1 </dev/null &&
|
||||
if test -n "$quiet_opt"; then
|
||||
test_stderr ""
|
||||
else
|
||||
test_stderr "no pathspec given."
|
||||
fi
|
||||
'
|
||||
|
||||
test_expect_success '-q with multiple args' '
|
||||
expect "" &&
|
||||
test_check_ignore "-q one two" 128 &&
|
||||
stderr_contains "fatal: --quiet is only valid with a single pathname"
|
||||
'
|
||||
|
||||
test_expect_success '--quiet with multiple args' '
|
||||
expect "" &&
|
||||
test_check_ignore "--quiet one two" 128 &&
|
||||
stderr_contains "fatal: --quiet is only valid with a single pathname"
|
||||
'
|
||||
|
||||
for verbose_opt in '-v' '--verbose'
|
||||
do
|
||||
for quiet_opt in '-q' '--quiet'
|
||||
do
|
||||
test_expect_success "$quiet_opt $verbose_opt" "
|
||||
expect '' &&
|
||||
test_check_ignore '$quiet_opt $verbose_opt foo' 128 &&
|
||||
stderr_contains 'fatal: cannot have both --quiet and --verbose'
|
||||
"
|
||||
done
|
||||
done
|
||||
|
||||
test_expect_success '--quiet with multiple args' '
|
||||
expect "" &&
|
||||
test_check_ignore "--quiet one two" 128 &&
|
||||
stderr_contains "fatal: --quiet is only valid with a single pathname"
|
||||
'
|
||||
|
||||
test_expect_success_multi 'erroneous use of --' '' '
|
||||
test_check_ignore "--" 128 &&
|
||||
stderr_contains "fatal: no path specified"
|
||||
'
|
||||
|
||||
test_expect_success_multi '--stdin with superfluous arg' '' '
|
||||
test_check_ignore "--stdin foo" 128 &&
|
||||
stderr_contains "fatal: cannot specify pathnames with --stdin"
|
||||
'
|
||||
|
||||
test_expect_success_multi '--stdin -z with superfluous arg' '' '
|
||||
test_check_ignore "--stdin -z foo" 128 &&
|
||||
stderr_contains "fatal: cannot specify pathnames with --stdin"
|
||||
'
|
||||
|
||||
test_expect_success_multi '-z without --stdin' '' '
|
||||
test_check_ignore "-z" 128 &&
|
||||
stderr_contains "fatal: -z only makes sense with --stdin"
|
||||
'
|
||||
|
||||
test_expect_success_multi '-z without --stdin and superfluous arg' '' '
|
||||
test_check_ignore "-z foo" 128 &&
|
||||
stderr_contains "fatal: -z only makes sense with --stdin"
|
||||
'
|
||||
|
||||
test_expect_success_multi 'needs work tree' '' '
|
||||
(
|
||||
cd .git &&
|
||||
test_check_ignore "foo" 128
|
||||
) &&
|
||||
stderr_contains "fatal: This operation must be run in a work tree"
|
||||
'
|
||||
|
||||
############################################################################
|
||||
#
|
||||
# test standard ignores
|
||||
|
||||
# First make sure that the presence of a file in the working tree
|
||||
# does not impact results, but that the presence of a file in the
|
||||
# index does.
|
||||
|
||||
for subdir in '' 'a/'
|
||||
do
|
||||
if test -z "$subdir"
|
||||
then
|
||||
where="at top-level"
|
||||
else
|
||||
where="in subdir $subdir"
|
||||
fi
|
||||
|
||||
test_expect_success_multi "non-existent file $where not ignored" '' "
|
||||
test_check_ignore '${subdir}non-existent' 1
|
||||
"
|
||||
|
||||
test_expect_success_multi "non-existent file $where ignored" \
|
||||
".gitignore:1:one ${subdir}one" "
|
||||
test_check_ignore '${subdir}one'
|
||||
"
|
||||
|
||||
test_expect_success_multi "existing untracked file $where not ignored" '' "
|
||||
test_check_ignore '${subdir}not-ignored' 1
|
||||
"
|
||||
|
||||
test_expect_success_multi "existing tracked file $where not ignored" '' "
|
||||
test_check_ignore '${subdir}ignored-but-in-index' 1
|
||||
"
|
||||
|
||||
test_expect_success_multi "existing untracked file $where ignored" \
|
||||
".gitignore:2:ignored-* ${subdir}ignored-and-untracked" "
|
||||
test_check_ignore '${subdir}ignored-and-untracked'
|
||||
"
|
||||
done
|
||||
|
||||
# Having established the above, from now on we mostly test against
|
||||
# files which do not exist in the working tree or index.
|
||||
|
||||
test_expect_success 'sub-directory local ignore' '
|
||||
expect "a/3-three" &&
|
||||
test_check_ignore "a/3-three a/three-not-this-one"
|
||||
'
|
||||
|
||||
test_expect_success 'sub-directory local ignore with --verbose' '
|
||||
expect "a/.gitignore:2:*three a/3-three" &&
|
||||
test_check_ignore "--verbose a/3-three a/three-not-this-one"
|
||||
'
|
||||
|
||||
test_expect_success 'local ignore inside a sub-directory' '
|
||||
expect "3-three" &&
|
||||
(
|
||||
cd a &&
|
||||
test_check_ignore "3-three three-not-this-one"
|
||||
)
|
||||
'
|
||||
test_expect_success 'local ignore inside a sub-directory with --verbose' '
|
||||
expect "a/.gitignore:2:*three 3-three" &&
|
||||
(
|
||||
cd a &&
|
||||
test_check_ignore "--verbose 3-three three-not-this-one"
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success_multi 'nested include' \
|
||||
'a/b/.gitignore:8:!on* a/b/one' '
|
||||
test_check_ignore "a/b/one"
|
||||
'
|
||||
|
||||
############################################################################
|
||||
#
|
||||
# test ignored sub-directories
|
||||
|
||||
test_expect_success_multi 'ignored sub-directory' \
|
||||
'a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir' '
|
||||
test_check_ignore "a/b/ignored-dir"
|
||||
'
|
||||
|
||||
test_expect_success 'multiple files inside ignored sub-directory' '
|
||||
expect_from_stdin <<-\EOF &&
|
||||
a/b/ignored-dir/foo
|
||||
a/b/ignored-dir/twoooo
|
||||
a/b/ignored-dir/seven
|
||||
EOF
|
||||
test_check_ignore "a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven"
|
||||
'
|
||||
|
||||
test_expect_success 'multiple files inside ignored sub-directory with -v' '
|
||||
expect_from_stdin <<-\EOF &&
|
||||
a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/foo
|
||||
a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/twoooo
|
||||
a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/seven
|
||||
EOF
|
||||
test_check_ignore "-v a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven"
|
||||
'
|
||||
|
||||
test_expect_success 'cd to ignored sub-directory' '
|
||||
expect_from_stdin <<-\EOF &&
|
||||
foo
|
||||
twoooo
|
||||
../one
|
||||
seven
|
||||
../../one
|
||||
EOF
|
||||
(
|
||||
cd a/b/ignored-dir &&
|
||||
test_check_ignore "foo twoooo ../one seven ../../one"
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'cd to ignored sub-directory with -v' '
|
||||
expect_from_stdin <<-\EOF &&
|
||||
a/b/.gitignore:5:ignored-dir/ foo
|
||||
a/b/.gitignore:5:ignored-dir/ twoooo
|
||||
a/b/.gitignore:8:!on* ../one
|
||||
a/b/.gitignore:5:ignored-dir/ seven
|
||||
.gitignore:1:one ../../one
|
||||
EOF
|
||||
(
|
||||
cd a/b/ignored-dir &&
|
||||
test_check_ignore "-v foo twoooo ../one seven ../../one"
|
||||
)
|
||||
'
|
||||
|
||||
############################################################################
|
||||
#
|
||||
# test handling of symlinks
|
||||
|
||||
test_expect_success_multi SYMLINKS 'symlink' '' '
|
||||
test_check_ignore "a/symlink" 1
|
||||
'
|
||||
|
||||
test_expect_success_multi SYMLINKS 'beyond a symlink' '' '
|
||||
test_check_ignore "a/symlink/foo" 128 &&
|
||||
test_stderr "fatal: '\''a/symlink/foo'\'' is beyond a symbolic link"
|
||||
'
|
||||
|
||||
test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
|
||||
(
|
||||
cd a &&
|
||||
test_check_ignore "symlink/foo" 128
|
||||
) &&
|
||||
test_stderr "fatal: '\''symlink/foo'\'' is beyond a symbolic link"
|
||||
'
|
||||
|
||||
############################################################################
|
||||
#
|
||||
# test handling of submodules
|
||||
|
||||
test_expect_success_multi 'submodule' '' '
|
||||
test_check_ignore "a/submodule/one" 128 &&
|
||||
test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
|
||||
'
|
||||
|
||||
test_expect_success_multi 'submodule from subdirectory' '' '
|
||||
(
|
||||
cd a &&
|
||||
test_check_ignore "submodule/one" 128
|
||||
) &&
|
||||
test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
|
||||
'
|
||||
|
||||
############################################################################
|
||||
#
|
||||
# test handling of global ignore files
|
||||
|
||||
test_expect_success 'global ignore not yet enabled' '
|
||||
expect_from_stdin <<-\EOF &&
|
||||
.git/info/exclude:7:per-repo per-repo
|
||||
a/.gitignore:2:*three a/globalthree
|
||||
.git/info/exclude:7:per-repo a/per-repo
|
||||
EOF
|
||||
test_check_ignore "-v globalone per-repo a/globalthree a/per-repo not-ignored a/globaltwo"
|
||||
'
|
||||
|
||||
test_expect_success 'global ignore' '
|
||||
enable_global_excludes &&
|
||||
expect_from_stdin <<-\EOF &&
|
||||
globalone
|
||||
per-repo
|
||||
globalthree
|
||||
a/globalthree
|
||||
a/per-repo
|
||||
globaltwo
|
||||
EOF
|
||||
test_check_ignore "globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
|
||||
'
|
||||
|
||||
test_expect_success 'global ignore with -v' '
|
||||
enable_global_excludes &&
|
||||
expect_from_stdin <<-EOF &&
|
||||
$global_excludes:1:globalone globalone
|
||||
.git/info/exclude:7:per-repo per-repo
|
||||
$global_excludes:3:globalthree globalthree
|
||||
a/.gitignore:2:*three a/globalthree
|
||||
.git/info/exclude:7:per-repo a/per-repo
|
||||
$global_excludes:2:!globaltwo globaltwo
|
||||
EOF
|
||||
test_check_ignore "-v globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
|
||||
'
|
||||
|
||||
############################################################################
|
||||
#
|
||||
# test --stdin
|
||||
|
||||
cat <<-\EOF >stdin
|
||||
one
|
||||
not-ignored
|
||||
a/one
|
||||
a/not-ignored
|
||||
a/b/on
|
||||
a/b/one
|
||||
a/b/one one
|
||||
"a/b/one two"
|
||||
"a/b/one\"three"
|
||||
a/b/not-ignored
|
||||
a/b/two
|
||||
a/b/twooo
|
||||
globaltwo
|
||||
a/globaltwo
|
||||
a/b/globaltwo
|
||||
b/globaltwo
|
||||
EOF
|
||||
cat <<-\EOF >expected-default
|
||||
one
|
||||
a/one
|
||||
a/b/on
|
||||
a/b/one
|
||||
a/b/one one
|
||||
a/b/one two
|
||||
"a/b/one\"three"
|
||||
a/b/two
|
||||
a/b/twooo
|
||||
globaltwo
|
||||
a/globaltwo
|
||||
a/b/globaltwo
|
||||
b/globaltwo
|
||||
EOF
|
||||
cat <<-EOF >expected-verbose
|
||||
.gitignore:1:one one
|
||||
.gitignore:1:one a/one
|
||||
a/b/.gitignore:8:!on* a/b/on
|
||||
a/b/.gitignore:8:!on* a/b/one
|
||||
a/b/.gitignore:8:!on* a/b/one one
|
||||
a/b/.gitignore:8:!on* a/b/one two
|
||||
a/b/.gitignore:8:!on* "a/b/one\"three"
|
||||
a/b/.gitignore:9:!two a/b/two
|
||||
a/.gitignore:1:two* a/b/twooo
|
||||
$global_excludes:2:!globaltwo globaltwo
|
||||
$global_excludes:2:!globaltwo a/globaltwo
|
||||
$global_excludes:2:!globaltwo a/b/globaltwo
|
||||
$global_excludes:2:!globaltwo b/globaltwo
|
||||
EOF
|
||||
|
||||
sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
|
||||
tr "\n" "\0" >stdin0
|
||||
sed -e 's/^"//' -e 's/\\//' -e 's/"$//' expected-default | \
|
||||
tr "\n" "\0" >expected-default0
|
||||
sed -e 's/ "/ /' -e 's/\\//' -e 's/"$//' expected-verbose | \
|
||||
tr ":\t\n" "\0" >expected-verbose0
|
||||
|
||||
test_expect_success '--stdin' '
|
||||
expect_from_stdin <expected-default &&
|
||||
test_check_ignore "--stdin" <stdin
|
||||
'
|
||||
|
||||
test_expect_success '--stdin -q' '
|
||||
expect "" &&
|
||||
test_check_ignore "-q --stdin" <stdin
|
||||
'
|
||||
|
||||
test_expect_success '--stdin -v' '
|
||||
expect_from_stdin <expected-verbose &&
|
||||
test_check_ignore "-v --stdin" <stdin
|
||||
'
|
||||
|
||||
for opts in '--stdin -z' '-z --stdin'
|
||||
do
|
||||
test_expect_success "$opts" "
|
||||
expect_from_stdin <expected-default0 &&
|
||||
test_check_ignore '$opts' <stdin0
|
||||
"
|
||||
|
||||
test_expect_success "$opts -q" "
|
||||
expect "" &&
|
||||
test_check_ignore '-q $opts' <stdin0
|
||||
"
|
||||
|
||||
test_expect_success "$opts -v" "
|
||||
expect_from_stdin <expected-verbose0 &&
|
||||
test_check_ignore '-v $opts' <stdin0
|
||||
"
|
||||
done
|
||||
|
||||
cat <<-\EOF >stdin
|
||||
../one
|
||||
../not-ignored
|
||||
one
|
||||
not-ignored
|
||||
b/on
|
||||
b/one
|
||||
b/one one
|
||||
"b/one two"
|
||||
"b/one\"three"
|
||||
b/two
|
||||
b/not-ignored
|
||||
b/twooo
|
||||
../globaltwo
|
||||
globaltwo
|
||||
b/globaltwo
|
||||
../b/globaltwo
|
||||
EOF
|
||||
cat <<-\EOF >expected-default
|
||||
../one
|
||||
one
|
||||
b/on
|
||||
b/one
|
||||
b/one one
|
||||
b/one two
|
||||
"b/one\"three"
|
||||
b/two
|
||||
b/twooo
|
||||
../globaltwo
|
||||
globaltwo
|
||||
b/globaltwo
|
||||
../b/globaltwo
|
||||
EOF
|
||||
cat <<-EOF >expected-verbose
|
||||
.gitignore:1:one ../one
|
||||
.gitignore:1:one one
|
||||
a/b/.gitignore:8:!on* b/on
|
||||
a/b/.gitignore:8:!on* b/one
|
||||
a/b/.gitignore:8:!on* b/one one
|
||||
a/b/.gitignore:8:!on* b/one two
|
||||
a/b/.gitignore:8:!on* "b/one\"three"
|
||||
a/b/.gitignore:9:!two b/two
|
||||
a/.gitignore:1:two* b/twooo
|
||||
$global_excludes:2:!globaltwo ../globaltwo
|
||||
$global_excludes:2:!globaltwo globaltwo
|
||||
$global_excludes:2:!globaltwo b/globaltwo
|
||||
$global_excludes:2:!globaltwo ../b/globaltwo
|
||||
EOF
|
||||
|
||||
sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
|
||||
tr "\n" "\0" >stdin0
|
||||
sed -e 's/^"//' -e 's/\\//' -e 's/"$//' expected-default | \
|
||||
tr "\n" "\0" >expected-default0
|
||||
sed -e 's/ "/ /' -e 's/\\//' -e 's/"$//' expected-verbose | \
|
||||
tr ":\t\n" "\0" >expected-verbose0
|
||||
|
||||
test_expect_success '--stdin from subdirectory' '
|
||||
expect_from_stdin <expected-default &&
|
||||
(
|
||||
cd a &&
|
||||
test_check_ignore "--stdin" <../stdin
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success '--stdin from subdirectory with -v' '
|
||||
expect_from_stdin <expected-verbose &&
|
||||
(
|
||||
cd a &&
|
||||
test_check_ignore "--stdin -v" <../stdin
|
||||
)
|
||||
'
|
||||
|
||||
for opts in '--stdin -z' '-z --stdin'
|
||||
do
|
||||
test_expect_success "$opts from subdirectory" '
|
||||
expect_from_stdin <expected-default0 &&
|
||||
(
|
||||
cd a &&
|
||||
test_check_ignore "'"$opts"'" <../stdin0
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success "$opts from subdirectory with -v" '
|
||||
expect_from_stdin <expected-verbose0 &&
|
||||
(
|
||||
cd a &&
|
||||
test_check_ignore "'"$opts"' -v" <../stdin0
|
||||
)
|
||||
'
|
||||
done
|
||||
|
||||
|
||||
test_done
|
@ -1020,7 +1020,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||
if (!core_apply_sparse_checkout || !o->update)
|
||||
o->skip_sparse_checkout = 1;
|
||||
if (!o->skip_sparse_checkout) {
|
||||
if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, NULL, &el, 0) < 0)
|
||||
if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, &el, 0) < 0)
|
||||
o->skip_sparse_checkout = 1;
|
||||
else
|
||||
o->el = ⪙
|
||||
|
Loading…
Reference in New Issue
Block a user