c19387e799
Usually we do not pass an empty string to the function hash_name() because we almost always ask for hash values for a path that is a candidate to be added to the index. However, check-ignore (and most likely check-attr, but I didn't check) apparently has a callchain to ask the hash value for an empty path when it was given a "." from the top-level directory to ask "Is the path . excluded by default?" Make sure that hash_name() does not overrun the end of the given pathname even when it is empty. Remove a sweep-the-issue-under-the-rug conditional in check-ignore that avoided to pass an empty string to the callchain while at it. It is a valid question to ask for check-ignore if the top-level is set to be ignored by default, even though the answer is most likely no, if only because there is currently no way to specify such an entry in the .gitignore file. But it is an unusual thing to ask and it is not worth optimizing for it by special casing at the top level of the call chain. Signed-off-by: Adam Spiers <git@adamspiers.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
174 lines
4.5 KiB
C
174 lines
4.5 KiB
C
#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]) {
|
|
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;
|
|
}
|