Commit e2934db2 by Vicent Martí

Merge pull request #1090 from arrbee/ignore-invalid-by-default

Ignore invalid entries by default
parents ee06fec5 d46b0a04
...@@ -41,9 +41,10 @@ GIT_EXTERN(int) git_ignore_add_rule( ...@@ -41,9 +41,10 @@ GIT_EXTERN(int) git_ignore_add_rule(
/** /**
* Clear ignore rules that were explicitly added. * Clear ignore rules that were explicitly added.
* *
* Clears the internal ignore rules that have been set up. This will not * Resets to the default internal ignore rules. This will not turn off
* turn off the rules in .gitignore files that actually exist in the * rules in .gitignore files that actually exist in the filesystem.
* filesystem. *
* The default internal ignores ignore ".", ".." and ".git" entries.
* *
* @param repo The repository to remove ignore rules from. * @param repo The repository to remove ignore rules from.
* @return 0 on success * @return 0 on success
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#define GIT_IGNORE_FILE_INREPO "info/exclude" #define GIT_IGNORE_FILE_INREPO "info/exclude"
#define GIT_IGNORE_FILE ".gitignore" #define GIT_IGNORE_FILE ".gitignore"
#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
static int parse_ignore_file( static int parse_ignore_file(
git_repository *repo, void *parsedata, const char *buffer, git_attr_file *ignores) git_repository *repo, void *parsedata, const char *buffer, git_attr_file *ignores)
{ {
...@@ -88,6 +90,19 @@ static int push_one_ignore(void *ref, git_buf *path) ...@@ -88,6 +90,19 @@ static int push_one_ignore(void *ref, git_buf *path)
return push_ignore_file(ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); return push_ignore_file(ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
} }
static int get_internal_ignores(git_attr_file **ign, git_repository *repo)
{
int error;
if (!(error = git_attr_cache__init(repo)))
error = git_attr_cache__internal_file(repo, GIT_IGNORE_INTERNAL, ign);
if (!error && !(*ign)->rules.length)
error = parse_ignore_file(repo, NULL, GIT_IGNORE_DEFAULT_RULES, *ign);
return error;
}
int git_ignore__for_path( int git_ignore__for_path(
git_repository *repo, git_repository *repo,
const char *path, const char *path,
...@@ -129,8 +144,7 @@ int git_ignore__for_path( ...@@ -129,8 +144,7 @@ int git_ignore__for_path(
goto cleanup; goto cleanup;
/* set up internals */ /* set up internals */
error = git_attr_cache__internal_file( error = get_internal_ignores(&ignores->ign_internal, repo);
repo, GIT_IGNORE_INTERNAL, &ignores->ign_internal);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
...@@ -239,16 +253,6 @@ cleanup: ...@@ -239,16 +253,6 @@ cleanup:
return 0; return 0;
} }
static int get_internal_ignores(git_attr_file **ign, git_repository *repo)
{
int error;
if (!(error = git_attr_cache__init(repo)))
error = git_attr_cache__internal_file(repo, GIT_IGNORE_INTERNAL, ign);
return error;
}
int git_ignore_add_rule( int git_ignore_add_rule(
git_repository *repo, git_repository *repo,
const char *rules) const char *rules)
...@@ -268,9 +272,13 @@ int git_ignore_clear_internal_rules( ...@@ -268,9 +272,13 @@ int git_ignore_clear_internal_rules(
int error; int error;
git_attr_file *ign_internal; git_attr_file *ign_internal;
if (!(error = get_internal_ignores(&ign_internal, repo))) if (!(error = get_internal_ignores(&ign_internal, repo))) {
git_attr_file__clear_rules(ign_internal); git_attr_file__clear_rules(ign_internal);
return parse_ignore_file(
repo, NULL, GIT_IGNORE_DEFAULT_RULES, ign_internal);
}
return error; return error;
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "ignore.h" #include "ignore.h"
#include "buffer.h" #include "buffer.h"
#include "git2/submodule.h" #include "git2/submodule.h"
#include <ctype.h>
#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \ #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \
(P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \ (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
...@@ -465,6 +466,23 @@ static int git_path_with_stat_cmp_icase(const void *a, const void *b) ...@@ -465,6 +466,23 @@ static int git_path_with_stat_cmp_icase(const void *a, const void *b)
return strcasecmp(path_with_stat_a->path, path_with_stat_b->path); return strcasecmp(path_with_stat_a->path, path_with_stat_b->path);
} }
GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
{
if (!ps)
return false;
else {
const char *path = ps->path;
size_t len = ps->path_len;
return len >= 4 &&
tolower(path[len - 1]) == 't' &&
tolower(path[len - 2]) == 'i' &&
tolower(path[len - 3]) == 'g' &&
path[len - 4] == '.' &&
(len == 4 || path[len - 5] == '/');
}
}
static workdir_iterator_frame *workdir_iterator__alloc_frame(workdir_iterator *wi) static workdir_iterator_frame *workdir_iterator__alloc_frame(workdir_iterator *wi)
{ {
workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame)); workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
...@@ -531,6 +549,9 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi) ...@@ -531,6 +549,9 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
CASESELECT(wi->base.ignore_case, workdir_iterator__entry_cmp_icase, workdir_iterator__entry_cmp_case), CASESELECT(wi->base.ignore_case, workdir_iterator__entry_cmp_icase, workdir_iterator__entry_cmp_case),
wf->start); wf->start);
if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
wf->index++;
wf->next = wi->stack; wf->next = wi->stack;
wi->stack = wf; wi->stack = wf;
...@@ -574,8 +595,7 @@ static int workdir_iterator__advance( ...@@ -574,8 +595,7 @@ static int workdir_iterator__advance(
next = git_vector_get(&wf->entries, ++wf->index); next = git_vector_get(&wf->entries, ++wf->index);
if (next != NULL) { if (next != NULL) {
/* match git's behavior of ignoring anything named ".git" */ /* match git's behavior of ignoring anything named ".git" */
if (STRCMP_CASESELECT(wi->base.ignore_case, next->path, DOT_GIT "/") == 0 || if (path_is_dotgit(next))
STRCMP_CASESELECT(wi->base.ignore_case, next->path, DOT_GIT) == 0)
continue; continue;
/* else found a good entry */ /* else found a good entry */
break; break;
...@@ -658,8 +678,7 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) ...@@ -658,8 +678,7 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
wi->entry.path = ps->path; wi->entry.path = ps->path;
/* skip over .git entries */ /* skip over .git entries */
if (STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT "/") == 0 || if (path_is_dotgit(ps))
STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT) == 0)
return workdir_iterator__advance((git_iterator *)wi, NULL); return workdir_iterator__advance((git_iterator *)wi, NULL);
wi->is_ignored = -1; wi->is_ignored = -1;
......
...@@ -26,9 +26,12 @@ static bool valid_filemode(const int filemode) ...@@ -26,9 +26,12 @@ static bool valid_filemode(const int filemode)
static int valid_entry_name(const char *filename) static int valid_entry_name(const char *filename)
{ {
return *filename != '\0' && strchr(filename, '/') == NULL && return *filename != '\0' &&
strcmp(filename, "..") != 0 && strcmp(filename, ".") != 0 && strchr(filename, '/') == NULL &&
strcmp(filename, ".git") != 0; (*filename != '.' ||
(strcmp(filename, ".") != 0 &&
strcmp(filename, "..") != 0 &&
strcmp(filename, DOT_GIT) != 0));
} }
static int entry_sort_cmp(const void *a, const void *b) static int entry_sort_cmp(const void *a, const void *b)
...@@ -299,9 +302,12 @@ size_t git_tree_entrycount(const git_tree *tree) ...@@ -299,9 +302,12 @@ size_t git_tree_entrycount(const git_tree *tree)
return tree->entries.length; return tree->entries.length;
} }
static int tree_error(const char *str) static int tree_error(const char *str, const char *path)
{ {
giterr_set(GITERR_TREE, "%s", str); if (path)
giterr_set(GITERR_TREE, "%s - %s", str, path);
else
giterr_set(GITERR_TREE, "%s", str);
return -1; return -1;
} }
...@@ -316,13 +322,13 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf ...@@ -316,13 +322,13 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || if (git__strtol32(&attr, buffer, &buffer, 8) < 0 ||
!buffer || !valid_filemode(attr)) !buffer || !valid_filemode(attr))
return tree_error("Failed to parse tree. Can't parse filemode"); return tree_error("Failed to parse tree. Can't parse filemode", NULL);
if (*buffer++ != ' ') if (*buffer++ != ' ')
return tree_error("Failed to parse tree. Object is corrupted"); return tree_error("Failed to parse tree. Object is corrupted", NULL);
if (memchr(buffer, 0, buffer_end - buffer) == NULL) if (memchr(buffer, 0, buffer_end - buffer) == NULL)
return tree_error("Failed to parse tree. Object is corrupted"); return tree_error("Failed to parse tree. Object is corrupted", NULL);
/** Allocate the entry and store it in the entries vector */ /** Allocate the entry and store it in the entries vector */
{ {
...@@ -379,7 +385,7 @@ static int append_entry( ...@@ -379,7 +385,7 @@ static int append_entry(
git_tree_entry *entry; git_tree_entry *entry;
if (!valid_entry_name(filename)) if (!valid_entry_name(filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry"); return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
entry = alloc_entry(filename); entry = alloc_entry(filename);
GITERR_CHECK_ALLOC(entry); GITERR_CHECK_ALLOC(entry);
...@@ -453,7 +459,8 @@ static int write_tree( ...@@ -453,7 +459,8 @@ static int write_tree(
/* Write out the subtree */ /* Write out the subtree */
written = write_tree(&sub_oid, repo, index, subdir, i); written = write_tree(&sub_oid, repo, index, subdir, i);
if (written < 0) { if (written < 0) {
tree_error("Failed to write subtree"); tree_error("Failed to write subtree", subdir);
git__free(subdir);
goto on_error; goto on_error;
} else { } else {
i = written - 1; /* -1 because of the loop increment */ i = written - 1; /* -1 because of the loop increment */
...@@ -471,18 +478,15 @@ static int write_tree( ...@@ -471,18 +478,15 @@ static int write_tree(
} else { } else {
last_comp = subdir; last_comp = subdir;
} }
error = append_entry(bld, last_comp, &sub_oid, S_IFDIR); error = append_entry(bld, last_comp, &sub_oid, S_IFDIR);
git__free(subdir); git__free(subdir);
if (error < 0) { if (error < 0)
tree_error("Failed to insert dir");
goto on_error; goto on_error;
}
} else { } else {
error = append_entry(bld, filename, &entry->oid, entry->mode); error = append_entry(bld, filename, &entry->oid, entry->mode);
if (error < 0) { if (error < 0)
tree_error("Failed to insert file");
goto on_error; goto on_error;
}
} }
} }
...@@ -587,12 +591,12 @@ int git_treebuilder_insert( ...@@ -587,12 +591,12 @@ int git_treebuilder_insert(
assert(bld && id && filename); assert(bld && id && filename);
if (!valid_filemode(filemode)) if (!valid_filemode(filemode))
return tree_error("Failed to insert entry. Invalid filemode"); return tree_error("Failed to insert entry. Invalid filemode for file", filename);
filemode = normalize_filemode(filemode); filemode = normalize_filemode(filemode);
if (!valid_entry_name(filename)) if (!valid_entry_name(filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry"); return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
pos = tree_key_search(&bld->entries, filename, strlen(filename)); pos = tree_key_search(&bld->entries, filename, strlen(filename));
...@@ -648,7 +652,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename) ...@@ -648,7 +652,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
git_tree_entry *remove_ptr = treebuilder_get(bld, filename); git_tree_entry *remove_ptr = treebuilder_get(bld, filename);
if (remove_ptr == NULL || remove_ptr->removed) if (remove_ptr == NULL || remove_ptr->removed)
return tree_error("Failed to remove entry. File isn't in the tree"); return tree_error("Failed to remove entry. File isn't in the tree", filename);
remove_ptr->removed = 1; remove_ptr->removed = 1;
return 0; return 0;
......
...@@ -668,3 +668,59 @@ void test_diff_iterator__workdir_1_ranged_empty_2(void) ...@@ -668,3 +668,59 @@ void test_diff_iterator__workdir_1_ranged_empty_2(void)
"status", NULL, "aaaa_empty_before", "status", NULL, "aaaa_empty_before",
0, 0, NULL, NULL); 0, 0, NULL, NULL);
} }
void test_diff_iterator__workdir_builtin_ignores(void)
{
git_repository *repo = cl_git_sandbox_init("attr");
git_iterator *i;
const git_index_entry *entry;
int idx;
static struct {
const char *path;
bool ignored;
} expected[] = {
{ "dir/", true },
{ "file", false },
{ "ign", true },
{ "macro_bad", false },
{ "macro_test", false },
{ "root_test1", false },
{ "root_test2", false },
{ "root_test3", false },
{ "root_test4.txt", false },
{ "sub/", false },
{ "sub/.gitattributes", false },
{ "sub/abc", false },
{ "sub/dir/", true },
{ "sub/file", false },
{ "sub/ign/", true },
{ "sub/sub/", false },
{ "sub/sub/.gitattributes", false },
{ "sub/sub/dir", false }, /* file is not actually a dir */
{ "sub/sub/file", false },
{ NULL, false }
};
cl_git_pass(p_mkdir("attr/sub/sub/.git", 0777));
cl_git_mkfile("attr/sub/.git", "whatever");
cl_git_pass(
git_iterator_for_workdir_range(&i, repo, "dir", "sub/sub/file"));
cl_git_pass(git_iterator_current(i, &entry));
for (idx = 0; entry != NULL; ++idx) {
int ignored = git_iterator_current_is_ignored(i);
cl_assert_equal_s(expected[idx].path, entry->path);
cl_assert_(ignored == expected[idx].ignored, expected[idx].path);
if (!ignored && S_ISDIR(entry->mode))
cl_git_pass(git_iterator_advance_into_directory(i, &entry));
else
cl_git_pass(git_iterator_advance(i, &entry));
}
cl_assert(expected[idx].path == NULL);
git_iterator_free(i);
}
...@@ -341,3 +341,41 @@ void test_status_ignore__internal_ignores_inside_deep_paths(void) ...@@ -341,3 +341,41 @@ void test_status_ignore__internal_ignores_inside_deep_paths(void)
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "xthis/is/deep")); cl_git_pass(git_status_should_ignore(&ignored, g_repo, "xthis/is/deep"));
cl_assert(!ignored); cl_assert(!ignored);
} }
void test_status_ignore__automatically_ignore_bad_files(void)
{
int ignored;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
cl_assert(!ignored);
cl_git_pass(git_ignore_add_rule(g_repo, "*.c\n"));
cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
cl_assert(ignored);
cl_git_pass(git_ignore_clear_internal_rules(g_repo));
cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
cl_assert(ignored);
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
cl_assert(!ignored);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment