Unverified Commit a5ddae68 by Edward Thomson Committed by GitHub

Merge pull request #5097 from pks-t/pks/ignore-escapes

gitignore with escapes
parents e277ff4d 3b517351
...@@ -565,15 +565,55 @@ void git_attr_path__free(git_attr_path *info) ...@@ -565,15 +565,55 @@ void git_attr_path__free(git_attr_path *info)
*/ */
static size_t trailing_space_length(const char *p, size_t len) static size_t trailing_space_length(const char *p, size_t len)
{ {
size_t n; size_t n, i;
for (n = len; n; n--) { for (n = len; n; n--) {
if ((p[n-1] != ' ' && p[n-1] != '\t') || if (p[n-1] != ' ' && p[n-1] != '\t')
(n > 1 && p[n-2] == '\\')) break;
/*
* Count escape-characters before space. In case where it's an
* even number of escape characters, then the escape char itself
* is escaped and the whitespace is an unescaped whitespace.
* Otherwise, the last escape char is not escaped and the
* whitespace in an escaped whitespace.
*/
i = n;
while (i > 1 && p[i-2] == '\\')
i--;
if ((n - i) % 2)
break; break;
} }
return len - n; return len - n;
} }
static size_t unescape_spaces(char *str)
{
char *scan, *pos = str;
bool escaped = false;
if (!str)
return 0;
for (scan = str; *scan; scan++) {
if (!escaped && *scan == '\\') {
escaped = true;
continue;
}
/* Only insert the escape character for escaped non-spaces */
if (escaped && !git__isspace(*scan))
*pos++ = '\\';
*pos++ = *scan;
escaped = false;
}
if (pos != scan)
*pos = '\0';
return (pos - str);
}
/* /*
* This will return 0 if the spec was filled out, * This will return 0 if the spec was filled out,
* GIT_ENOTFOUND if the fnmatch does not require matching, or * GIT_ENOTFOUND if the fnmatch does not require matching, or
...@@ -587,6 +627,7 @@ int git_attr_fnmatch__parse( ...@@ -587,6 +627,7 @@ int git_attr_fnmatch__parse(
{ {
const char *pattern, *scan; const char *pattern, *scan;
int slash_count, allow_space; int slash_count, allow_space;
bool escaped;
assert(spec && base && *base); assert(spec && base && *base);
...@@ -623,30 +664,31 @@ int git_attr_fnmatch__parse( ...@@ -623,30 +664,31 @@ int git_attr_fnmatch__parse(
} }
slash_count = 0; slash_count = 0;
escaped = false;
/* Scan until a non-escaped whitespace. */
for (scan = pattern; *scan != '\0'; ++scan) { for (scan = pattern; *scan != '\0'; ++scan) {
/* char c = *scan;
* Scan until a non-escaped whitespace: find a whitespace, then look
* one char backward to ensure that it's not prefixed by a `\`.
* Only look backward if we're not at the first position (`pattern`).
*/
if (git__isspace(*scan) && scan > pattern && *(scan - 1) != '\\') {
if (!allow_space || (*scan != ' ' && *scan != '\t' && *scan != '\r'))
break;
}
if (*scan == '/') { if (c == '\\' && !escaped) {
escaped = true;
continue;
} else if (git__isspace(c) && !escaped) {
if (!allow_space || (c != ' ' && c != '\t' && c != '\r'))
break;
} else if (c == '/') {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH; spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
slash_count++; slash_count++;
if (slash_count == 1 && pattern == scan) if (slash_count == 1 && pattern == scan)
pattern++; pattern++;
} } else if (git__iswildcard(c) && !escaped) {
/* remember if we see an unescaped wildcard in pattern */ /* remember if we see an unescaped wildcard in pattern */
else if (git__iswildcard(*scan) &&
(scan == pattern || (*(scan - 1) != '\\')))
spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD; spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
} }
escaped = false;
}
*base = scan; *base = scan;
if ((spec->length = scan - pattern) == 0) if ((spec->length = scan - pattern) == 0)
...@@ -699,9 +741,8 @@ int git_attr_fnmatch__parse( ...@@ -699,9 +741,8 @@ int git_attr_fnmatch__parse(
*base = git__next_line(pattern); *base = git__next_line(pattern);
return -1; return -1;
} else { } else {
/* strip '\' that might have be used for internal whitespace */ /* strip '\' that might have been used for internal whitespace */
spec->length = git__unescape(spec->pattern); spec->length = unescape_spaces(spec->pattern);
/* TODO: convert remaining '\' into '/' for POSIX ??? */
} }
return 0; return 0;
......
...@@ -276,9 +276,12 @@ int git_path_root(const char *path) ...@@ -276,9 +276,12 @@ int git_path_root(const char *path)
while (path[offset] && path[offset] != '/' && path[offset] != '\\') while (path[offset] && path[offset] != '/' && path[offset] != '\\')
offset++; offset++;
} }
if (path[offset] == '\\')
return offset;
#endif #endif
if (path[offset] == '/' || path[offset] == '\\') if (path[offset] == '/')
return offset; return offset;
return -1; /* Not a real error - signals that path is not rooted */ return -1; /* Not a real error - signals that path is not rooted */
......
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
static git_repository *g_repo = NULL; static git_repository *g_repo = NULL;
void test_attr_ignore__initialize(void) void test_ignore_path__initialize(void)
{ {
g_repo = cl_git_sandbox_init("attr"); g_repo = cl_git_sandbox_init("attr");
} }
void test_attr_ignore__cleanup(void) void test_ignore_path__cleanup(void)
{ {
cl_git_sandbox_cleanup(); cl_git_sandbox_cleanup();
g_repo = NULL; g_repo = NULL;
...@@ -31,7 +31,7 @@ static void assert_is_ignored_( ...@@ -31,7 +31,7 @@ static void assert_is_ignored_(
#define assert_is_ignored(expected, filepath) \ #define assert_is_ignored(expected, filepath) \
assert_is_ignored_(expected, filepath, __FILE__, __LINE__) assert_is_ignored_(expected, filepath, __FILE__, __LINE__)
void test_attr_ignore__honor_temporary_rules(void) void test_ignore_path__honor_temporary_rules(void)
{ {
cl_git_rewritefile("attr/.gitignore", "/NewFolder\n/NewFolder/NewFolder"); cl_git_rewritefile("attr/.gitignore", "/NewFolder\n/NewFolder/NewFolder");
...@@ -41,7 +41,7 @@ void test_attr_ignore__honor_temporary_rules(void) ...@@ -41,7 +41,7 @@ void test_attr_ignore__honor_temporary_rules(void)
assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
} }
void test_attr_ignore__allow_root(void) void test_ignore_path__allow_root(void)
{ {
cl_git_rewritefile("attr/.gitignore", "/"); cl_git_rewritefile("attr/.gitignore", "/");
...@@ -51,7 +51,7 @@ void test_attr_ignore__allow_root(void) ...@@ -51,7 +51,7 @@ void test_attr_ignore__allow_root(void)
assert_is_ignored(false, "NewFolder/NewFolder/File.txt"); assert_is_ignored(false, "NewFolder/NewFolder/File.txt");
} }
void test_attr_ignore__ignore_space(void) void test_ignore_path__ignore_space(void)
{ {
cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder \n/NewFolder/NewFolder"); cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder \n/NewFolder/NewFolder");
...@@ -61,7 +61,7 @@ void test_attr_ignore__ignore_space(void) ...@@ -61,7 +61,7 @@ void test_attr_ignore__ignore_space(void)
assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
} }
void test_attr_ignore__intermittent_space(void) void test_ignore_path__intermittent_space(void)
{ {
cl_git_rewritefile("attr/.gitignore", "foo bar\n"); cl_git_rewritefile("attr/.gitignore", "foo bar\n");
...@@ -70,7 +70,7 @@ void test_attr_ignore__intermittent_space(void) ...@@ -70,7 +70,7 @@ void test_attr_ignore__intermittent_space(void)
assert_is_ignored(true, "foo bar"); assert_is_ignored(true, "foo bar");
} }
void test_attr_ignore__trailing_space(void) void test_ignore_path__trailing_space(void)
{ {
cl_git_rewritefile( cl_git_rewritefile(
"attr/.gitignore", "attr/.gitignore",
...@@ -85,7 +85,7 @@ void test_attr_ignore__trailing_space(void) ...@@ -85,7 +85,7 @@ void test_attr_ignore__trailing_space(void)
assert_is_ignored(false, "bar "); assert_is_ignored(false, "bar ");
} }
void test_attr_ignore__escaped_trailing_spaces(void) void test_ignore_path__escaped_trailing_spaces(void)
{ {
cl_git_rewritefile( cl_git_rewritefile(
"attr/.gitignore", "attr/.gitignore",
...@@ -107,7 +107,7 @@ void test_attr_ignore__escaped_trailing_spaces(void) ...@@ -107,7 +107,7 @@ void test_attr_ignore__escaped_trailing_spaces(void)
assert_is_ignored(false, "qux "); assert_is_ignored(false, "qux ");
} }
void test_attr_ignore__ignore_dir(void) void test_ignore_path__ignore_dir(void)
{ {
cl_git_rewritefile("attr/.gitignore", "dir/\n"); cl_git_rewritefile("attr/.gitignore", "dir/\n");
...@@ -115,7 +115,7 @@ void test_attr_ignore__ignore_dir(void) ...@@ -115,7 +115,7 @@ void test_attr_ignore__ignore_dir(void)
assert_is_ignored(true, "dir/file"); assert_is_ignored(true, "dir/file");
} }
void test_attr_ignore__ignore_dir_with_trailing_space(void) void test_ignore_path__ignore_dir_with_trailing_space(void)
{ {
cl_git_rewritefile("attr/.gitignore", "dir/ \n"); cl_git_rewritefile("attr/.gitignore", "dir/ \n");
...@@ -123,7 +123,7 @@ void test_attr_ignore__ignore_dir_with_trailing_space(void) ...@@ -123,7 +123,7 @@ void test_attr_ignore__ignore_dir_with_trailing_space(void)
assert_is_ignored(true, "dir/file"); assert_is_ignored(true, "dir/file");
} }
void test_attr_ignore__ignore_root(void) void test_ignore_path__ignore_root(void)
{ {
cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder\n/NewFolder/NewFolder"); cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder\n/NewFolder/NewFolder");
...@@ -133,7 +133,7 @@ void test_attr_ignore__ignore_root(void) ...@@ -133,7 +133,7 @@ void test_attr_ignore__ignore_root(void)
assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
} }
void test_attr_ignore__full_paths(void) void test_ignore_path__full_paths(void)
{ {
cl_git_rewritefile("attr/.gitignore", "Folder/*/Contained"); cl_git_rewritefile("attr/.gitignore", "Folder/*/Contained");
...@@ -153,7 +153,7 @@ void test_attr_ignore__full_paths(void) ...@@ -153,7 +153,7 @@ void test_attr_ignore__full_paths(void)
assert_is_ignored(false, "Folder/Middle/More/More/Contained/Not/Happy/Child"); assert_is_ignored(false, "Folder/Middle/More/More/Contained/Not/Happy/Child");
} }
void test_attr_ignore__more_starstar_cases(void) void test_ignore_path__more_starstar_cases(void)
{ {
cl_must_pass(p_unlink("attr/.gitignore")); cl_must_pass(p_unlink("attr/.gitignore"));
cl_git_mkfile( cl_git_mkfile(
...@@ -171,7 +171,7 @@ void test_attr_ignore__more_starstar_cases(void) ...@@ -171,7 +171,7 @@ void test_attr_ignore__more_starstar_cases(void)
assert_is_ignored(false, "sub/sub2/aaa.html"); assert_is_ignored(false, "sub/sub2/aaa.html");
} }
void test_attr_ignore__leading_stars(void) void test_ignore_path__leading_stars(void)
{ {
cl_git_rewritefile( cl_git_rewritefile(
"attr/.gitignore", "attr/.gitignore",
...@@ -204,7 +204,7 @@ void test_attr_ignore__leading_stars(void) ...@@ -204,7 +204,7 @@ void test_attr_ignore__leading_stars(void)
assert_is_ignored(false, "dir1/kid2/file"); assert_is_ignored(false, "dir1/kid2/file");
} }
void test_attr_ignore__globs_and_path_delimiters(void) void test_ignore_path__globs_and_path_delimiters(void)
{ {
cl_git_rewritefile("attr/.gitignore", "foo/bar/**"); cl_git_rewritefile("attr/.gitignore", "foo/bar/**");
assert_is_ignored(true, "foo/bar/baz"); assert_is_ignored(true, "foo/bar/baz");
...@@ -230,7 +230,7 @@ void test_attr_ignore__globs_and_path_delimiters(void) ...@@ -230,7 +230,7 @@ void test_attr_ignore__globs_and_path_delimiters(void)
assert_is_ignored(false, "_test/foo/bar/code/file"); assert_is_ignored(false, "_test/foo/bar/code/file");
} }
void test_attr_ignore__skip_gitignore_directory(void) void test_ignore_path__skip_gitignore_directory(void)
{ {
cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder"); cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder");
p_unlink("attr/.gitignore"); p_unlink("attr/.gitignore");
...@@ -244,7 +244,7 @@ void test_attr_ignore__skip_gitignore_directory(void) ...@@ -244,7 +244,7 @@ void test_attr_ignore__skip_gitignore_directory(void)
assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
} }
void test_attr_ignore__subdirectory_gitignore(void) void test_ignore_path__subdirectory_gitignore(void)
{ {
p_unlink("attr/.gitignore"); p_unlink("attr/.gitignore");
cl_assert(!git_path_exists("attr/.gitignore")); cl_assert(!git_path_exists("attr/.gitignore"));
...@@ -262,7 +262,7 @@ void test_attr_ignore__subdirectory_gitignore(void) ...@@ -262,7 +262,7 @@ void test_attr_ignore__subdirectory_gitignore(void)
assert_is_ignored(false, "dir/file3"); assert_is_ignored(false, "dir/file3");
} }
void test_attr_ignore__expand_tilde_to_homedir(void) void test_ignore_path__expand_tilde_to_homedir(void)
{ {
git_config *cfg; git_config *cfg;
...@@ -292,7 +292,7 @@ void test_attr_ignore__expand_tilde_to_homedir(void) ...@@ -292,7 +292,7 @@ void test_attr_ignore__expand_tilde_to_homedir(void)
/* Ensure that the .gitignore in the subdirectory only affects /* Ensure that the .gitignore in the subdirectory only affects
* items in the subdirectory. */ * items in the subdirectory. */
void test_attr_ignore__gitignore_in_subdir(void) void test_ignore_path__gitignore_in_subdir(void)
{ {
cl_git_rmfile("attr/.gitignore"); cl_git_rmfile("attr/.gitignore");
...@@ -320,7 +320,7 @@ void test_attr_ignore__gitignore_in_subdir(void) ...@@ -320,7 +320,7 @@ void test_attr_ignore__gitignore_in_subdir(void)
} }
/* Ensure that files do not match folder cases */ /* Ensure that files do not match folder cases */
void test_attr_ignore__dont_ignore_files_for_folder(void) void test_ignore_path__dont_ignore_files_for_folder(void)
{ {
cl_git_rmfile("attr/.gitignore"); cl_git_rmfile("attr/.gitignore");
...@@ -351,7 +351,7 @@ void test_attr_ignore__dont_ignore_files_for_folder(void) ...@@ -351,7 +351,7 @@ void test_attr_ignore__dont_ignore_files_for_folder(void)
assert_is_ignored(false, "dir/TeSt"); assert_is_ignored(false, "dir/TeSt");
} }
void test_attr_ignore__symlink_to_outside(void) void test_ignore_path__symlink_to_outside(void)
{ {
#ifdef GIT_WIN32 #ifdef GIT_WIN32
cl_skip(); cl_skip();
...@@ -364,7 +364,7 @@ void test_attr_ignore__symlink_to_outside(void) ...@@ -364,7 +364,7 @@ void test_attr_ignore__symlink_to_outside(void)
assert_is_ignored(true, "lala/../symlink"); assert_is_ignored(true, "lala/../symlink");
} }
void test_attr_ignore__test(void) void test_ignore_path__test(void)
{ {
cl_git_rewritefile("attr/.gitignore", cl_git_rewritefile("attr/.gitignore",
"/*/\n" "/*/\n"
...@@ -376,7 +376,7 @@ void test_attr_ignore__test(void) ...@@ -376,7 +376,7 @@ void test_attr_ignore__test(void)
assert_is_ignored(true, "bin/foo"); assert_is_ignored(true, "bin/foo");
} }
void test_attr_ignore__unignore_dir_succeeds(void) void test_ignore_path__unignore_dir_succeeds(void)
{ {
cl_git_rewritefile("attr/.gitignore", cl_git_rewritefile("attr/.gitignore",
"*.c\n" "*.c\n"
...@@ -385,7 +385,7 @@ void test_attr_ignore__unignore_dir_succeeds(void) ...@@ -385,7 +385,7 @@ void test_attr_ignore__unignore_dir_succeeds(void)
assert_is_ignored(true, "src/foo/foo.c"); assert_is_ignored(true, "src/foo/foo.c");
} }
void test_attr_ignore__case_insensitive_unignores_previous_rule(void) void test_ignore_path__case_insensitive_unignores_previous_rule(void)
{ {
git_config *cfg; git_config *cfg;
...@@ -402,7 +402,7 @@ void test_attr_ignore__case_insensitive_unignores_previous_rule(void) ...@@ -402,7 +402,7 @@ void test_attr_ignore__case_insensitive_unignores_previous_rule(void)
assert_is_ignored(false, "case/file"); assert_is_ignored(false, "case/file");
} }
void test_attr_ignore__case_sensitive_unignore_does_nothing(void) void test_ignore_path__case_sensitive_unignore_does_nothing(void)
{ {
git_config *cfg; git_config *cfg;
...@@ -419,7 +419,7 @@ void test_attr_ignore__case_sensitive_unignore_does_nothing(void) ...@@ -419,7 +419,7 @@ void test_attr_ignore__case_sensitive_unignore_does_nothing(void)
assert_is_ignored(true, "case/file"); assert_is_ignored(true, "case/file");
} }
void test_attr_ignore__ignored_subdirfiles_with_subdir_rule(void) void test_ignore_path__ignored_subdirfiles_with_subdir_rule(void)
{ {
cl_git_rewritefile( cl_git_rewritefile(
"attr/.gitignore", "attr/.gitignore",
...@@ -431,7 +431,7 @@ void test_attr_ignore__ignored_subdirfiles_with_subdir_rule(void) ...@@ -431,7 +431,7 @@ void test_attr_ignore__ignored_subdirfiles_with_subdir_rule(void)
assert_is_ignored(true, "dir/sub1/sub2"); assert_is_ignored(true, "dir/sub1/sub2");
} }
void test_attr_ignore__ignored_subdirfiles_with_negations(void) void test_ignore_path__ignored_subdirfiles_with_negations(void)
{ {
cl_git_rewritefile( cl_git_rewritefile(
"attr/.gitignore", "attr/.gitignore",
...@@ -443,7 +443,7 @@ void test_attr_ignore__ignored_subdirfiles_with_negations(void) ...@@ -443,7 +443,7 @@ void test_attr_ignore__ignored_subdirfiles_with_negations(void)
assert_is_ignored(true, "dir/sub1/c.test"); assert_is_ignored(true, "dir/sub1/c.test");
} }
void test_attr_ignore__negative_directory_rules_only_match_directories(void) void test_ignore_path__negative_directory_rules_only_match_directories(void)
{ {
cl_git_rewritefile( cl_git_rewritefile(
"attr/.gitignore", "attr/.gitignore",
...@@ -459,3 +459,82 @@ void test_attr_ignore__negative_directory_rules_only_match_directories(void) ...@@ -459,3 +459,82 @@ void test_attr_ignore__negative_directory_rules_only_match_directories(void)
assert_is_ignored(false, "src/A.keep"); assert_is_ignored(false, "src/A.keep");
assert_is_ignored(false, ".gitignore"); assert_is_ignored(false, ".gitignore");
} }
void test_ignore_path__escaped_character(void)
{
cl_git_rewritefile("attr/.gitignore", "\\c\n");
assert_is_ignored(true, "c");
assert_is_ignored(false, "\\c");
}
void test_ignore_path__escaped_newline(void)
{
cl_git_rewritefile(
"attr/.gitignore",
"\\\nnewline\n"
);
assert_is_ignored(true, "\nnewline");
}
void test_ignore_path__escaped_glob(void)
{
cl_git_rewritefile("attr/.gitignore", "\\*\n");
assert_is_ignored(true, "*");
assert_is_ignored(false, "foo");
}
void test_ignore_path__escaped_comments(void)
{
cl_git_rewritefile(
"attr/.gitignore",
"#foo\n"
"\\#bar\n"
"\\##baz\n"
"\\#\\\\#qux\n"
);
assert_is_ignored(false, "#foo");
assert_is_ignored(true, "#bar");
assert_is_ignored(false, "\\#bar");
assert_is_ignored(true, "##baz");
assert_is_ignored(false, "\\##baz");
assert_is_ignored(true, "#\\#qux");
assert_is_ignored(false, "##qux");
assert_is_ignored(false, "\\##qux");
}
void test_ignore_path__escaped_slash(void)
{
cl_git_rewritefile(
"attr/.gitignore",
"\\\\\n"
"\\\\preceding\n"
"inter\\\\mittent\n"
"trailing\\\\\n"
);
#ifndef GIT_WIN32
assert_is_ignored(true, "\\");
assert_is_ignored(true, "\\preceding");
#endif
assert_is_ignored(true, "inter\\mittent");
assert_is_ignored(true, "trailing\\");
}
void test_ignore_path__escaped_space(void)
{
cl_git_rewritefile(
"attr/.gitignore",
"foo\\\\ \n"
"bar\\\\\\ \n");
assert_is_ignored(true, "foo\\");
assert_is_ignored(false, "foo\\ ");
assert_is_ignored(false, "foo\\\\ ");
assert_is_ignored(false, "foo\\\\");
assert_is_ignored(true, "bar\\ ");
assert_is_ignored(false, "bar\\\\");
assert_is_ignored(false, "bar\\\\ ");
assert_is_ignored(false, "bar\\\\\\");
assert_is_ignored(false, "bar\\\\\\ ");
}
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
#include "git2/attr.h" #include "git2/attr.h"
#include "ignore.h" #include "ignore.h"
#include "attr.h" #include "attr.h"
#include "status_helpers.h" #include "status/status_helpers.h"
static git_repository *g_repo = NULL; static git_repository *g_repo = NULL;
void test_status_ignore__initialize(void) void test_ignore_status__initialize(void)
{ {
} }
void test_status_ignore__cleanup(void) void test_ignore_status__cleanup(void)
{ {
cl_git_sandbox_cleanup(); cl_git_sandbox_cleanup();
} }
...@@ -33,7 +33,7 @@ static void assert_ignored_( ...@@ -33,7 +33,7 @@ static void assert_ignored_(
#define refute_is_ignored(filepath) \ #define refute_is_ignored(filepath) \
assert_ignored_(false, filepath, __FILE__, __LINE__) assert_ignored_(false, filepath, __FILE__, __LINE__)
void test_status_ignore__0(void) void test_ignore_status__0(void)
{ {
struct { struct {
const char *path; const char *path;
...@@ -75,7 +75,7 @@ void test_status_ignore__0(void) ...@@ -75,7 +75,7 @@ void test_status_ignore__0(void)
} }
void test_status_ignore__1(void) void test_ignore_status__1(void)
{ {
g_repo = cl_git_sandbox_init("attr"); g_repo = cl_git_sandbox_init("attr");
...@@ -90,7 +90,7 @@ void test_status_ignore__1(void) ...@@ -90,7 +90,7 @@ void test_status_ignore__1(void)
refute_is_ignored("sub/dir/"); refute_is_ignored("sub/dir/");
} }
void test_status_ignore__empty_repo_with_gitignore_rewrite(void) void test_ignore_status__empty_repo_with_gitignore_rewrite(void)
{ {
status_entry_single st; status_entry_single st;
...@@ -134,7 +134,7 @@ void test_status_ignore__empty_repo_with_gitignore_rewrite(void) ...@@ -134,7 +134,7 @@ void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
assert_is_ignored("look-ma.txt"); assert_is_ignored("look-ma.txt");
} }
void test_status_ignore__ignore_pattern_contains_space(void) void test_ignore_status__ignore_pattern_contains_space(void)
{ {
unsigned int flags; unsigned int flags;
const mode_t mode = 0777; const mode_t mode = 0777;
...@@ -155,7 +155,7 @@ void test_status_ignore__ignore_pattern_contains_space(void) ...@@ -155,7 +155,7 @@ void test_status_ignore__ignore_pattern_contains_space(void)
cl_assert(flags == GIT_STATUS_WT_NEW); cl_assert(flags == GIT_STATUS_WT_NEW);
} }
void test_status_ignore__ignore_pattern_ignorecase(void) void test_ignore_status__ignore_pattern_ignorecase(void)
{ {
unsigned int flags; unsigned int flags;
bool ignore_case; bool ignore_case;
...@@ -174,7 +174,7 @@ void test_status_ignore__ignore_pattern_ignorecase(void) ...@@ -174,7 +174,7 @@ void test_status_ignore__ignore_pattern_ignorecase(void)
cl_assert(flags == ignore_case ? GIT_STATUS_IGNORED : GIT_STATUS_WT_NEW); cl_assert(flags == ignore_case ? GIT_STATUS_IGNORED : GIT_STATUS_WT_NEW);
} }
void test_status_ignore__subdirectories(void) void test_ignore_status__subdirectories(void)
{ {
status_entry_single st; status_entry_single st;
...@@ -250,7 +250,7 @@ static const char *test_files_1[] = { ...@@ -250,7 +250,7 @@ static const char *test_files_1[] = {
NULL NULL
}; };
void test_status_ignore__subdirectories_recursion(void) void test_ignore_status__subdirectories_recursion(void)
{ {
/* Let's try again with recursing into ignored dirs turned on */ /* Let's try again with recursing into ignored dirs turned on */
git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_status_options opts = GIT_STATUS_OPTIONS_INIT;
...@@ -319,7 +319,7 @@ void test_status_ignore__subdirectories_recursion(void) ...@@ -319,7 +319,7 @@ void test_status_ignore__subdirectories_recursion(void)
cl_assert_equal_i(0, counts.wrong_sorted_path); cl_assert_equal_i(0, counts.wrong_sorted_path);
} }
void test_status_ignore__subdirectories_not_at_root(void) void test_ignore_status__subdirectories_not_at_root(void)
{ {
git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts; status_entry_counts counts;
...@@ -360,7 +360,7 @@ void test_status_ignore__subdirectories_not_at_root(void) ...@@ -360,7 +360,7 @@ void test_status_ignore__subdirectories_not_at_root(void)
cl_assert_equal_i(0, counts.wrong_sorted_path); cl_assert_equal_i(0, counts.wrong_sorted_path);
} }
void test_status_ignore__leading_slash_ignores(void) void test_ignore_status__leading_slash_ignores(void)
{ {
git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts; status_entry_counts counts;
...@@ -413,7 +413,7 @@ void test_status_ignore__leading_slash_ignores(void) ...@@ -413,7 +413,7 @@ void test_status_ignore__leading_slash_ignores(void)
cl_assert_equal_i(0, counts.wrong_sorted_path); cl_assert_equal_i(0, counts.wrong_sorted_path);
} }
void test_status_ignore__multiple_leading_slash(void) void test_ignore_status__multiple_leading_slash(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/a.test", "empty_standard_repo/a.test",
...@@ -437,7 +437,7 @@ void test_status_ignore__multiple_leading_slash(void) ...@@ -437,7 +437,7 @@ void test_status_ignore__multiple_leading_slash(void)
refute_is_ignored("d.test"); refute_is_ignored("d.test");
} }
void test_status_ignore__contained_dir_with_matching_name(void) void test_ignore_status__contained_dir_with_matching_name(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/subdir_match/aaa/subdir_match/file", "empty_standard_repo/subdir_match/aaa/subdir_match/file",
...@@ -477,7 +477,7 @@ void test_status_ignore__contained_dir_with_matching_name(void) ...@@ -477,7 +477,7 @@ void test_status_ignore__contained_dir_with_matching_name(void)
cl_assert_equal_i(0, counts.wrong_sorted_path); cl_assert_equal_i(0, counts.wrong_sorted_path);
} }
void test_status_ignore__trailing_slash_star(void) void test_ignore_status__trailing_slash_star(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/file", "empty_standard_repo/file",
...@@ -495,7 +495,7 @@ void test_status_ignore__trailing_slash_star(void) ...@@ -495,7 +495,7 @@ void test_status_ignore__trailing_slash_star(void)
assert_is_ignored("subdir/file"); assert_is_ignored("subdir/file");
} }
void test_status_ignore__adding_internal_ignores(void) void test_ignore_status__adding_internal_ignores(void)
{ {
g_repo = cl_git_sandbox_init("empty_standard_repo"); g_repo = cl_git_sandbox_init("empty_standard_repo");
...@@ -529,7 +529,7 @@ void test_status_ignore__adding_internal_ignores(void) ...@@ -529,7 +529,7 @@ void test_status_ignore__adding_internal_ignores(void)
assert_is_ignored("two.bar"); assert_is_ignored("two.bar");
} }
void test_status_ignore__add_internal_as_first_thing(void) void test_ignore_status__add_internal_as_first_thing(void)
{ {
const char *add_me = "\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n\n"; const char *add_me = "\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n\n";
...@@ -541,7 +541,7 @@ void test_status_ignore__add_internal_as_first_thing(void) ...@@ -541,7 +541,7 @@ void test_status_ignore__add_internal_as_first_thing(void)
refute_is_ignored("two.bar"); refute_is_ignored("two.bar");
} }
void test_status_ignore__internal_ignores_inside_deep_paths(void) void test_ignore_status__internal_ignores_inside_deep_paths(void)
{ {
const char *add_me = "Debug\nthis/is/deep\npatterned*/dir\n"; const char *add_me = "Debug\nthis/is/deep\npatterned*/dir\n";
...@@ -571,7 +571,7 @@ void test_status_ignore__internal_ignores_inside_deep_paths(void) ...@@ -571,7 +571,7 @@ void test_status_ignore__internal_ignores_inside_deep_paths(void)
refute_is_ignored("xthis/is/deep"); refute_is_ignored("xthis/is/deep");
} }
void test_status_ignore__automatically_ignore_bad_files(void) void test_ignore_status__automatically_ignore_bad_files(void)
{ {
g_repo = cl_git_sandbox_init("empty_standard_repo"); g_repo = cl_git_sandbox_init("empty_standard_repo");
...@@ -595,7 +595,7 @@ void test_status_ignore__automatically_ignore_bad_files(void) ...@@ -595,7 +595,7 @@ void test_status_ignore__automatically_ignore_bad_files(void)
refute_is_ignored("path/whatever.c"); refute_is_ignored("path/whatever.c");
} }
void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void) void test_ignore_status__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void)
{ {
status_entry_single st; status_entry_single st;
char *test_cases[] = { char *test_cases[] = {
...@@ -629,7 +629,7 @@ void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_s ...@@ -629,7 +629,7 @@ void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_s
} }
} }
void test_status_ignore__issue_1766_negated_ignores(void) void test_ignore_status__issue_1766_negated_ignores(void)
{ {
unsigned int status; unsigned int status;
...@@ -714,7 +714,7 @@ static void add_one_to_index(const char *file) ...@@ -714,7 +714,7 @@ static void add_one_to_index(const char *file)
} }
/* Some further broken scenarios that have been reported */ /* Some further broken scenarios that have been reported */
void test_status_ignore__more_breakage(void) void test_ignore_status__more_breakage(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/d1/pfx-d2/d3/d4/d5/tracked", "empty_standard_repo/d1/pfx-d2/d3/d4/d5/tracked",
...@@ -770,7 +770,7 @@ void test_status_ignore__more_breakage(void) ...@@ -770,7 +770,7 @@ void test_status_ignore__more_breakage(void)
refute_is_ignored("d1/pfx-d2/d3/d4/untracked"); refute_is_ignored("d1/pfx-d2/d3/d4/untracked");
} }
void test_status_ignore__negative_ignores_inside_ignores(void) void test_ignore_status__negative_ignores_inside_ignores(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/top/mid/btm/tracked", "empty_standard_repo/top/mid/btm/tracked",
...@@ -822,7 +822,7 @@ void test_status_ignore__negative_ignores_inside_ignores(void) ...@@ -822,7 +822,7 @@ void test_status_ignore__negative_ignores_inside_ignores(void)
refute_is_ignored("foo/bar"); refute_is_ignored("foo/bar");
} }
void test_status_ignore__negative_ignores_in_slash_star(void) void test_ignore_status__negative_ignores_in_slash_star(void)
{ {
git_status_options status_opts = GIT_STATUS_OPTIONS_INIT; git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
git_status_list *list; git_status_list *list;
...@@ -860,7 +860,7 @@ void test_status_ignore__negative_ignores_in_slash_star(void) ...@@ -860,7 +860,7 @@ void test_status_ignore__negative_ignores_in_slash_star(void)
cl_assert(found_what_about); cl_assert(found_what_about);
} }
void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(void) void test_ignore_status__negative_ignores_without_trailing_slash_inside_ignores(void)
{ {
git_status_options status_opts = GIT_STATUS_OPTIONS_INIT; git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
git_status_list *list; git_status_list *list;
...@@ -916,7 +916,7 @@ void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores( ...@@ -916,7 +916,7 @@ void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(
cl_assert(found_parent_child2_file); cl_assert(found_parent_child2_file);
} }
void test_status_ignore__negative_directory_ignores(void) void test_ignore_status__negative_directory_ignores(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/parent/child1/bar.txt", "empty_standard_repo/parent/child1/bar.txt",
...@@ -969,7 +969,7 @@ void test_status_ignore__negative_directory_ignores(void) ...@@ -969,7 +969,7 @@ void test_status_ignore__negative_directory_ignores(void)
assert_is_ignored("padded_parent/child8/bar.txt"); assert_is_ignored("padded_parent/child8/bar.txt");
} }
void test_status_ignore__unignore_entry_in_ignored_dir(void) void test_ignore_status__unignore_entry_in_ignored_dir(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/bar.txt", "empty_standard_repo/bar.txt",
...@@ -991,7 +991,7 @@ void test_status_ignore__unignore_entry_in_ignored_dir(void) ...@@ -991,7 +991,7 @@ void test_status_ignore__unignore_entry_in_ignored_dir(void)
assert_is_ignored("nested/parent/child/bar.txt"); assert_is_ignored("nested/parent/child/bar.txt");
} }
void test_status_ignore__do_not_unignore_basename_prefix(void) void test_ignore_status__do_not_unignore_basename_prefix(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/foo_bar.txt", "empty_standard_repo/foo_bar.txt",
...@@ -1007,7 +1007,7 @@ void test_status_ignore__do_not_unignore_basename_prefix(void) ...@@ -1007,7 +1007,7 @@ void test_status_ignore__do_not_unignore_basename_prefix(void)
assert_is_ignored("foo_bar.txt"); assert_is_ignored("foo_bar.txt");
} }
void test_status_ignore__filename_with_cr(void) void test_ignore_status__filename_with_cr(void)
{ {
int ignored; int ignored;
...@@ -1040,7 +1040,7 @@ void test_status_ignore__filename_with_cr(void) ...@@ -1040,7 +1040,7 @@ void test_status_ignore__filename_with_cr(void)
cl_assert_equal_i(1, ignored); cl_assert_equal_i(1, ignored);
} }
void test_status_ignore__subdir_doesnt_match_above(void) void test_ignore_status__subdir_doesnt_match_above(void)
{ {
int ignored, icase = 0, error; int ignored, icase = 0, error;
git_config *cfg; git_config *cfg;
...@@ -1073,7 +1073,7 @@ void test_status_ignore__subdir_doesnt_match_above(void) ...@@ -1073,7 +1073,7 @@ void test_status_ignore__subdir_doesnt_match_above(void)
cl_assert_equal_i(icase, ignored); cl_assert_equal_i(icase, ignored);
} }
void test_status_ignore__negate_exact_previous(void) void test_ignore_status__negate_exact_previous(void)
{ {
int ignored; int ignored;
...@@ -1085,7 +1085,7 @@ void test_status_ignore__negate_exact_previous(void) ...@@ -1085,7 +1085,7 @@ void test_status_ignore__negate_exact_previous(void)
cl_assert_equal_i(1, ignored); cl_assert_equal_i(1, ignored);
} }
void test_status_ignore__negate_starstar(void) void test_ignore_status__negate_starstar(void)
{ {
int ignored; int ignored;
...@@ -1102,7 +1102,7 @@ void test_status_ignore__negate_starstar(void) ...@@ -1102,7 +1102,7 @@ void test_status_ignore__negate_starstar(void)
cl_assert_equal_i(0, ignored); cl_assert_equal_i(0, ignored);
} }
void test_status_ignore__ignore_all_toplevel_dirs_include_files(void) void test_ignore_status__ignore_all_toplevel_dirs_include_files(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/README.md", "empty_standard_repo/README.md",
...@@ -1127,7 +1127,7 @@ void test_status_ignore__ignore_all_toplevel_dirs_include_files(void) ...@@ -1127,7 +1127,7 @@ void test_status_ignore__ignore_all_toplevel_dirs_include_files(void)
refute_is_ignored("src/foo/foo.c"); refute_is_ignored("src/foo/foo.c");
} }
void test_status_ignore__subdir_ignore_all_toplevel_dirs_include_files(void) void test_ignore_status__subdir_ignore_all_toplevel_dirs_include_files(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/project/README.md", "empty_standard_repo/project/README.md",
...@@ -1152,7 +1152,7 @@ void test_status_ignore__subdir_ignore_all_toplevel_dirs_include_files(void) ...@@ -1152,7 +1152,7 @@ void test_status_ignore__subdir_ignore_all_toplevel_dirs_include_files(void)
refute_is_ignored("project/README.md"); refute_is_ignored("project/README.md");
} }
void test_status_ignore__subdir_ignore_everything_except_certain_files(void) void test_ignore_status__subdir_ignore_everything_except_certain_files(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/project/README.md", "empty_standard_repo/project/README.md",
...@@ -1180,7 +1180,7 @@ void test_status_ignore__subdir_ignore_everything_except_certain_files(void) ...@@ -1180,7 +1180,7 @@ void test_status_ignore__subdir_ignore_everything_except_certain_files(void)
refute_is_ignored("project/src/foo/foo.c"); refute_is_ignored("project/src/foo/foo.c");
} }
void test_status_ignore__deeper(void) void test_ignore_status__deeper(void)
{ {
const char *test_files[] = { const char *test_files[] = {
"empty_standard_repo/foo.data", "empty_standard_repo/foo.data",
...@@ -1202,7 +1202,7 @@ void test_status_ignore__deeper(void) ...@@ -1202,7 +1202,7 @@ void test_status_ignore__deeper(void)
refute_is_ignored("dont_ignore/bar.data"); refute_is_ignored("dont_ignore/bar.data");
} }
void test_status_ignore__unignored_dir_with_ignored_contents(void) void test_ignore_status__unignored_dir_with_ignored_contents(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/dir/a.test", "empty_standard_repo/dir/a.test",
...@@ -1220,7 +1220,7 @@ void test_status_ignore__unignored_dir_with_ignored_contents(void) ...@@ -1220,7 +1220,7 @@ void test_status_ignore__unignored_dir_with_ignored_contents(void)
assert_is_ignored("dir/subdir/a.test"); assert_is_ignored("dir/subdir/a.test");
} }
void test_status_ignore__unignored_subdirs(void) void test_ignore_status__unignored_subdirs(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/dir/a.test", "empty_standard_repo/dir/a.test",
...@@ -1238,7 +1238,7 @@ void test_status_ignore__unignored_subdirs(void) ...@@ -1238,7 +1238,7 @@ void test_status_ignore__unignored_subdirs(void)
refute_is_ignored("dir/subdir/a.test"); refute_is_ignored("dir/subdir/a.test");
} }
void test_status_ignore__skips_bom(void) void test_ignore_status__skips_bom(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/a.test", "empty_standard_repo/a.test",
...@@ -1261,7 +1261,7 @@ void test_status_ignore__skips_bom(void) ...@@ -1261,7 +1261,7 @@ void test_status_ignore__skips_bom(void)
refute_is_ignored("bar.txt"); refute_is_ignored("bar.txt");
} }
void test_status_ignore__leading_spaces_are_significant(void) void test_ignore_status__leading_spaces_are_significant(void)
{ {
static const char *test_files[] = { static const char *test_files[] = {
"empty_standard_repo/a.test", "empty_standard_repo/a.test",
......
...@@ -343,6 +343,16 @@ void test_path_core__join_unrooted(void) ...@@ -343,6 +343,16 @@ void test_path_core__join_unrooted(void)
test_join_unrooted("c:/foo", 2, "c:/foo", "c:/asdf"); test_join_unrooted("c:/foo", 2, "c:/foo", "c:/asdf");
test_join_unrooted("c:/foo/bar", 2, "c:/foo/bar", "c:/asdf"); test_join_unrooted("c:/foo/bar", 2, "c:/foo/bar", "c:/asdf");
#ifdef GIT_WIN32
/* Paths starting with '\\' are absolute */
test_join_unrooted("\\bar", 0, "\\bar", "c:/foo/");
test_join_unrooted("\\\\network\\bar", 9, "\\\\network\\bar", "c:/foo/");
#else
/* Paths starting with '\\' are not absolute on non-Windows systems */
test_join_unrooted("/foo/\\bar", 4, "\\bar", "/foo");
test_join_unrooted("c:/foo/\\bar", 7, "\\bar", "c:/foo/");
#endif
/* Base is returned when it's provided and is the prefix */ /* Base is returned when it's provided and is the prefix */
test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo"); test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo");
test_join_unrooted("c:/foo/bar/foobar", 10, "c:/foo/bar/foobar", "c:/foo/bar"); test_join_unrooted("c:/foo/bar/foobar", 10, "c:/foo/bar/foobar", "c:/foo/bar");
......
#include "clar_libgit2.h"
#include "fileops.h"
#include "git2/attr.h"
#include "ignore.h"
#include "attr.h"
#include "status_helpers.h"
static git_repository *g_repo = NULL;
void test_status_ignore__initialize(void)
{
}
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_expect(
git_status_should_ignore(&is_ignored, g_repo, filepath), 0, 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 {
const char *path;
int expected;
} test_cases[] = {
/* pattern "ign" from .gitignore */
{ "file", 0 },
{ "ign", 1 },
{ "sub", 0 },
{ "sub/file", 0 },
{ "sub/ign", 1 },
{ "sub/ign/file", 1 },
{ "sub/ign/sub", 1 },
{ "sub/ign/sub/file", 1 },
{ "sub/sub", 0 },
{ "sub/sub/file", 0 },
{ "sub/sub/ign", 1 },
{ "sub/sub/sub", 0 },
/* pattern "dir/" from .gitignore */
{ "dir", 1 },
{ "dir/", 1 },
{ "sub/dir", 1 },
{ "sub/dir/", 1 },
{ "sub/dir/file", 1 }, /* contained in ignored parent */
{ "sub/sub/dir", 0 }, /* dir is not actually a dir, but a file */
{ NULL, 0 }
}, *one_test;
g_repo = cl_git_sandbox_init("attr");
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(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/exclude"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitignore"));
}
void test_status_ignore__1(void)
{
g_repo = cl_git_sandbox_init("attr");
cl_git_rewritefile("attr/.gitignore", "/*.txt\n/dir/\n");
git_attr_cache_flush(g_repo);
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;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_mkfile(
"empty_standard_repo/look-ma.txt", "I'm going to be ignored!");
memset(&st, 0, sizeof(st));
cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
cl_assert(st.count == 1);
cl_assert(st.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
cl_assert(st.status == GIT_STATUS_WT_NEW);
refute_is_ignored("look-ma.txt");
cl_git_rewritefile("empty_standard_repo/.gitignore", "*.nomatch\n");
memset(&st, 0, sizeof(st));
cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
cl_assert(st.count == 2);
cl_assert(st.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
cl_assert(st.status == GIT_STATUS_WT_NEW);
refute_is_ignored("look-ma.txt");
cl_git_rewritefile("empty_standard_repo/.gitignore", "*.txt\n");
memset(&st, 0, sizeof(st));
cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
cl_assert(st.count == 2);
cl_assert(st.status == GIT_STATUS_IGNORED);
cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
cl_assert(st.status == GIT_STATUS_IGNORED);
assert_is_ignored("look-ma.txt");
}
void test_status_ignore__ignore_pattern_contains_space(void)
{
unsigned int flags;
const mode_t mode = 0777;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_rewritefile("empty_standard_repo/.gitignore", "foo bar.txt\n");
cl_git_mkfile(
"empty_standard_repo/foo bar.txt", "I'm going to be ignored!");
cl_git_pass(git_status_file(&flags, g_repo, "foo bar.txt"));
cl_assert(flags == GIT_STATUS_IGNORED);
cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", mode));
cl_git_mkfile("empty_standard_repo/foo/look-ma.txt", "I'm not going to be ignored!");
cl_git_pass(git_status_file(&flags, g_repo, "foo/look-ma.txt"));
cl_assert(flags == GIT_STATUS_WT_NEW);
}
void test_status_ignore__ignore_pattern_ignorecase(void)
{
unsigned int flags;
bool ignore_case;
git_index *index;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_rewritefile("empty_standard_repo/.gitignore", "a.txt\n");
cl_git_mkfile("empty_standard_repo/A.txt", "Differs in case");
cl_git_pass(git_repository_index(&index, g_repo));
ignore_case = (git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0;
git_index_free(index);
cl_git_pass(git_status_file(&flags, g_repo, "A.txt"));
cl_assert(flags == ignore_case ? GIT_STATUS_IGNORED : GIT_STATUS_WT_NEW);
}
void test_status_ignore__subdirectories(void)
{
status_entry_single st;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_mkfile(
"empty_standard_repo/ignore_me", "I'm going to be ignored!");
cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n");
memset(&st, 0, sizeof(st));
cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
cl_assert_equal_i(2, st.count);
cl_assert(st.status == GIT_STATUS_IGNORED);
cl_git_pass(git_status_file(&st.status, g_repo, "ignore_me"));
cl_assert(st.status == GIT_STATUS_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
* items are skipped completed, even if --ignored is passed to status.
* It you mirror these steps and run "git status -uall --ignored" then
* you will not see "test/ignore_me/" in the results.
*
* However, we had a couple reports of this as a bug, plus there is a
* similar circumstance where we were differing for core git when you
* used a rooted path for an ignore, so I changed this behavior.
*/
cl_git_pass(git_futils_mkdir_r(
"empty_standard_repo/test/ignore_me", 0775));
cl_git_mkfile(
"empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!");
cl_git_mkfile(
"empty_standard_repo/test/ignore_me/file2", "Me, too!");
memset(&st, 0, sizeof(st));
cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
cl_assert_equal_i(3, st.count);
cl_git_pass(git_status_file(&st.status, g_repo, "test/ignore_me/file"));
cl_assert(st.status == GIT_STATUS_IGNORED);
assert_is_ignored("test/ignore_me/file");
}
static void make_test_data(const char *reponame, const char **files)
{
const char **scan;
size_t repolen = strlen(reponame) + 1;
g_repo = cl_git_sandbox_init(reponame);
for (scan = files; *scan != NULL; ++scan) {
cl_git_pass(git_futils_mkdir_relative(
*scan + repolen, reponame,
0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST, NULL));
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 */
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *paths_r[] = {
".gitignore",
"dir/a/ignore_me",
"dir/b/ignore_me",
"dir/ignore_me",
"ignore_also/file",
"ignore_me",
"test/ignore_me/and_me/file",
"test/ignore_me/file",
"test/ignore_me/file2",
};
static const unsigned int statuses_r[] = {
GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
};
static const char *paths_nr[] = {
".gitignore",
"dir/a/ignore_me",
"dir/b/ignore_me",
"dir/ignore_me",
"ignore_also/",
"ignore_me",
"test/ignore_me/",
};
static const unsigned int statuses_nr[] = {
GIT_STATUS_WT_NEW,
GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
};
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));
counts.expected_entry_count = 9;
counts.expected_paths = paths_r;
counts.expected_statuses = statuses_r;
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);
memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = 7;
counts.expected_paths = paths_nr;
counts.expected_statuses = statuses_nr;
opts.flags = GIT_STATUS_OPT_DEFAULTS;
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__subdirectories_not_at_root(void)
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *paths_1[] = {
"dir/.gitignore",
"dir/a/ignore_me",
"dir/b/ignore_me",
"dir/ignore_me",
"ignore_also/file",
"ignore_me",
"test/.gitignore",
"test/ignore_me/and_me/file",
"test/ignore_me/file",
"test/ignore_me/file2",
};
static const unsigned int statuses_1[] = {
GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
};
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");
memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = 10;
counts.expected_paths = paths_1;
counts.expected_statuses = statuses_1;
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__leading_slash_ignores(void)
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *paths_2[] = {
"dir/.gitignore",
"dir/a/ignore_me",
"dir/b/ignore_me",
"dir/ignore_me",
"ignore_also/file",
"ignore_me",
"test/.gitignore",
"test/ignore_me/and_me/file",
"test/ignore_me/file",
"test/ignore_me/file2",
};
static const unsigned int statuses_2[] = {
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
};
make_test_data(test_repo_1, test_files_1);
cl_fake_home();
cl_git_mkfile("home/.gitignore", "/ignore_me\n");
{
git_config *cfg;
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_string(
cfg, "core.excludesfile", "~/.gitignore"));
git_config_free(cfg);
}
cl_git_rewritefile("empty_standard_repo/.git/info/exclude", "/ignore_also\n");
cl_git_rewritefile("empty_standard_repo/dir/.gitignore", "/ignore_me\n");
cl_git_rewritefile("empty_standard_repo/test/.gitignore", "/and_me\n");
memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = 10;
counts.expected_paths = paths_2;
counts.expected_statuses = statuses_2;
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__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)
{
g_repo = cl_git_sandbox_init("empty_standard_repo");
refute_is_ignored("one.txt");
refute_is_ignored("two.bar");
cl_git_pass(git_ignore_add_rule(g_repo, "*.nomatch\n"));
refute_is_ignored("one.txt");
refute_is_ignored("two.bar");
cl_git_pass(git_ignore_add_rule(g_repo, "*.txt\n"));
assert_is_ignored("one.txt");
refute_is_ignored("two.bar");
cl_git_pass(git_ignore_add_rule(g_repo, "*.bar\n"));
assert_is_ignored("one.txt");
assert_is_ignored("two.bar");
cl_git_pass(git_ignore_clear_internal_rules(g_repo));
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"));
refute_is_ignored("one.txt");
assert_is_ignored("two.bar");
}
void test_status_ignore__add_internal_as_first_thing(void)
{
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));
assert_is_ignored("one.tmp");
refute_is_ignored("two.bar");
}
void test_status_ignore__internal_ignores_inside_deep_paths(void)
{
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));
assert_is_ignored("Debug");
assert_is_ignored("and/Debug");
assert_is_ignored("really/Debug/this/file");
assert_is_ignored("Debug/what/I/say");
refute_is_ignored("and/NoDebug");
refute_is_ignored("NoDebug/this");
refute_is_ignored("please/NoDebug/this");
assert_is_ignored("this/is/deep");
/* pattern containing slash gets FNM_PATHNAME so all slashes must match */
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 */
refute_is_ignored("but/this/is/deep/and/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)
{
g_repo = cl_git_sandbox_init("empty_standard_repo");
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"));
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));
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)
{
status_entry_single st;
char *test_cases[] = {
"!file",
"#blah",
"[blah]",
"[attr]",
"[attr]blah",
NULL
};
int i;
for (i = 0; *(test_cases + i) != NULL; i++) {
git_buf file = GIT_BUF_INIT;
char *file_name = *(test_cases + i);
git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_buf_joinpath(&file, "empty_standard_repo", file_name));
cl_git_mkfile(git_buf_cstr(&file), "Please don't ignore me!");
memset(&st, 0, sizeof(st));
cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
cl_assert(st.count == 1);
cl_assert(st.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&st.status, repo, file_name));
cl_assert(st.status == GIT_STATUS_WT_NEW);
cl_git_sandbox_cleanup();
git_buf_dispose(&file);
}
}
void test_status_ignore__issue_1766_negated_ignores(void)
{
unsigned int status;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_futils_mkdir_r(
"empty_standard_repo/a", 0775));
cl_git_mkfile(
"empty_standard_repo/a/.gitignore", "*\n!.gitignore\n");
cl_git_mkfile(
"empty_standard_repo/a/ignoreme", "I should be ignored\n");
refute_is_ignored("a/.gitignore");
assert_is_ignored("a/ignoreme");
cl_git_pass(git_futils_mkdir_r(
"empty_standard_repo/b", 0775));
cl_git_mkfile(
"empty_standard_repo/b/.gitignore", "*\n!.gitignore\n");
cl_git_mkfile(
"empty_standard_repo/b/ignoreme", "I should be ignored\n");
refute_is_ignored("b/.gitignore");
assert_is_ignored("b/ignoreme");
/* shouldn't have changed results from first couple either */
refute_is_ignored("a/.gitignore");
assert_is_ignored("a/ignoreme");
/* status should find the two ignore files and nothing else */
cl_git_pass(git_status_file(&status, g_repo, "a/.gitignore"));
cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status);
cl_git_pass(git_status_file(&status, g_repo, "a/ignoreme"));
cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status);
cl_git_pass(git_status_file(&status, g_repo, "b/.gitignore"));
cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status);
cl_git_pass(git_status_file(&status, g_repo, "b/ignoreme"));
cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status);
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *paths[] = {
"a/.gitignore",
"a/ignoreme",
"b/.gitignore",
"b/ignoreme",
};
static const unsigned int statuses[] = {
GIT_STATUS_WT_NEW,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_NEW,
GIT_STATUS_IGNORED,
};
memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = 4;
counts.expected_paths = paths;
counts.expected_statuses = statuses;
opts.flags = GIT_STATUS_OPT_DEFAULTS;
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);
}
}
static void add_one_to_index(const char *file)
{
git_index *index;
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_add_bypath(index, file));
git_index_free(index);
}
/* Some further broken scenarios that have been reported */
void test_status_ignore__more_breakage(void)
{
static const char *test_files[] = {
"empty_standard_repo/d1/pfx-d2/d3/d4/d5/tracked",
"empty_standard_repo/d1/pfx-d2/d3/d4/d5/untracked",
"empty_standard_repo/d1/pfx-d2/d3/d4/untracked",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"/d1/pfx-*\n"
"!/d1/pfx-d2/\n"
"/d1/pfx-d2/*\n"
"!/d1/pfx-d2/d3/\n"
"/d1/pfx-d2/d3/*\n"
"!/d1/pfx-d2/d3/d4/\n");
add_one_to_index("d1/pfx-d2/d3/d4/d5/tracked");
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *files[] = {
".gitignore",
"d1/pfx-d2/d3/d4/d5/tracked",
"d1/pfx-d2/d3/d4/d5/untracked",
"d1/pfx-d2/d3/d4/untracked",
};
static const unsigned int statuses[] = {
GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
};
memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = 4;
counts.expected_paths = files;
counts.expected_statuses = statuses;
opts.flags = GIT_STATUS_OPT_DEFAULTS |
GIT_STATUS_OPT_INCLUDE_IGNORED |
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);
}
refute_is_ignored("d1/pfx-d2/d3/d4/d5/tracked");
refute_is_ignored("d1/pfx-d2/d3/d4/d5/untracked");
refute_is_ignored("d1/pfx-d2/d3/d4/untracked");
}
void test_status_ignore__negative_ignores_inside_ignores(void)
{
static const char *test_files[] = {
"empty_standard_repo/top/mid/btm/tracked",
"empty_standard_repo/top/mid/btm/untracked",
"empty_standard_repo/zoo/bar",
"empty_standard_repo/zoo/foo/bar",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"top\n"
"!top/mid/btm\n"
"zoo/*\n"
"!zoo/bar\n"
"!zoo/foo/bar\n");
add_one_to_index("top/mid/btm/tracked");
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *files[] = {
".gitignore", "top/mid/btm/tracked", "top/mid/btm/untracked",
"zoo/bar", "zoo/foo/bar",
};
static const unsigned int statuses[] = {
GIT_STATUS_WT_NEW, GIT_STATUS_INDEX_NEW, GIT_STATUS_IGNORED,
GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED,
};
memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = 5;
counts.expected_paths = files;
counts.expected_statuses = statuses;
opts.flags = GIT_STATUS_OPT_DEFAULTS |
GIT_STATUS_OPT_INCLUDE_IGNORED |
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);
}
assert_is_ignored("top/mid/btm/tracked");
assert_is_ignored("top/mid/btm/untracked");
refute_is_ignored("foo/bar");
}
void test_status_ignore__negative_ignores_in_slash_star(void)
{
git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
git_status_list *list;
int found_look_ma = 0, found_what_about = 0;
size_t i;
static const char *test_files[] = {
"empty_standard_repo/bin/look-ma.txt",
"empty_standard_repo/bin/what-about-me.txt",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"bin/*\n"
"!bin/w*\n");
assert_is_ignored("bin/look-ma.txt");
refute_is_ignored("bin/what-about-me.txt");
status_opts.flags = GIT_STATUS_OPT_DEFAULTS;
cl_git_pass(git_status_list_new(&list, g_repo, &status_opts));
for (i = 0; i < git_status_list_entrycount(list); i++) {
const git_status_entry *entry = git_status_byindex(list, i);
if (!strcmp("bin/look-ma.txt", entry->index_to_workdir->new_file.path))
found_look_ma = 1;
if (!strcmp("bin/what-about-me.txt", entry->index_to_workdir->new_file.path))
found_what_about = 1;
}
git_status_list_free(list);
cl_assert(found_look_ma);
cl_assert(found_what_about);
}
void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(void)
{
git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
git_status_list *list;
int found_parent_file = 0, found_parent_child1_file = 0, found_parent_child2_file = 0;
size_t i;
static const char *test_files[] = {
"empty_standard_repo/parent/file.txt",
"empty_standard_repo/parent/force.txt",
"empty_standard_repo/parent/child1/file.txt",
"empty_standard_repo/parent/child2/file.txt",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"parent/*\n"
"!parent/force.txt\n"
"!parent/child1\n"
"!parent/child2/\n");
add_one_to_index("parent/force.txt");
assert_is_ignored("parent/file.txt");
refute_is_ignored("parent/force.txt");
refute_is_ignored("parent/child1/file.txt");
refute_is_ignored("parent/child2/file.txt");
status_opts.flags = GIT_STATUS_OPT_DEFAULTS;
cl_git_pass(git_status_list_new(&list, g_repo, &status_opts));
for (i = 0; i < git_status_list_entrycount(list); i++) {
const git_status_entry *entry = git_status_byindex(list, i);
if (!entry->index_to_workdir)
continue;
if (!strcmp("parent/file.txt", entry->index_to_workdir->new_file.path))
found_parent_file = 1;
if (!strcmp("parent/force.txt", entry->index_to_workdir->new_file.path))
found_parent_file = 1;
if (!strcmp("parent/child1/file.txt", entry->index_to_workdir->new_file.path))
found_parent_child1_file = 1;
if (!strcmp("parent/child2/file.txt", entry->index_to_workdir->new_file.path))
found_parent_child2_file = 1;
}
git_status_list_free(list);
cl_assert(found_parent_file);
cl_assert(found_parent_child1_file);
cl_assert(found_parent_child2_file);
}
void test_status_ignore__negative_directory_ignores(void)
{
static const char *test_files[] = {
"empty_standard_repo/parent/child1/bar.txt",
"empty_standard_repo/parent/child2/bar.txt",
"empty_standard_repo/parent/child3/foo.txt",
"empty_standard_repo/parent/child4/bar.txt",
"empty_standard_repo/parent/nested/child5/bar.txt",
"empty_standard_repo/parent/nested/child6/bar.txt",
"empty_standard_repo/parent/nested/child7/bar.txt",
"empty_standard_repo/padded_parent/child8/bar.txt",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"foo.txt\n"
"parent/child1\n"
"parent/child2\n"
"parent/child4\n"
"parent/nested/child5\n"
"nested/child6\n"
"nested/child7\n"
"padded_parent/child8\n"
/* test simple exact match */
"!parent/child1\n"
/* test negating file without negating dir */
"!parent/child2/bar.txt\n"
/* test negative pattern on dir with its content
* being ignored */
"!parent/child3\n"
/* test with partial match at end */
"!child4\n"
/* test with partial match with '/' at end */
"!nested/child5\n"
/* test with complete match */
"!nested/child6\n"
/* test with trailing '/' */
"!child7/\n"
/* test with partial dir match */
"!_parent/child8\n");
refute_is_ignored("parent/child1/bar.txt");
assert_is_ignored("parent/child2/bar.txt");
assert_is_ignored("parent/child3/foo.txt");
refute_is_ignored("parent/child4/bar.txt");
assert_is_ignored("parent/nested/child5/bar.txt");
refute_is_ignored("parent/nested/child6/bar.txt");
refute_is_ignored("parent/nested/child7/bar.txt");
assert_is_ignored("padded_parent/child8/bar.txt");
}
void test_status_ignore__unignore_entry_in_ignored_dir(void)
{
static const char *test_files[] = {
"empty_standard_repo/bar.txt",
"empty_standard_repo/parent/bar.txt",
"empty_standard_repo/parent/child/bar.txt",
"empty_standard_repo/nested/parent/child/bar.txt",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"bar.txt\n"
"!parent/child/bar.txt\n");
assert_is_ignored("bar.txt");
assert_is_ignored("parent/bar.txt");
refute_is_ignored("parent/child/bar.txt");
assert_is_ignored("nested/parent/child/bar.txt");
}
void test_status_ignore__do_not_unignore_basename_prefix(void)
{
static const char *test_files[] = {
"empty_standard_repo/foo_bar.txt",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"foo_bar.txt\n"
"!bar.txt\n");
assert_is_ignored("foo_bar.txt");
}
void test_status_ignore__filename_with_cr(void)
{
int ignored;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_mkfile("empty_standard_repo/.gitignore", "Icon\r\r\n");
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
cl_assert_equal_i(1, ignored);
cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\n");
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn"));
cl_assert_equal_i(1, ignored);
cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\r\n");
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn"));
cl_assert_equal_i(1, ignored);
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn\r"));
cl_assert_equal_i(0, ignored);
cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\r\r\n");
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn\r"));
cl_assert_equal_i(1, ignored);
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
cl_assert_equal_i(0, ignored);
cl_git_mkfile("empty_standard_repo/.gitignore", "Icon\r\n");
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
cl_assert_equal_i(0, ignored);
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon"));
cl_assert_equal_i(1, ignored);
}
void test_status_ignore__subdir_doesnt_match_above(void)
{
int ignored, icase = 0, error;
git_config *cfg;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
error = git_config_get_bool(&icase, cfg, "core.ignorecase");
git_config_free(cfg);
if (error == GIT_ENOTFOUND)
error = 0;
cl_git_pass(error);
cl_git_pass(p_mkdir("empty_standard_repo/src", 0777));
cl_git_pass(p_mkdir("empty_standard_repo/src/src", 0777));
cl_git_mkfile("empty_standard_repo/src/.gitignore", "src\n");
cl_git_mkfile("empty_standard_repo/.gitignore", "");
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/test.txt"));
cl_assert_equal_i(0, ignored);
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/src/test.txt"));
cl_assert_equal_i(1, ignored);
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/foo/test.txt"));
cl_assert_equal_i(0, ignored);
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "SRC/src/test.txt"));
cl_assert_equal_i(icase, ignored);
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/SRC/test.txt"));
cl_assert_equal_i(icase, ignored);
}
void test_status_ignore__negate_exact_previous(void)
{
int ignored;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_mkfile("empty_standard_repo/.gitignore", "*.com\ntags\n!tags/\n.buildpath");
cl_git_mkfile("empty_standard_repo/.buildpath", "");
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, ".buildpath"));
cl_assert_equal_i(1, ignored);
}
void test_status_ignore__negate_starstar(void)
{
int ignored;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_mkfile("empty_standard_repo/.gitignore",
"code/projects/**/packages/*\n"
"!code/projects/**/packages/repositories.config");
cl_git_pass(git_futils_mkdir_r("empty_standard_repo/code/projects/foo/bar/packages", 0777));
cl_git_mkfile("empty_standard_repo/code/projects/foo/bar/packages/repositories.config", "");
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "code/projects/foo/bar/packages/repositories.config"));
cl_assert_equal_i(0, ignored);
}
void test_status_ignore__ignore_all_toplevel_dirs_include_files(void)
{
static const char *test_files[] = {
"empty_standard_repo/README.md",
"empty_standard_repo/src/main.c",
"empty_standard_repo/src/foo/foo.c",
"empty_standard_repo/dist/foo.o",
"empty_standard_repo/dist/main.o",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"/*/\n"
"!/src\n");
assert_is_ignored("dist/foo.o");
assert_is_ignored("dist/main.o");
refute_is_ignored("README.md");
refute_is_ignored("src/foo.c");
refute_is_ignored("src/foo/foo.c");
}
void test_status_ignore__subdir_ignore_all_toplevel_dirs_include_files(void)
{
static const char *test_files[] = {
"empty_standard_repo/project/README.md",
"empty_standard_repo/project/src/main.c",
"empty_standard_repo/project/src/foo/foo.c",
"empty_standard_repo/project/dist/foo.o",
"empty_standard_repo/project/dist/main.o",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/project/.gitignore",
"/*/\n"
"!/src\n");
assert_is_ignored("project/dist/foo.o");
assert_is_ignored("project/dist/main.o");
refute_is_ignored("project/src/foo.c");
refute_is_ignored("project/src/foo/foo.c");
refute_is_ignored("project/README.md");
}
void test_status_ignore__subdir_ignore_everything_except_certain_files(void)
{
static const char *test_files[] = {
"empty_standard_repo/project/README.md",
"empty_standard_repo/project/some_file",
"empty_standard_repo/project/src/main.c",
"empty_standard_repo/project/src/foo/foo.c",
"empty_standard_repo/project/dist/foo.o",
"empty_standard_repo/project/dist/main.o",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/project/.gitignore",
"/*\n"
"!/src\n"
"!README.md\n");
assert_is_ignored("project/some_file");
assert_is_ignored("project/dist/foo.o");
assert_is_ignored("project/dist/main.o");
refute_is_ignored("project/README.md");
refute_is_ignored("project/src/foo.c");
refute_is_ignored("project/src/foo/foo.c");
}
void test_status_ignore__deeper(void)
{
const char *test_files[] = {
"empty_standard_repo/foo.data",
"empty_standard_repo/bar.data",
"empty_standard_repo/dont_ignore/foo.data",
"empty_standard_repo/dont_ignore/bar.data",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile("empty_standard_repo/.gitignore",
"*.data\n"
"!dont_ignore/*.data\n");
assert_is_ignored("foo.data");
assert_is_ignored("bar.data");
refute_is_ignored("dont_ignore/foo.data");
refute_is_ignored("dont_ignore/bar.data");
}
void test_status_ignore__unignored_dir_with_ignored_contents(void)
{
static const char *test_files[] = {
"empty_standard_repo/dir/a.test",
"empty_standard_repo/dir/subdir/a.test",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"*.test\n"
"!dir/*\n");
refute_is_ignored("dir/a.test");
assert_is_ignored("dir/subdir/a.test");
}
void test_status_ignore__unignored_subdirs(void)
{
static const char *test_files[] = {
"empty_standard_repo/dir/a.test",
"empty_standard_repo/dir/subdir/a.test",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"dir/*\n"
"!dir/*/\n");
assert_is_ignored("dir/a.test");
refute_is_ignored("dir/subdir/a.test");
}
void test_status_ignore__skips_bom(void)
{
static const char *test_files[] = {
"empty_standard_repo/a.test",
"empty_standard_repo/b.test",
"empty_standard_repo/c.test",
"empty_standard_repo/foo.txt",
"empty_standard_repo/bar.txt",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
"\xEF\xBB\xBF*.test\n");
assert_is_ignored("a.test");
assert_is_ignored("b.test");
assert_is_ignored("c.test");
refute_is_ignored("foo.txt");
refute_is_ignored("bar.txt");
}
void test_status_ignore__leading_spaces_are_significant(void)
{
static const char *test_files[] = {
"empty_standard_repo/a.test",
"empty_standard_repo/b.test",
"empty_standard_repo/c.test",
"empty_standard_repo/d.test",
NULL
};
make_test_data("empty_standard_repo", test_files);
cl_git_mkfile(
"empty_standard_repo/.gitignore",
" a.test\n"
"# this is a comment\n"
"b.test\n"
"\tc.test\n"
" # not a comment\n"
"d.test\n");
refute_is_ignored("a.test");
assert_is_ignored(" a.test");
refute_is_ignored("# this is a comment");
assert_is_ignored("b.test");
refute_is_ignored("c.test");
assert_is_ignored("\tc.test");
assert_is_ignored(" # not a comment");
assert_is_ignored("d.test");
}
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