From a51cd8e6f6724079a552b75e014f792f3f68e158 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 16 Jan 2012 16:58:27 -0800 Subject: Fix handling of relative paths for attrs Per issue #533, the handling of relative paths in attribute and ignore files was not right. Fixed this by pre-joining the relative path of the attribute/ignore file onto the match string when a full path match is required. Unfortunately, fixing this required a bit more code than I would have liked because I had to juggle things around so that the fnmatch parser would have sufficient information to prepend the relative path when it was needed. --- src/attr.c | 7 +- src/attr.h | 2 +- src/attr_file.c | 113 +++++++++++++++++---------- src/attr_file.h | 12 ++- src/ignore.c | 28 +++---- src/util.c | 17 ++++ src/util.h | 2 + tests-clay/attr/file.c | 20 +++-- tests-clay/attr/lookup.c | 25 +++--- tests-clay/attr/repo.c | 47 ++++++----- tests-clay/status/ignore.c | 49 ++++++++++++ tests/resources/attr/.gitted/info/attributes | 2 + tests/resources/attr/file | 1 + tests/resources/attr/gitignore | 2 + tests/resources/attr/ign | 1 + tests/resources/attr/sub/.gitattributes | 7 ++ tests/resources/attr/sub/abc | 37 +++++++++ tests/resources/attr/sub/file | 1 + tests/resources/attr/sub/ign | 1 + tests/resources/attr/sub/sub/file | 1 + tests/resources/attr/sub/sub/subsub.txt | 1 + tests/resources/attr/sub/subdir_test1 | 2 + tests/resources/attr/sub/subdir_test2.txt | 1 + tests/resources/attr/subdir/.gitattributes | 5 -- tests/resources/attr/subdir/abc | 37 --------- tests/resources/attr/subdir/subdir_test1 | 2 - tests/resources/attr/subdir/subdir_test2.txt | 1 - tests/resources/attr/subdir2/subdir2_test1 | 1 - 28 files changed, 276 insertions(+), 149 deletions(-) create mode 100644 tests-clay/status/ignore.c create mode 100644 tests/resources/attr/file create mode 100644 tests/resources/attr/gitignore create mode 100644 tests/resources/attr/ign create mode 100644 tests/resources/attr/sub/.gitattributes create mode 100644 tests/resources/attr/sub/abc create mode 100644 tests/resources/attr/sub/file create mode 100644 tests/resources/attr/sub/ign create mode 100644 tests/resources/attr/sub/sub/file create mode 100644 tests/resources/attr/sub/sub/subsub.txt create mode 100644 tests/resources/attr/sub/subdir_test1 create mode 100644 tests/resources/attr/sub/subdir_test2.txt delete mode 100644 tests/resources/attr/subdir/.gitattributes delete mode 100644 tests/resources/attr/subdir/abc delete mode 100644 tests/resources/attr/subdir/subdir_test1 delete mode 100644 tests/resources/attr/subdir/subdir_test2.txt delete mode 100644 tests/resources/attr/subdir2/subdir2_test1 diff --git a/src/attr.c b/src/attr.c index dc42379f..3fe76d12 100644 --- a/src/attr.c +++ b/src/attr.c @@ -214,7 +214,7 @@ int git_attr_cache__push_file( git_vector *stack, const char *base, const char *filename, - int (*loader)(git_repository *, const char *, git_attr_file **)) + int (*loader)(git_repository *, const char *, git_attr_file *)) { int error = GIT_SUCCESS; git_attr_cache *cache = &repo->attrcache; @@ -231,11 +231,12 @@ int git_attr_cache__push_file( /* either get attr_file from cache or read from disk */ file = git_hashtable_lookup(cache->files, filename); if (file == NULL && git_futils_exists(filename) == GIT_SUCCESS) { - error = (*loader)(repo, filename, &file); + if ((error = git_attr_file__new(&file)) == GIT_SUCCESS) + error = (*loader)(repo, filename, file); add_to_cache = (error == GIT_SUCCESS); } - if (file != NULL) { + if (error == GIT_SUCCESS && file != NULL) { /* add file to vector, if we found it */ error = git_vector_insert(stack, file); diff --git a/src/attr.h b/src/attr.h index 5edff30d..a758cc4b 100644 --- a/src/attr.h +++ b/src/attr.h @@ -25,6 +25,6 @@ extern int git_attr_cache__push_file( git_vector *stack, const char *base, const char *filename, - int (*loader)(git_repository *, const char *, git_attr_file **)); + int (*loader)(git_repository *, const char *, git_attr_file *)); #endif diff --git a/src/attr_file.c b/src/attr_file.c index 5ea07c98..f6eaad69 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -31,21 +31,46 @@ int git_attr_file__new(git_attr_file **attrs_ptr) return error; } +int git_attr_file__set_path( + git_repository *repo, const char *path, git_attr_file *file) +{ + if (file->path != NULL) { + git__free(file->path); + file->path = NULL; + } + + if (repo == NULL) + file->path = git__strdup(path); + else { + const char *workdir = git_repository_workdir(repo); + + if (workdir && git__prefixcmp(path, workdir) == 0) + file->path = git__strdup(path + strlen(workdir)); + else + file->path = git__strdup(path); + } + + return (file->path == NULL) ? GIT_ENOMEM : GIT_SUCCESS; +} + int git_attr_file__from_buffer( - git_repository *repo, const char *buffer, git_attr_file **out) + git_repository *repo, const char *buffer, git_attr_file *attrs) { int error = GIT_SUCCESS; - git_attr_file *attrs = NULL; const char *scan = NULL; + char *context = NULL; git_attr_rule *rule = NULL; - *out = NULL; - - if ((error = git_attr_file__new(&attrs)) < GIT_SUCCESS) - goto cleanup; + assert(buffer && attrs); scan = buffer; + if (attrs->path && git__suffixcmp(attrs->path, GIT_ATTR_FILE) == 0) { + context = git__strndup(attrs->path, + strlen(attrs->path) - strlen(GIT_ATTR_FILE)); + if (!context) error = GIT_ENOMEM; + } + while (error == GIT_SUCCESS && *scan) { /* allocate rule if needed */ if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) { @@ -54,7 +79,7 @@ int git_attr_file__from_buffer( } /* parse the next "pattern attr attr attr" line */ - if (!(error = git_attr_fnmatch__parse(&rule->match, &scan)) && + if (!(error = git_attr_fnmatch__parse(&rule->match, context, &scan)) && !(error = git_attr_assignment__parse(repo, &rule->assigns, &scan))) { if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) @@ -76,35 +101,30 @@ int git_attr_file__from_buffer( } } -cleanup: - if (error != GIT_SUCCESS) { - git_attr_rule__free(rule); - git_attr_file__free(attrs); - } else { - *out = attrs; - } + git_attr_rule__free(rule); + git__free(context); return error; } int git_attr_file__from_file( - git_repository *repo, const char *path, git_attr_file **out) + git_repository *repo, const char *path, git_attr_file *file) { int error = GIT_SUCCESS; git_fbuffer fbuf = GIT_FBUFFER_INIT; - *out = NULL; + assert(path && file); - if ((error = git_futils_readbuffer(&fbuf, path)) < GIT_SUCCESS || - (error = git_attr_file__from_buffer(repo, fbuf.data, out)) < GIT_SUCCESS) - { - git__rethrow(error, "Could not open attribute file '%s'", path); - } else { - /* save path (okay to fail) */ - (*out)->path = git__strdup(path); - } + if (file->path == NULL) + error = git_attr_file__set_path(repo, path, file); + + if (error == GIT_SUCCESS && + (error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS) + error = git_attr_file__from_buffer(repo, fbuf.data, file); git_futils_freebuffer(&fbuf); + if (error != GIT_SUCCESS) + git__rethrow(error, "Could not open attribute file '%s'", path); return error; } @@ -267,6 +287,7 @@ int git_attr_path__init( */ int git_attr_fnmatch__parse( git_attr_fnmatch *spec, + const char *source, const char **base) { const char *pattern, *scan; @@ -312,32 +333,40 @@ int git_attr_fnmatch__parse( *base = scan; spec->length = scan - pattern; - spec->pattern = git__strndup(pattern, spec->length); - - if (!spec->pattern) { - *base = git__next_line(pattern); - return GIT_ENOMEM; - } else { - /* remove '\' that might have be used for internal whitespace */ - char *from = spec->pattern, *to = spec->pattern; - while (*from) { - if (*from == '\\') { - from++; - spec->length--; - } - *to++ = *from++; - } - *to = '\0'; - } if (pattern[spec->length - 1] == '/') { spec->length--; - spec->pattern[spec->length] = '\0'; spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY; if (--slash_count <= 0) spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH; } + if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 && + source != NULL && git_path_root(pattern) < 0) + { + size_t sourcelen = strlen(source); + /* given an unrooted fullpath match from a file inside a repo, + * prefix the pattern with the relative directory of the source file + */ + spec->pattern = git__malloc(sourcelen + spec->length + 1); + if (spec->pattern) { + memcpy(spec->pattern, source, sourcelen); + memcpy(spec->pattern + sourcelen, pattern, spec->length); + spec->length += sourcelen; + spec->pattern[spec->length] = '\0'; + } + } else { + spec->pattern = git__strndup(pattern, spec->length); + } + + if (!spec->pattern) { + *base = git__next_line(pattern); + return GIT_ENOMEM; + } else { + /* remove '\' that might have be used for internal whitespace */ + spec->length = git__removechar(spec->pattern, '\\'); + } + return GIT_SUCCESS; } diff --git a/src/attr_file.h b/src/attr_file.h index 7190c4c7..304c7a85 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -62,13 +62,16 @@ typedef struct { * git_attr_file API */ +extern int git_attr_file__new(git_attr_file **attrs_ptr); +extern void git_attr_file__free(git_attr_file *file); + extern int git_attr_file__from_buffer( - git_repository *repo, const char *buf, git_attr_file **out); + git_repository *repo, const char *buf, git_attr_file *file); extern int git_attr_file__from_file( - git_repository *repo, const char *path, git_attr_file **out); + git_repository *repo, const char *path, git_attr_file *file); -extern int git_attr_file__new(git_attr_file **attrs_ptr); -extern void git_attr_file__free(git_attr_file *file); +extern int git_attr_file__set_path( + git_repository *repo, const char *path, git_attr_file *file); extern int git_attr_file__lookup_one( git_attr_file *file, @@ -90,6 +93,7 @@ extern unsigned long git_attr_file__name_hash(const char *name); extern int git_attr_fnmatch__parse( git_attr_fnmatch *spec, + const char *source, const char **base); extern int git_attr_fnmatch__match( diff --git a/src/ignore.c b/src/ignore.c index 1040574d..388a4b28 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -8,22 +8,25 @@ #define GIT_IGNORE_CONFIG "core.excludesfile" static int load_ignore_file( - git_repository *GIT_UNUSED(repo), const char *path, git_attr_file **out) + git_repository *repo, const char *path, git_attr_file *ignores) { int error = GIT_SUCCESS; git_fbuffer fbuf = GIT_FBUFFER_INIT; - git_attr_file *ignores = NULL; git_attr_fnmatch *match = NULL; const char *scan = NULL; + char *context = NULL; - GIT_UNUSED_ARG(repo); + if (ignores->path == NULL) + error = git_attr_file__set_path(repo, path, ignores); - *out = NULL; - - if ((error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS) - error = git_attr_file__new(&ignores); + if (git__suffixcmp(ignores->path, GIT_IGNORE_FILE) == 0) { + context = git__strndup(ignores->path, + strlen(ignores->path) - strlen(GIT_IGNORE_FILE)); + if (!context) error = GIT_ENOMEM; + } - ignores->path = git__strdup(path); + if (error == GIT_SUCCESS) + error = git_futils_readbuffer(&fbuf, path); scan = fbuf.data; @@ -33,7 +36,7 @@ static int load_ignore_file( break; } - if (!(error = git_attr_fnmatch__parse(match, &scan))) { + if (!(error = git_attr_fnmatch__parse(match, context, &scan))) { match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE; scan = git__next_line(scan); error = git_vector_insert(&ignores->rules, match); @@ -52,13 +55,10 @@ static int load_ignore_file( git_futils_freebuffer(&fbuf); git__free(match); + git__free(context); - if (error != GIT_SUCCESS) { + if (error != GIT_SUCCESS) git__rethrow(error, "Could not open ignore file '%s'", path); - git_attr_file__free(ignores); - } else { - *out = ignores; - } return error; } diff --git a/src/util.c b/src/util.c index 1ca9d850..f47de9e5 100644 --- a/src/util.c +++ b/src/util.c @@ -156,6 +156,23 @@ void git__strtolower(char *str) git__strntolower(str, strlen(str)); } +size_t git__removechar(char *str, char remove) +{ + char *from = str, *to = str; + + while (*from) { + if (*from == remove) + from++; + if (to != from) + *to = *from; + to++; + from++; + } + *to = '\0'; + + return (to - str); +} + int git__prefixcmp(const char *str, const char *prefix) { for (;;) { diff --git a/src/util.h b/src/util.h index 6c929cf0..818e6f0f 100644 --- a/src/util.h +++ b/src/util.h @@ -102,6 +102,8 @@ extern char *git__strtok(char **end, const char *sep); extern void git__strntolower(char *str, size_t len); extern void git__strtolower(char *str); +extern size_t git__removechar(char *str, char remove); + GIT_INLINE(const char *) git__next_line(const char *s) { while (*s && *s != '\n') s++; diff --git a/tests-clay/attr/file.c b/tests-clay/attr/file.c index acca0c65..652ee273 100644 --- a/tests-clay/attr/file.c +++ b/tests-clay/attr/file.c @@ -6,11 +6,12 @@ void test_attr_file__simple_read(void) { - git_attr_file *file = NULL; + git_attr_file *file; git_attr_assignment *assign; git_attr_rule *rule; - cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file)); cl_assert_strequal(cl_fixture("attr/attr0"), file->path); cl_assert(file->rules.length == 1); @@ -32,11 +33,12 @@ void test_attr_file__simple_read(void) void test_attr_file__match_variants(void) { - git_attr_file *file = NULL; + git_attr_file *file; git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file)); cl_assert_strequal(cl_fixture("attr/attr1"), file->path); cl_assert(file->rules.length == 10); @@ -119,11 +121,12 @@ static void check_one_assign( void test_attr_file__assign_variants(void) { - git_attr_file *file = NULL; + git_attr_file *file; git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file)); cl_assert_strequal(cl_fixture("attr/attr2"), file->path); cl_assert(file->rules.length == 11); @@ -184,11 +187,12 @@ void test_attr_file__assign_variants(void) void test_attr_file__check_attr_examples(void) { - git_attr_file *file = NULL; + git_attr_file *file; git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file)); cl_assert_strequal(cl_fixture("attr/attr3"), file->path); cl_assert(file->rules.length == 3); diff --git a/tests-clay/attr/lookup.c b/tests-clay/attr/lookup.c index fcade522..b251562b 100644 --- a/tests-clay/attr/lookup.c +++ b/tests-clay/attr/lookup.c @@ -3,11 +3,12 @@ void test_attr_lookup__simple(void) { - git_attr_file *file = NULL; + git_attr_file *file; git_attr_path path; const char *value = NULL; - cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file)); cl_assert_strequal(cl_fixture("attr/attr0"), file->path); cl_assert(file->rules.length == 1); @@ -60,7 +61,7 @@ static void run_test_cases(git_attr_file *file, test_case *cases) void test_attr_lookup__match_variants(void) { - git_attr_file *file = NULL; + git_attr_file *file; git_attr_path path; test_case cases[] = { /* pat0 -> simple match */ @@ -132,7 +133,8 @@ void test_attr_lookup__match_variants(void) { NULL, NULL, NULL, 0, 0 } }; - cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file)); cl_assert_strequal(cl_fixture("attr/attr1"), file->path); cl_assert(file->rules.length == 10); @@ -146,7 +148,7 @@ void test_attr_lookup__match_variants(void) void test_attr_lookup__assign_variants(void) { - git_attr_file *file = NULL; + git_attr_file *file; test_case cases[] = { /* pat0 -> simple assign */ { "pat0", "simple", GIT_ATTR_TRUE, 0, 0 }, @@ -190,7 +192,8 @@ void test_attr_lookup__assign_variants(void) { NULL, NULL, NULL, 0, 0 } }; - cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file)); cl_assert(file->rules.length == 11); run_test_cases(file, cases); @@ -200,7 +203,7 @@ void test_attr_lookup__assign_variants(void) void test_attr_lookup__check_attr_examples(void) { - git_attr_file *file = NULL; + git_attr_file *file; test_case cases[] = { { "foo.java", "diff", "java", 1, 0 }, { "foo.java", "crlf", GIT_ATTR_FALSE, 0, 0 }, @@ -224,7 +227,8 @@ void test_attr_lookup__check_attr_examples(void) { NULL, NULL, NULL, 0, 0 } }; - cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file)); cl_assert(file->rules.length == 3); run_test_cases(file, cases); @@ -234,7 +238,7 @@ void test_attr_lookup__check_attr_examples(void) void test_attr_lookup__from_buffer(void) { - git_attr_file *file = NULL; + git_attr_file *file; test_case cases[] = { { "abc", "foo", GIT_ATTR_TRUE, 0, 0 }, { "abc", "bar", GIT_ATTR_TRUE, 0, 0 }, @@ -248,7 +252,8 @@ void test_attr_lookup__from_buffer(void) { NULL, NULL, NULL, 0, 0 } }; - cl_git_pass(git_attr_file__from_buffer(NULL, "a* foo\nabc bar\n* baz", &file)); + cl_git_pass(git_attr_file__new(&file)); + cl_git_pass(git_attr_file__from_buffer(NULL, "a* foo\nabc bar\n* baz", file)); cl_assert(file->rules.length == 3); run_test_cases(file, cases); diff --git a/tests-clay/attr/repo.c b/tests-clay/attr/repo.c index 3e9b9de1..13f28ca8 100644 --- a/tests-clay/attr/repo.c +++ b/tests-clay/attr/repo.c @@ -45,19 +45,24 @@ void test_attr_repo__get_one(void) { "root_test3", "rootattr", NULL }, { "root_test3", "multiattr", "3" }, { "root_test3", "multi2", NULL }, - { "subdir/subdir_test1", "repoattr", GIT_ATTR_TRUE }, - { "subdir/subdir_test1", "rootattr", GIT_ATTR_TRUE }, - { "subdir/subdir_test1", "missingattr", NULL }, - { "subdir/subdir_test1", "subattr", "yes" }, - { "subdir/subdir_test1", "negattr", GIT_ATTR_FALSE }, - { "subdir/subdir_test1", "another", NULL }, - { "subdir/subdir_test2.txt", "repoattr", GIT_ATTR_TRUE }, - { "subdir/subdir_test2.txt", "rootattr", GIT_ATTR_TRUE }, - { "subdir/subdir_test2.txt", "missingattr", NULL }, - { "subdir/subdir_test2.txt", "subattr", "yes" }, - { "subdir/subdir_test2.txt", "negattr", GIT_ATTR_FALSE }, - { "subdir/subdir_test2.txt", "another", "one" }, + { "sub/subdir_test1", "repoattr", GIT_ATTR_TRUE }, + { "sub/subdir_test1", "rootattr", GIT_ATTR_TRUE }, + { "sub/subdir_test1", "missingattr", NULL }, + { "sub/subdir_test1", "subattr", "yes" }, + { "sub/subdir_test1", "negattr", GIT_ATTR_FALSE }, + { "sub/subdir_test1", "another", NULL }, + { "sub/subdir_test2.txt", "repoattr", GIT_ATTR_TRUE }, + { "sub/subdir_test2.txt", "rootattr", GIT_ATTR_TRUE }, + { "sub/subdir_test2.txt", "missingattr", NULL }, + { "sub/subdir_test2.txt", "subattr", "yes" }, + { "sub/subdir_test2.txt", "negattr", GIT_ATTR_FALSE }, + { "sub/subdir_test2.txt", "another", "zero" }, + { "sub/subdir_test2.txt", "reposub", GIT_ATTR_TRUE }, + { "sub/sub/subdir.txt", "another", "one" }, + { "sub/sub/subdir.txt", "reposubsub", GIT_ATTR_TRUE }, + { "sub/sub/subdir.txt", "reposub", NULL }, { "does-not-exist", "foo", "yes" }, + { "sub/deep/file", "deepdeep", GIT_ATTR_TRUE }, { NULL, NULL, NULL } }, *scan; @@ -105,7 +110,7 @@ void test_attr_repo__get_many(void) cl_assert(values[2] == NULL); cl_assert(values[3] == NULL); - cl_git_pass(git_attr_get_many(g_repo, "subdir/subdir_test1", 4, names, values)); + cl_git_pass(git_attr_get_many(g_repo, "sub/subdir_test1", 4, names, values)); cl_assert(values[0] == GIT_ATTR_TRUE); cl_assert(values[1] == GIT_ATTR_TRUE); @@ -136,33 +141,33 @@ void test_attr_repo__foreach(void) cl_assert(count == 2); count = 0; - cl_git_pass(git_attr_foreach(g_repo, "subdir/subdir_test1", + cl_git_pass(git_attr_foreach(g_repo, "sub/subdir_test1", &count_attrs, &count)); cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */ count = 0; - cl_git_pass(git_attr_foreach(g_repo, "subdir/subdir_test2.txt", + cl_git_pass(git_attr_foreach(g_repo, "sub/subdir_test2.txt", &count_attrs, &count)); - cl_assert(count == 5); /* repoattr, rootattr, subattr, negattr, another */ + cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */ } void test_attr_repo__manpage_example(void) { const char *value; - cl_git_pass(git_attr_get(g_repo, "subdir/abc", "foo", &value)); + cl_git_pass(git_attr_get(g_repo, "sub/abc", "foo", &value)); cl_assert(value == GIT_ATTR_TRUE); - cl_git_pass(git_attr_get(g_repo, "subdir/abc", "bar", &value)); + cl_git_pass(git_attr_get(g_repo, "sub/abc", "bar", &value)); cl_assert(value == NULL); - cl_git_pass(git_attr_get(g_repo, "subdir/abc", "baz", &value)); + cl_git_pass(git_attr_get(g_repo, "sub/abc", "baz", &value)); cl_assert(value == GIT_ATTR_FALSE); - cl_git_pass(git_attr_get(g_repo, "subdir/abc", "merge", &value)); + cl_git_pass(git_attr_get(g_repo, "sub/abc", "merge", &value)); cl_assert_strequal("filfre", value); - cl_git_pass(git_attr_get(g_repo, "subdir/abc", "frotz", &value)); + cl_git_pass(git_attr_get(g_repo, "sub/abc", "frotz", &value)); cl_assert(value == NULL); } diff --git a/tests-clay/status/ignore.c b/tests-clay/status/ignore.c new file mode 100644 index 00000000..f14e10c8 --- /dev/null +++ b/tests-clay/status/ignore.c @@ -0,0 +1,49 @@ +#include "clay_libgit2.h" +#include "fileops.h" +#include "git2/attr.h" + +static git_repository *g_repo = NULL; + +void test_status_ignore__initialize(void) +{ + /* Before each test, instantiate the attr repo from the fixtures and + * rename the .gitted to .git so it is a repo with a working dir. Also + * rename gitignore to .gitignore. + */ + cl_fixture_sandbox("attr"); + cl_git_pass(p_rename("attr/.gitted", "attr/.git")); + cl_git_pass(p_rename("attr/gitignore", "attr/.gitignore")); + cl_git_pass(git_repository_open(&g_repo, "attr/.git")); +} + +void test_status_ignore__cleanup(void) +{ + git_repository_free(g_repo); + g_repo = NULL; + cl_fixture_cleanup("attr"); +} + +void test_status_ignore__0(void) +{ + struct { + const char *path; + int expected; + } test_cases[] = { + { "file", 0 }, + { "ign", 1 }, + { "sub", 1 }, + { "sub/file", 0 }, + { "sub/ign", 1 }, + { "sub/sub", 1 }, + { "sub/sub/file", 0 }, + { "sub/sub/ign", 1 }, + { "sub/sub/sub", 1 }, + { NULL, 0 } + }, *one_test; + + for (one_test = test_cases; one_test->path != NULL; one_test++) { + int ignored; + cl_git_pass(git_status_should_ignore(g_repo, one_test->path, &ignored)); + cl_assert_(ignored == one_test->expected, one_test->path); + } +} diff --git a/tests/resources/attr/.gitted/info/attributes b/tests/resources/attr/.gitted/info/attributes index 2e9643a5..5fe62a37 100644 --- a/tests/resources/attr/.gitted/info/attributes +++ b/tests/resources/attr/.gitted/info/attributes @@ -1,2 +1,4 @@ * repoattr a* foo !bar -baz +sub/*.txt reposub +sub/sub/*.txt reposubsub diff --git a/tests/resources/attr/file b/tests/resources/attr/file new file mode 100644 index 00000000..45b983be --- /dev/null +++ b/tests/resources/attr/file @@ -0,0 +1 @@ +hi diff --git a/tests/resources/attr/gitignore b/tests/resources/attr/gitignore new file mode 100644 index 00000000..66f77694 --- /dev/null +++ b/tests/resources/attr/gitignore @@ -0,0 +1,2 @@ +sub +ign diff --git a/tests/resources/attr/ign b/tests/resources/attr/ign new file mode 100644 index 00000000..592fd259 --- /dev/null +++ b/tests/resources/attr/ign @@ -0,0 +1 @@ +ignore me diff --git a/tests/resources/attr/sub/.gitattributes b/tests/resources/attr/sub/.gitattributes new file mode 100644 index 00000000..329c1c5b --- /dev/null +++ b/tests/resources/attr/sub/.gitattributes @@ -0,0 +1,7 @@ +* subattr=yes -negattr +*.txt another=zero +sub/*.txt another=one +ab* merge=filfre +abc -foo -bar +*.c frotz +deep/file deepdeep diff --git a/tests/resources/attr/sub/abc b/tests/resources/attr/sub/abc new file mode 100644 index 00000000..3e42ffc5 --- /dev/null +++ b/tests/resources/attr/sub/abc @@ -0,0 +1,37 @@ +# Test file from gitattributes(5) example: + +If you have these three gitattributes file: + + (in $GIT_DIR/info/attributes) + + a* foo !bar -baz + + (in .gitattributes) + abc foo bar baz + + (in t/.gitattributes) + ab* merge=filfre + abc -foo -bar + *.c frotz + +the attributes given to path t/abc are computed as follows: + +1. By examining t/.gitattributes (which is in the same directory as the path + in question), git finds that the first line matches. merge attribute is + set. It also finds that the second line matches, and attributes foo and + bar are unset. +2. Then it examines .gitattributes (which is in the parent directory), and + finds that the first line matches, but t/.gitattributes file already + decided how merge, foo and bar attributes should be given to this path, + so it leaves foo and bar unset. Attribute baz is set. +3. Finally it examines $GIT_DIR/info/attributes. This file is used to + override the in-tree settings. The first line is a match, and foo is set, + bar is reverted to unspecified state, and baz is unset. + +As the result, the attributes assignment to t/abc becomes: + + foo set to true + bar unspecified + baz set to false + merge set to string value "filfre" + frotz unspecified diff --git a/tests/resources/attr/sub/file b/tests/resources/attr/sub/file new file mode 100644 index 00000000..45b983be --- /dev/null +++ b/tests/resources/attr/sub/file @@ -0,0 +1 @@ +hi diff --git a/tests/resources/attr/sub/ign b/tests/resources/attr/sub/ign new file mode 100644 index 00000000..592fd259 --- /dev/null +++ b/tests/resources/attr/sub/ign @@ -0,0 +1 @@ +ignore me diff --git a/tests/resources/attr/sub/sub/file b/tests/resources/attr/sub/sub/file new file mode 100644 index 00000000..45b983be --- /dev/null +++ b/tests/resources/attr/sub/sub/file @@ -0,0 +1 @@ +hi diff --git a/tests/resources/attr/sub/sub/subsub.txt b/tests/resources/attr/sub/sub/subsub.txt new file mode 100644 index 00000000..9e5bdc47 --- /dev/null +++ b/tests/resources/attr/sub/sub/subsub.txt @@ -0,0 +1 @@ +subsub diff --git a/tests/resources/attr/sub/subdir_test1 b/tests/resources/attr/sub/subdir_test1 new file mode 100644 index 00000000..e563cf47 --- /dev/null +++ b/tests/resources/attr/sub/subdir_test1 @@ -0,0 +1,2 @@ +Hello from the subdir + diff --git a/tests/resources/attr/sub/subdir_test2.txt b/tests/resources/attr/sub/subdir_test2.txt new file mode 100644 index 00000000..fb5067b1 --- /dev/null +++ b/tests/resources/attr/sub/subdir_test2.txt @@ -0,0 +1 @@ +Hello again diff --git a/tests/resources/attr/subdir/.gitattributes b/tests/resources/attr/subdir/.gitattributes deleted file mode 100644 index 99eae476..00000000 --- a/tests/resources/attr/subdir/.gitattributes +++ /dev/null @@ -1,5 +0,0 @@ -* subattr=yes -negattr -subdir/*.txt another=one -ab* merge=filfre -abc -foo -bar -*.c frotz diff --git a/tests/resources/attr/subdir/abc b/tests/resources/attr/subdir/abc deleted file mode 100644 index 3e42ffc5..00000000 --- a/tests/resources/attr/subdir/abc +++ /dev/null @@ -1,37 +0,0 @@ -# Test file from gitattributes(5) example: - -If you have these three gitattributes file: - - (in $GIT_DIR/info/attributes) - - a* foo !bar -baz - - (in .gitattributes) - abc foo bar baz - - (in t/.gitattributes) - ab* merge=filfre - abc -foo -bar - *.c frotz - -the attributes given to path t/abc are computed as follows: - -1. By examining t/.gitattributes (which is in the same directory as the path - in question), git finds that the first line matches. merge attribute is - set. It also finds that the second line matches, and attributes foo and - bar are unset. -2. Then it examines .gitattributes (which is in the parent directory), and - finds that the first line matches, but t/.gitattributes file already - decided how merge, foo and bar attributes should be given to this path, - so it leaves foo and bar unset. Attribute baz is set. -3. Finally it examines $GIT_DIR/info/attributes. This file is used to - override the in-tree settings. The first line is a match, and foo is set, - bar is reverted to unspecified state, and baz is unset. - -As the result, the attributes assignment to t/abc becomes: - - foo set to true - bar unspecified - baz set to false - merge set to string value "filfre" - frotz unspecified diff --git a/tests/resources/attr/subdir/subdir_test1 b/tests/resources/attr/subdir/subdir_test1 deleted file mode 100644 index e563cf47..00000000 --- a/tests/resources/attr/subdir/subdir_test1 +++ /dev/null @@ -1,2 +0,0 @@ -Hello from the subdir - diff --git a/tests/resources/attr/subdir/subdir_test2.txt b/tests/resources/attr/subdir/subdir_test2.txt deleted file mode 100644 index fb5067b1..00000000 --- a/tests/resources/attr/subdir/subdir_test2.txt +++ /dev/null @@ -1 +0,0 @@ -Hello again diff --git a/tests/resources/attr/subdir2/subdir2_test1 b/tests/resources/attr/subdir2/subdir2_test1 deleted file mode 100644 index dccada46..00000000 --- a/tests/resources/attr/subdir2/subdir2_test1 +++ /dev/null @@ -1 +0,0 @@ -Hello from subdir2 -- cgit v1.2.1 From 83bfbdf593a76c591bb9cbd40cec6fca36c81a9c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 16 Jan 2012 18:00:18 -0800 Subject: Remove poor git__removechar function Going back over this, the git__removechar function was not needed (only invoked once) and is actually mislabeled. As implemented, it really only made sense for removing backslash characters, since two of the "removed" characters in a row would include the second one -- i.e. it really implements stripping backslash-escaped strings where a backslash allows internal whitespace in a word. --- src/attr.c | 2 +- src/attr_file.c | 14 ++++++++++++-- src/util.c | 17 ----------------- src/util.h | 2 -- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/attr.c b/src/attr.c index 3fe76d12..fa1a4f12 100644 --- a/src/attr.c +++ b/src/attr.c @@ -232,7 +232,7 @@ int git_attr_cache__push_file( file = git_hashtable_lookup(cache->files, filename); if (file == NULL && git_futils_exists(filename) == GIT_SUCCESS) { if ((error = git_attr_file__new(&file)) == GIT_SUCCESS) - error = (*loader)(repo, filename, file); + error = loader(repo, filename, file); add_to_cache = (error == GIT_SUCCESS); } diff --git a/src/attr_file.c b/src/attr_file.c index f6eaad69..4303c766 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -363,8 +363,18 @@ int git_attr_fnmatch__parse( *base = git__next_line(pattern); return GIT_ENOMEM; } else { - /* remove '\' that might have be used for internal whitespace */ - spec->length = git__removechar(spec->pattern, '\\'); + /* strip '\' that might have be used for internal whitespace */ + char *to = spec->pattern; + for (scan = spec->pattern; *scan; to++, scan++) { + if (*scan == '\\') + scan++; /* skip '\' but include next char */ + if (to != scan) + *to = *scan; + } + if (to != scan) { + *to = '\0'; + spec->length = (to - spec->pattern); + } } return GIT_SUCCESS; diff --git a/src/util.c b/src/util.c index f47de9e5..1ca9d850 100644 --- a/src/util.c +++ b/src/util.c @@ -156,23 +156,6 @@ void git__strtolower(char *str) git__strntolower(str, strlen(str)); } -size_t git__removechar(char *str, char remove) -{ - char *from = str, *to = str; - - while (*from) { - if (*from == remove) - from++; - if (to != from) - *to = *from; - to++; - from++; - } - *to = '\0'; - - return (to - str); -} - int git__prefixcmp(const char *str, const char *prefix) { for (;;) { diff --git a/src/util.h b/src/util.h index 818e6f0f..6c929cf0 100644 --- a/src/util.h +++ b/src/util.h @@ -102,8 +102,6 @@ extern char *git__strtok(char **end, const char *sep); extern void git__strntolower(char *str, size_t len); extern void git__strtolower(char *str); -extern size_t git__removechar(char *str, char remove); - GIT_INLINE(const char *) git__next_line(const char *s) { while (*s && *s != '\n') s++; -- cgit v1.2.1