Fix 'git add' with .gitignore

When '*.ig' is ignored, and you have two files f.ig and d.ig/foo
in the working tree,

	$ git add .

correctly ignored f.ig but failed to ignore d.ig/foo.  This was
caused by a thinko in an earlier commit 4888c534, when we tried
to allow adding otherwise ignored files.

After reverting that commit, this takes a much simpler approach.
When we have an unmatched pathspec that talks about an existing
pathname, we know it is an ignored path the user tried to add,
so we include it in the set of paths directory walker returned.

This does not let you say "git add -f D" on an ignored directory
D and add everything under D.  People can submit a patch to
further allow it if they want to, but I think it is a saner
behaviour to require explicit paths to be spelled out in such a
case.

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano
2006-12-29 11:01:31 -08:00
parent c889763bf3
commit 4d06f8ac43
4 changed files with 69 additions and 29 deletions

View File

@ -26,18 +26,9 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
i = dir->nr;
while (--i >= 0) {
struct dir_entry *entry = *src++;
int how = match_pathspec(pathspec, entry->name, entry->len,
prefix, seen);
/*
* ignored entries can be added with exact match,
* but not with glob nor recursive.
*/
if (!how ||
(entry->ignored_entry && how != MATCHED_EXACTLY)) {
free(entry);
continue;
}
*dst++ = entry;
if (match_pathspec(pathspec, entry->name, entry->len,
prefix, seen))
*dst++ = entry;
}
dir->nr = dst - dir->entries;
@ -47,10 +38,20 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
if (seen[i])
continue;
/* Existing file? We must have ignored it */
match = pathspec[i];
if (!match[0] || !lstat(match, &st))
if (!match[0])
continue;
/* Existing file? We must have ignored it */
if (!lstat(match, &st)) {
struct dir_entry *ent;
ent = dir_add_name(dir, match, strlen(match));
ent->ignored = 1;
if (S_ISDIR(st.st_mode))
ent->ignored_dir = 1;
continue;
}
die("pathspec '%s' did not match any files", match);
}
}
@ -62,8 +63,6 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
/* Set up the default git porcelain excludes */
memset(dir, 0, sizeof(*dir));
if (pathspec)
dir->show_both = 1;
dir->exclude_per_dir = ".gitignore";
path = git_path("info/exclude");
if (!access(path, R_OK))
@ -154,7 +153,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (show_only) {
const char *sep = "", *eof = "";
for (i = 0; i < dir.nr; i++) {
if (!ignored_too && dir.entries[i]->ignored_entry)
if (!ignored_too && dir.entries[i]->ignored)
continue;
printf("%s%s", sep, dir.entries[i]->name);
sep = " ";
@ -168,16 +167,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
die("index file corrupt");
if (!ignored_too) {
int has_ignored = -1;
for (i = 0; has_ignored < 0 && i < dir.nr; i++)
if (dir.entries[i]->ignored_entry)
has_ignored = i;
if (0 <= has_ignored) {
int has_ignored = 0;
for (i = 0; i < dir.nr; i++)
if (dir.entries[i]->ignored)
has_ignored = 1;
if (has_ignored) {
fprintf(stderr, ignore_warning);
for (i = has_ignored; i < dir.nr; i++) {
if (!dir.entries[i]->ignored_entry)
for (i = 0; i < dir.nr; i++) {
if (!dir.entries[i]->ignored)
continue;
fprintf(stderr, "%s\n", dir.entries[i]->name);
fprintf(stderr, "%s", dir.entries[i]->name);
if (dir.entries[i]->ignored_dir)
fprintf(stderr, " (directory)");
fputc('\n', stderr);
}
fprintf(stderr,
"Use -f if you really want to add them.\n");