Skip to content

Commit

Permalink
Merge branch 'as/check-ignore'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Jan 24, 2013
2 parents f12e49a + 72aeb18 commit a39b15b
Show file tree
Hide file tree
Showing 20 changed files with 1,251 additions and 119 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
/git-bundle
/git-cat-file
/git-check-attr
/git-check-ignore
/git-check-ref-format
/git-checkout
/git-checkout-index
Expand Down
89 changes: 89 additions & 0 deletions Documentation/git-check-ignore.txt
Original file line number Diff line number Diff line change
@@ -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
6 changes: 4 additions & 2 deletions Documentation/gitignore.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
---
Expand Down
14 changes: 9 additions & 5 deletions Documentation/technical/api-directory-listing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,20 @@ 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.

* Call `read_directory()`.

* Use `dir.entries[]`.

* Call `clear_directory()` when none of the contained elements are no longer in use.

(JC)
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
78 changes: 18 additions & 60 deletions builtin/add.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand All @@ -149,35 +117,23 @@ 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;

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)
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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])) {
Expand Down
Loading

0 comments on commit a39b15b

Please sign in to comment.