mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-06 11:19:47 +00:00
Merge pull request #2279 from libgit2/rb/moar-eegnöre-fîxés
Fix several ignore and attribute file behavior bugs
This commit is contained in:
commit
7b0f8ba9a8
74
src/attr.c
74
src/attr.c
@ -217,6 +217,74 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int preload_attr_file(
|
||||
git_repository *repo,
|
||||
git_attr_file_source source,
|
||||
const char *base,
|
||||
const char *file)
|
||||
{
|
||||
int error;
|
||||
git_attr_file *preload = NULL;
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
if (!(error = git_attr_cache__get(
|
||||
&preload, repo, source, base, file, git_attr_file__parse_buffer)))
|
||||
git_attr_file__free(preload);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int attr_setup(git_repository *repo)
|
||||
{
|
||||
int error = 0;
|
||||
const char *workdir = git_repository_workdir(repo);
|
||||
git_index *idx = NULL;
|
||||
git_buf sys = GIT_BUF_INIT;
|
||||
|
||||
if ((error = git_attr_cache__init(repo)) < 0)
|
||||
return error;
|
||||
|
||||
/* preload attribute files that could contain macros so the
|
||||
* definitions will be available for later file parsing
|
||||
*/
|
||||
|
||||
if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) {
|
||||
error = preload_attr_file(
|
||||
repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr);
|
||||
git_buf_free(&sys);
|
||||
}
|
||||
if (error < 0) {
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
} else
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = preload_attr_file(
|
||||
repo, GIT_ATTR_FILE__FROM_FILE,
|
||||
NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = preload_attr_file(
|
||||
repo, GIT_ATTR_FILE__FROM_FILE,
|
||||
git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0)
|
||||
return error;
|
||||
|
||||
if (workdir != NULL &&
|
||||
(error = preload_attr_file(
|
||||
repo, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
|
||||
(error = preload_attr_file(
|
||||
repo, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0)
|
||||
return error;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_attr_add_macro(
|
||||
git_repository *repo,
|
||||
const char *name,
|
||||
@ -226,8 +294,8 @@ int git_attr_add_macro(
|
||||
git_attr_rule *macro = NULL;
|
||||
git_pool *pool;
|
||||
|
||||
if (git_attr_cache__init(repo) < 0)
|
||||
return -1;
|
||||
if ((error = git_attr_cache__init(repo)) < 0)
|
||||
return error;
|
||||
|
||||
macro = git__calloc(1, sizeof(git_attr_rule));
|
||||
GITERR_CHECK_ALLOC(macro);
|
||||
@ -348,7 +416,7 @@ static int collect_attr_files(
|
||||
const char *workdir = git_repository_workdir(repo);
|
||||
attr_walk_up_info info = { NULL };
|
||||
|
||||
if ((error = git_attr_cache__init(repo)) < 0)
|
||||
if ((error = attr_setup(repo)) < 0)
|
||||
return error;
|
||||
|
||||
/* Resolve path in a non-bare repo */
|
||||
|
@ -248,9 +248,7 @@ int git_attr_file__parse_buffer(
|
||||
repo, &attrs->pool, &rule->assigns, &scan)))
|
||||
{
|
||||
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
|
||||
/* should generate error/warning if this is coming from any
|
||||
* file other than .gitattributes at repo root.
|
||||
*/
|
||||
/* TODO: warning if macro found in file below repo root */
|
||||
error = git_attr_cache__insert_macro(repo, rule);
|
||||
else
|
||||
error = git_vector_insert(&attrs->rules, rule);
|
||||
@ -355,6 +353,8 @@ bool git_attr_fnmatch__match(
|
||||
|
||||
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
|
||||
flags |= FNM_CASEFOLD;
|
||||
if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
|
||||
flags |= FNM_LEADING_DIR;
|
||||
|
||||
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
|
||||
filename = path->path;
|
||||
@ -545,6 +545,14 @@ int git_attr_fnmatch__parse(
|
||||
if (--slash_count <= 0)
|
||||
spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
|
||||
}
|
||||
if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0 &&
|
||||
spec->length >= 2 &&
|
||||
pattern[spec->length - 1] == '*' &&
|
||||
pattern[spec->length - 2] == '/') {
|
||||
spec->length -= 2;
|
||||
spec->flags = spec->flags | GIT_ATTR_FNMATCH_LEADINGDIR;
|
||||
/* leave FULLPATH match on, however */
|
||||
}
|
||||
|
||||
if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
|
||||
context != NULL && git_path_root(pattern) < 0)
|
||||
|
@ -30,10 +30,12 @@
|
||||
#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
|
||||
#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
|
||||
#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
|
||||
#define GIT_ATTR_FNMATCH_LEADINGDIR (1U << 11)
|
||||
#define GIT_ATTR_FNMATCH_NOLEADINGDIR (1U << 12)
|
||||
|
||||
#define GIT_ATTR_FNMATCH__INCOMING \
|
||||
(GIT_ATTR_FNMATCH_ALLOWSPACE | \
|
||||
GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
|
||||
(GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | \
|
||||
GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR)
|
||||
|
||||
typedef enum {
|
||||
GIT_ATTR_FILE__IN_MEMORY = 0,
|
||||
|
13
src/ignore.c
13
src/ignore.c
@ -123,7 +123,7 @@ int git_ignore__for_path(
|
||||
int error = 0;
|
||||
const char *workdir = git_repository_workdir(repo);
|
||||
|
||||
assert(ignores);
|
||||
assert(ignores && path);
|
||||
|
||||
memset(ignores, 0, sizeof(*ignores));
|
||||
ignores->repo = repo;
|
||||
@ -140,10 +140,13 @@ int git_ignore__for_path(
|
||||
if (workdir && git_path_root(path) < 0)
|
||||
error = git_path_find_dir(&ignores->dir, path, workdir);
|
||||
else
|
||||
error = git_buf_sets(&ignores->dir, path);
|
||||
error = git_buf_joinpath(&ignores->dir, path, "");
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (workdir && !git__prefixcmp(ignores->dir.ptr, workdir))
|
||||
ignores->dir_root = strlen(workdir);
|
||||
|
||||
/* set up internals */
|
||||
if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0)
|
||||
goto cleanup;
|
||||
@ -204,10 +207,10 @@ int git_ignore__pop_dir(git_ignores *ign)
|
||||
|
||||
if ((end = strrchr(start, '/')) != NULL) {
|
||||
size_t dirlen = (end - start) + 1;
|
||||
const char *relpath = ign->dir.ptr + ign->dir_root;
|
||||
size_t pathlen = ign->dir.size - ign->dir_root;
|
||||
|
||||
if (ign->dir.size >= dirlen &&
|
||||
!memcmp(ign->dir.ptr + ign->dir.size - dirlen, start, dirlen))
|
||||
{
|
||||
if (pathlen == dirlen && !memcmp(relpath, start, dirlen)) {
|
||||
git_vector_pop(&ign->ign_path);
|
||||
git_attr_file__free(file);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ typedef struct {
|
||||
git_attr_file *ign_internal;
|
||||
git_vector ign_path;
|
||||
git_vector ign_global;
|
||||
size_t dir_root; /* offset in dir to repo root */
|
||||
int ignore_case;
|
||||
int depth;
|
||||
} git_ignores;
|
||||
|
@ -624,7 +624,7 @@ int git_path_find_dir(git_buf *dir, const char *path, const char *base)
|
||||
|
||||
/* call dirname if this is not a directory */
|
||||
if (!error) /* && git_path_isdir(dir->ptr) == false) */
|
||||
error = git_path_dirname_r(dir, dir->ptr);
|
||||
error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0;
|
||||
|
||||
if (!error)
|
||||
error = git_path_to_dir(dir);
|
||||
|
@ -83,7 +83,8 @@ int git_pathspec__vinit(
|
||||
if (!match)
|
||||
return -1;
|
||||
|
||||
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
|
||||
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE |
|
||||
GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_NOLEADINGDIR;
|
||||
|
||||
ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
|
||||
if (ret == GIT_ENOTFOUND) {
|
||||
|
@ -16,7 +16,7 @@ void test_attr_ignore__cleanup(void)
|
||||
g_repo = NULL;
|
||||
}
|
||||
|
||||
void assert_is_ignored_(
|
||||
static void assert_is_ignored_(
|
||||
bool expected, const char *filepath, const char *file, int line)
|
||||
{
|
||||
int is_ignored = 0;
|
||||
|
@ -23,49 +23,74 @@ void test_attr_repo__cleanup(void)
|
||||
g_repo = NULL;
|
||||
}
|
||||
|
||||
static struct attr_expected get_one_test_cases[] = {
|
||||
{ "root_test1", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "root_test1", "rootattr", EXPECT_TRUE, NULL },
|
||||
{ "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test2", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "root_test2", "rootattr", EXPECT_FALSE, NULL },
|
||||
{ "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test2", "multiattr", EXPECT_FALSE, NULL },
|
||||
{ "root_test3", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test3", "multiattr", EXPECT_STRING, "3" },
|
||||
{ "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
|
||||
{ "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
|
||||
{ "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
|
||||
{ "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
|
||||
{ "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
|
||||
{ "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
|
||||
{ "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
|
||||
{ "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
|
||||
{ "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
|
||||
{ "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
|
||||
{ "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
|
||||
{ "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
|
||||
{ "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
|
||||
{ "does-not-exist", "foo", EXPECT_STRING, "yes" },
|
||||
{ "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
|
||||
{ "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
|
||||
{ "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
|
||||
};
|
||||
|
||||
void test_attr_repo__get_one(void)
|
||||
{
|
||||
struct attr_expected test_cases[] = {
|
||||
{ "root_test1", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "root_test1", "rootattr", EXPECT_TRUE, NULL },
|
||||
{ "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test2", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "root_test2", "rootattr", EXPECT_FALSE, NULL },
|
||||
{ "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test2", "multiattr", EXPECT_FALSE, NULL },
|
||||
{ "root_test3", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "root_test3", "multiattr", EXPECT_STRING, "3" },
|
||||
{ "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
|
||||
{ "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
|
||||
{ "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
|
||||
{ "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
|
||||
{ "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
|
||||
{ "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
|
||||
{ "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
|
||||
{ "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
|
||||
{ "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
|
||||
{ "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
|
||||
{ "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
|
||||
{ "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
|
||||
{ "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
|
||||
{ "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
|
||||
{ "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
|
||||
{ "does-not-exist", "foo", EXPECT_STRING, "yes" },
|
||||
{ "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
|
||||
{ "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
|
||||
{ "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
|
||||
{ NULL, NULL, 0, NULL }
|
||||
}, *scan;
|
||||
int i;
|
||||
|
||||
for (scan = test_cases; scan->path != NULL; scan++) {
|
||||
for (i = 0; i < (int)ARRAY_SIZE(get_one_test_cases); ++i) {
|
||||
struct attr_expected *scan = &get_one_test_cases[i];
|
||||
const char *value;
|
||||
|
||||
cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
|
||||
attr_check_expected(scan->expected, scan->expected_str, scan->attr, value);
|
||||
attr_check_expected(
|
||||
scan->expected, scan->expected_str, scan->attr, value);
|
||||
}
|
||||
|
||||
cl_assert(git_attr_cache__is_cached(
|
||||
g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
|
||||
cl_assert(git_attr_cache__is_cached(
|
||||
g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
|
||||
cl_assert(git_attr_cache__is_cached(
|
||||
g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
|
||||
}
|
||||
|
||||
void test_attr_repo__get_one_start_deep(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = (int)ARRAY_SIZE(get_one_test_cases) - 1; i >= 0; --i) {
|
||||
struct attr_expected *scan = &get_one_test_cases[i];
|
||||
const char *value;
|
||||
|
||||
cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
|
||||
attr_check_expected(
|
||||
scan->expected, scan->expected_str, scan->attr, value);
|
||||
}
|
||||
|
||||
cl_assert(git_attr_cache__is_cached(
|
||||
|
@ -16,6 +16,23 @@ void test_status_ignore__cleanup(void)
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
static void assert_ignored_(
|
||||
bool expected, const char *filepath, const char *file, int line)
|
||||
{
|
||||
int is_ignored = 0;
|
||||
cl_git_pass_(
|
||||
git_status_should_ignore(&is_ignored, g_repo, filepath), file, line);
|
||||
clar__assert(
|
||||
(expected != 0) == (is_ignored != 0),
|
||||
file, line, "expected != is_ignored", filepath, 1);
|
||||
}
|
||||
#define assert_ignored(expected, filepath) \
|
||||
assert_ignored_(expected, filepath, __FILE__, __LINE__)
|
||||
#define assert_is_ignored(filepath) \
|
||||
assert_ignored_(true, filepath, __FILE__, __LINE__)
|
||||
#define refute_is_ignored(filepath) \
|
||||
assert_ignored_(false, filepath, __FILE__, __LINE__)
|
||||
|
||||
void test_status_ignore__0(void)
|
||||
{
|
||||
struct {
|
||||
@ -47,11 +64,8 @@ void test_status_ignore__0(void)
|
||||
|
||||
g_repo = cl_git_sandbox_init("attr");
|
||||
|
||||
for (one_test = test_cases; one_test->path != NULL; one_test++) {
|
||||
int ignored;
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, one_test->path));
|
||||
cl_assert_(ignored == one_test->expected, one_test->path);
|
||||
}
|
||||
for (one_test = test_cases; one_test->path != NULL; one_test++)
|
||||
assert_ignored(one_test->expected, one_test->path);
|
||||
|
||||
/* confirm that ignore files were cached */
|
||||
cl_assert(git_attr_cache__is_cached(
|
||||
@ -63,37 +77,22 @@ void test_status_ignore__0(void)
|
||||
|
||||
void test_status_ignore__1(void)
|
||||
{
|
||||
int ignored;
|
||||
|
||||
g_repo = cl_git_sandbox_init("attr");
|
||||
|
||||
cl_git_rewritefile("attr/.gitignore", "/*.txt\n/dir/\n");
|
||||
git_attr_cache_flush(g_repo);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "root_test4.txt"));
|
||||
cl_assert(ignored);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/subdir_test2.txt"));
|
||||
cl_assert(!ignored);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir"));
|
||||
cl_assert(ignored);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir/"));
|
||||
cl_assert(ignored);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir"));
|
||||
cl_assert(!ignored);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir/"));
|
||||
cl_assert(!ignored);
|
||||
assert_is_ignored("root_test4.txt");
|
||||
refute_is_ignored("sub/subdir_test2.txt");
|
||||
assert_is_ignored("dir");
|
||||
assert_is_ignored("dir/");
|
||||
refute_is_ignored("sub/dir");
|
||||
refute_is_ignored("sub/dir/");
|
||||
}
|
||||
|
||||
|
||||
void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
|
||||
{
|
||||
status_entry_single st;
|
||||
int ignored;
|
||||
|
||||
g_repo = cl_git_sandbox_init("empty_standard_repo");
|
||||
|
||||
@ -108,8 +107,7 @@ void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
|
||||
cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
|
||||
cl_assert(st.status == GIT_STATUS_WT_NEW);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
|
||||
cl_assert(!ignored);
|
||||
refute_is_ignored("look-ma.txt");
|
||||
|
||||
cl_git_rewritefile("empty_standard_repo/.gitignore", "*.nomatch\n");
|
||||
|
||||
@ -121,8 +119,7 @@ void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
|
||||
cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
|
||||
cl_assert(st.status == GIT_STATUS_WT_NEW);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
|
||||
cl_assert(!ignored);
|
||||
refute_is_ignored("look-ma.txt");
|
||||
|
||||
cl_git_rewritefile("empty_standard_repo/.gitignore", "*.txt\n");
|
||||
|
||||
@ -134,8 +131,7 @@ void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
|
||||
cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
|
||||
cl_assert(st.status == GIT_STATUS_IGNORED);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
|
||||
cl_assert(ignored);
|
||||
assert_is_ignored("look-ma.txt");
|
||||
}
|
||||
|
||||
void test_status_ignore__ignore_pattern_contains_space(void)
|
||||
@ -181,7 +177,6 @@ void test_status_ignore__ignore_pattern_ignorecase(void)
|
||||
void test_status_ignore__subdirectories(void)
|
||||
{
|
||||
status_entry_single st;
|
||||
int ignored;
|
||||
|
||||
g_repo = cl_git_sandbox_init("empty_standard_repo");
|
||||
|
||||
@ -198,8 +193,7 @@ void test_status_ignore__subdirectories(void)
|
||||
cl_git_pass(git_status_file(&st.status, g_repo, "ignore_me"));
|
||||
cl_assert(st.status == GIT_STATUS_IGNORED);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "ignore_me"));
|
||||
cl_assert(ignored);
|
||||
assert_is_ignored("ignore_me");
|
||||
|
||||
/* I've changed libgit2 so that the behavior here now differs from
|
||||
* core git but seems to make more sense. In core git, the following
|
||||
@ -225,37 +219,37 @@ void test_status_ignore__subdirectories(void)
|
||||
cl_git_pass(git_status_file(&st.status, g_repo, "test/ignore_me/file"));
|
||||
cl_assert(st.status == GIT_STATUS_IGNORED);
|
||||
|
||||
cl_git_pass(
|
||||
git_status_should_ignore(&ignored, g_repo, "test/ignore_me/file"));
|
||||
cl_assert(ignored);
|
||||
assert_is_ignored("test/ignore_me/file");
|
||||
}
|
||||
|
||||
static void make_test_data(void)
|
||||
static void make_test_data(const char *reponame, const char **files)
|
||||
{
|
||||
static const char *files[] = {
|
||||
"empty_standard_repo/dir/a/ignore_me",
|
||||
"empty_standard_repo/dir/b/ignore_me",
|
||||
"empty_standard_repo/dir/ignore_me",
|
||||
"empty_standard_repo/ignore_also/file",
|
||||
"empty_standard_repo/ignore_me",
|
||||
"empty_standard_repo/test/ignore_me/file",
|
||||
"empty_standard_repo/test/ignore_me/file2",
|
||||
"empty_standard_repo/test/ignore_me/and_me/file",
|
||||
NULL
|
||||
};
|
||||
static const char *repo = "empty_standard_repo";
|
||||
const char **scan;
|
||||
size_t repolen = strlen(repo) + 1;
|
||||
size_t repolen = strlen(reponame) + 1;
|
||||
|
||||
g_repo = cl_git_sandbox_init(repo);
|
||||
g_repo = cl_git_sandbox_init(reponame);
|
||||
|
||||
for (scan = files; *scan != NULL; ++scan) {
|
||||
cl_git_pass(git_futils_mkdir(
|
||||
*scan + repolen, repo, 0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST));
|
||||
*scan + repolen, reponame,
|
||||
0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST));
|
||||
cl_git_mkfile(*scan, "contents");
|
||||
}
|
||||
}
|
||||
|
||||
static const char *test_repo_1 = "empty_standard_repo";
|
||||
static const char *test_files_1[] = {
|
||||
"empty_standard_repo/dir/a/ignore_me",
|
||||
"empty_standard_repo/dir/b/ignore_me",
|
||||
"empty_standard_repo/dir/ignore_me",
|
||||
"empty_standard_repo/ignore_also/file",
|
||||
"empty_standard_repo/ignore_me",
|
||||
"empty_standard_repo/test/ignore_me/file",
|
||||
"empty_standard_repo/test/ignore_me/file2",
|
||||
"empty_standard_repo/test/ignore_me/and_me/file",
|
||||
NULL
|
||||
};
|
||||
|
||||
void test_status_ignore__subdirectories_recursion(void)
|
||||
{
|
||||
/* Let's try again with recursing into ignored dirs turned on */
|
||||
@ -292,7 +286,7 @@ void test_status_ignore__subdirectories_recursion(void)
|
||||
GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
|
||||
};
|
||||
|
||||
make_test_data();
|
||||
make_test_data(test_repo_1, test_files_1);
|
||||
cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n/ignore_also\n");
|
||||
|
||||
memset(&counts, 0x0, sizeof(status_entry_counts));
|
||||
@ -347,7 +341,7 @@ void test_status_ignore__subdirectories_not_at_root(void)
|
||||
GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
|
||||
};
|
||||
|
||||
make_test_data();
|
||||
make_test_data(test_repo_1, test_files_1);
|
||||
cl_git_rewritefile("empty_standard_repo/dir/.gitignore", "ignore_me\n/ignore_also\n");
|
||||
cl_git_rewritefile("empty_standard_repo/test/.gitignore", "and_me\n");
|
||||
|
||||
@ -389,7 +383,7 @@ void test_status_ignore__leading_slash_ignores(void)
|
||||
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
|
||||
};
|
||||
|
||||
make_test_data();
|
||||
make_test_data(test_repo_1, test_files_1);
|
||||
|
||||
cl_fake_home(&home);
|
||||
cl_git_mkfile("home/.gitignore", "/ignore_me\n");
|
||||
@ -422,151 +416,162 @@ void test_status_ignore__leading_slash_ignores(void)
|
||||
cl_fake_home_cleanup(&home);
|
||||
}
|
||||
|
||||
void test_status_ignore__contained_dir_with_matching_name(void)
|
||||
{
|
||||
static const char *test_files[] = {
|
||||
"empty_standard_repo/subdir_match/aaa/subdir_match/file",
|
||||
"empty_standard_repo/subdir_match/zzz_ignoreme",
|
||||
NULL
|
||||
};
|
||||
static const char *expected_paths[] = {
|
||||
"subdir_match/.gitignore",
|
||||
"subdir_match/aaa/subdir_match/file",
|
||||
"subdir_match/zzz_ignoreme",
|
||||
};
|
||||
static const unsigned int expected_statuses[] = {
|
||||
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED
|
||||
};
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
status_entry_counts counts;
|
||||
|
||||
make_test_data("empty_standard_repo", test_files);
|
||||
cl_git_mkfile(
|
||||
"empty_standard_repo/subdir_match/.gitignore", "*_ignoreme\n");
|
||||
|
||||
refute_is_ignored("subdir_match/aaa/subdir_match/file");
|
||||
assert_is_ignored("subdir_match/zzz_ignoreme");
|
||||
|
||||
memset(&counts, 0x0, sizeof(status_entry_counts));
|
||||
counts.expected_entry_count = 3;
|
||||
counts.expected_paths = expected_paths;
|
||||
counts.expected_statuses = expected_statuses;
|
||||
|
||||
opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
|
||||
|
||||
cl_git_pass(git_status_foreach_ext(
|
||||
g_repo, &opts, cb_status__normal, &counts));
|
||||
|
||||
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
|
||||
cl_assert_equal_i(0, counts.wrong_status_flags_count);
|
||||
cl_assert_equal_i(0, counts.wrong_sorted_path);
|
||||
}
|
||||
|
||||
void test_status_ignore__trailing_slash_star(void)
|
||||
{
|
||||
static const char *test_files[] = {
|
||||
"empty_standard_repo/file",
|
||||
"empty_standard_repo/subdir/file",
|
||||
"empty_standard_repo/subdir/sub2/sub3/file",
|
||||
NULL
|
||||
};
|
||||
|
||||
make_test_data("empty_standard_repo", test_files);
|
||||
cl_git_mkfile(
|
||||
"empty_standard_repo/subdir/.gitignore", "/**/*\n");
|
||||
|
||||
refute_is_ignored("file");
|
||||
assert_is_ignored("subdir/sub2/sub3/file");
|
||||
assert_is_ignored("subdir/file");
|
||||
}
|
||||
|
||||
void test_status_ignore__adding_internal_ignores(void)
|
||||
{
|
||||
int ignored;
|
||||
|
||||
g_repo = cl_git_sandbox_init("empty_standard_repo");
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
|
||||
cl_assert(!ignored);
|
||||
refute_is_ignored("one.txt");
|
||||
refute_is_ignored("two.bar");
|
||||
|
||||
cl_git_pass(git_ignore_add_rule(g_repo, "*.nomatch\n"));
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
|
||||
cl_assert(!ignored);
|
||||
refute_is_ignored("one.txt");
|
||||
refute_is_ignored("two.bar");
|
||||
|
||||
cl_git_pass(git_ignore_add_rule(g_repo, "*.txt\n"));
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
|
||||
cl_assert(ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
|
||||
cl_assert(!ignored);
|
||||
assert_is_ignored("one.txt");
|
||||
refute_is_ignored("two.bar");
|
||||
|
||||
cl_git_pass(git_ignore_add_rule(g_repo, "*.bar\n"));
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
|
||||
cl_assert(ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
|
||||
cl_assert(ignored);
|
||||
assert_is_ignored("one.txt");
|
||||
assert_is_ignored("two.bar");
|
||||
|
||||
cl_git_pass(git_ignore_clear_internal_rules(g_repo));
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
|
||||
cl_assert(!ignored);
|
||||
refute_is_ignored("one.txt");
|
||||
refute_is_ignored("two.bar");
|
||||
|
||||
cl_git_pass(git_ignore_add_rule(
|
||||
g_repo, "multiple\n*.rules\n# comment line\n*.bar\n"));
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
|
||||
cl_assert(ignored);
|
||||
refute_is_ignored("one.txt");
|
||||
assert_is_ignored("two.bar");
|
||||
}
|
||||
|
||||
void test_status_ignore__add_internal_as_first_thing(void)
|
||||
{
|
||||
int ignored;
|
||||
const char *add_me = "\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n\n";
|
||||
|
||||
g_repo = cl_git_sandbox_init("empty_standard_repo");
|
||||
|
||||
cl_git_pass(git_ignore_add_rule(g_repo, add_me));
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.tmp"));
|
||||
cl_assert(ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
|
||||
cl_assert(!ignored);
|
||||
assert_is_ignored("one.tmp");
|
||||
refute_is_ignored("two.bar");
|
||||
}
|
||||
|
||||
void test_status_ignore__internal_ignores_inside_deep_paths(void)
|
||||
{
|
||||
int ignored;
|
||||
const char *add_me = "Debug\nthis/is/deep\npatterned*/dir\n";
|
||||
|
||||
g_repo = cl_git_sandbox_init("empty_standard_repo");
|
||||
|
||||
cl_git_pass(git_ignore_add_rule(g_repo, add_me));
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "Debug"));
|
||||
cl_assert(ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/Debug"));
|
||||
cl_assert(ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "really/Debug/this/file"));
|
||||
cl_assert(ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "Debug/what/I/say"));
|
||||
cl_assert(ignored);
|
||||
assert_is_ignored("Debug");
|
||||
assert_is_ignored("and/Debug");
|
||||
assert_is_ignored("really/Debug/this/file");
|
||||
assert_is_ignored("Debug/what/I/say");
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/NoDebug"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "NoDebug/this"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "please/NoDebug/this"));
|
||||
cl_assert(!ignored);
|
||||
refute_is_ignored("and/NoDebug");
|
||||
refute_is_ignored("NoDebug/this");
|
||||
refute_is_ignored("please/NoDebug/this");
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deep"));
|
||||
cl_assert(ignored);
|
||||
assert_is_ignored("this/is/deep");
|
||||
/* pattern containing slash gets FNM_PATHNAME so all slashes must match */
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/this/is/deep"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deep/too"));
|
||||
cl_assert(ignored);
|
||||
refute_is_ignored("and/this/is/deep");
|
||||
assert_is_ignored("this/is/deep/too");
|
||||
/* pattern containing slash gets FNM_PATHNAME so all slashes must match */
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "but/this/is/deep/and/ignored"));
|
||||
cl_assert(!ignored);
|
||||
refute_is_ignored("but/this/is/deep/and/ignored");
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/not/deep"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "is/this/not/as/deep"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deepish"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "xthis/is/deep"));
|
||||
cl_assert(!ignored);
|
||||
refute_is_ignored("this/is/not/deep");
|
||||
refute_is_ignored("is/this/not/as/deep");
|
||||
refute_is_ignored("this/is/deepish");
|
||||
refute_is_ignored("xthis/is/deep");
|
||||
}
|
||||
|
||||
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);
|
||||
assert_is_ignored(".git");
|
||||
assert_is_ignored("this/file/.");
|
||||
assert_is_ignored("path/../funky");
|
||||
refute_is_ignored("path/whatever.c");
|
||||
|
||||
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);
|
||||
assert_is_ignored(".git");
|
||||
assert_is_ignored("this/file/.");
|
||||
assert_is_ignored("path/../funky");
|
||||
assert_is_ignored("path/whatever.c");
|
||||
|
||||
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);
|
||||
assert_is_ignored(".git");
|
||||
assert_is_ignored("this/file/.");
|
||||
assert_is_ignored("path/../funky");
|
||||
refute_is_ignored("path/whatever.c");
|
||||
}
|
||||
|
||||
void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void)
|
||||
@ -605,7 +610,6 @@ void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_s
|
||||
|
||||
void test_status_ignore__issue_1766_negated_ignores(void)
|
||||
{
|
||||
int ignored = 0;
|
||||
unsigned int status;
|
||||
|
||||
g_repo = cl_git_sandbox_init("empty_standard_repo");
|
||||
@ -617,11 +621,8 @@ void test_status_ignore__issue_1766_negated_ignores(void)
|
||||
cl_git_mkfile(
|
||||
"empty_standard_repo/a/ignoreme", "I should be ignored\n");
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore"));
|
||||
cl_assert(!ignored);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme"));
|
||||
cl_assert(ignored);
|
||||
refute_is_ignored("a/.gitignore");
|
||||
assert_is_ignored("a/ignoreme");
|
||||
|
||||
cl_git_pass(git_futils_mkdir_r(
|
||||
"empty_standard_repo/b", NULL, 0775));
|
||||
@ -630,18 +631,12 @@ void test_status_ignore__issue_1766_negated_ignores(void)
|
||||
cl_git_mkfile(
|
||||
"empty_standard_repo/b/ignoreme", "I should be ignored\n");
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/.gitignore"));
|
||||
cl_assert(!ignored);
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/ignoreme"));
|
||||
cl_assert(ignored);
|
||||
refute_is_ignored("b/.gitignore");
|
||||
assert_is_ignored("b/ignoreme");
|
||||
|
||||
/* shouldn't have changed results from first couple either */
|
||||
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore"));
|
||||
cl_assert(!ignored);
|
||||
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme"));
|
||||
cl_assert(ignored);
|
||||
refute_is_ignored("a/.gitignore");
|
||||
assert_is_ignored("a/ignoreme");
|
||||
|
||||
/* status should find the two ignore files and nothing else */
|
||||
|
||||
|
@ -9,20 +9,13 @@ int cb_status__normal(
|
||||
if (counts->debug)
|
||||
cb_status__print(path, status_flags, NULL);
|
||||
|
||||
if (counts->entry_count >= counts->expected_entry_count) {
|
||||
if (counts->entry_count >= counts->expected_entry_count)
|
||||
counts->wrong_status_flags_count++;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strcmp(path, counts->expected_paths[counts->entry_count])) {
|
||||
else if (strcmp(path, counts->expected_paths[counts->entry_count]))
|
||||
counts->wrong_sorted_path++;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (status_flags != counts->expected_statuses[counts->entry_count])
|
||||
else if (status_flags != counts->expected_statuses[counts->entry_count])
|
||||
counts->wrong_status_flags_count++;
|
||||
|
||||
exit:
|
||||
counts->entry_count++;
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user