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:
@ -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 ";
|
||||
|
Reference in New Issue
Block a user