diff options
38 files changed, 612 insertions, 221 deletions
diff --git a/include/git2/attr.h b/include/git2/attr.h index d585937b7..f4c5975a6 100644 --- a/include/git2/attr.h +++ b/include/git2/attr.h @@ -50,6 +50,30 @@ GIT_EXTERN(int) git_attr_foreach( int (*callback)(const char *name, const char *value, void *payload), void *payload); +/** + * Flush the gitattributes cache. + * + * Call this if you have reason to believe that the attributes files + * on disk no longer match the cached contents of memory. + */ +GIT_EXTERN(void) git_attr_cache_flush( + git_repository *repo); + +/** + * Add a macro definition. + * + * Macros will automatically be loaded from the top level .gitattributes + * file of the repository (plus the build-in "binary" macro). This + * function allows you to add others. For example, to add the default + * macro, you would call: + * + * git_attr_add_macro(repo, "binary", "-diff -crlf"); + */ +GIT_EXTERN(int) git_attr_add_macro( + git_repository *repo, + const char *name, + const char *values); + /** @} */ GIT_END_DECL #endif diff --git a/src/attr.c b/src/attr.c index d8e7095b1..fac2fa4b9 100644 --- a/src/attr.c +++ b/src/attr.c @@ -1,19 +1,17 @@ -#include "attr.h" -#include "buffer.h" +#include "repository.h" #include "fileops.h" #include "config.h" #include <ctype.h> #define GIT_ATTR_FILE_INREPO "info/attributes" #define GIT_ATTR_FILE ".gitattributes" -#define GIT_ATTR_FILE_SYSTEM "/etc/gitattributes" -#if GIT_WIN32 -#define GIT_ATTR_FILE_WIN32 L"%PROGRAMFILES%\\Git\\etc\\gitattributes" -#endif +#define GIT_ATTR_FILE_SYSTEM "gitattributes" static int collect_attr_files( git_repository *repo, const char *path, git_vector *files); +static int attr_cache_init(git_repository *repo); + int git_attr_get( git_repository *repo, const char *pathname, @@ -180,6 +178,44 @@ cleanup: } +int git_attr_add_macro( + git_repository *repo, + const char *name, + const char *values) +{ + int error; + git_attr_rule *macro = NULL; + + if ((error = attr_cache_init(repo)) < GIT_SUCCESS) + return error; + + macro = git__calloc(1, sizeof(git_attr_rule)); + if (!macro) + return GIT_ENOMEM; + + macro->match.pattern = git__strdup(name); + if (!macro->match.pattern) { + git__free(macro); + return GIT_ENOMEM; + } + + macro->match.length = strlen(macro->match.pattern); + macro->match.flags = GIT_ATTR_FNMATCH_MACRO; + + error = git_attr_assignment__parse(repo, ¯o->assigns, &values); + + if (error == GIT_SUCCESS) + error = git_attr_cache__insert_macro(repo, macro); + + if (error < GIT_SUCCESS) { + git_attr_rule__free(macro); + git__free(macro); + } + + return error; +} + + /* add git_attr_file to vector of files, loading if needed */ static int push_attrs( git_repository *repo, @@ -193,13 +229,6 @@ static int push_attrs( git_attr_file *file; int add_to_cache = 0; - if (cache->files == NULL) { - cache->files = git_hashtable_alloc( - 8, git_hash__strhash_cb, git_hash__strcmp_cb); - if (!cache->files) - return git__throw(GIT_ENOMEM, "Could not create attribute cache"); - } - if ((error = git_path_prettify(&path, filename, base)) < GIT_SUCCESS) { if (error == GIT_EOSERR) /* file was not found -- ignore error */ @@ -210,7 +239,7 @@ static int push_attrs( /* either get attr_file from cache or read from disk */ file = git_hashtable_lookup(cache->files, path.ptr); if (file == NULL) { - error = git_attr_file__from_file(&file, path.ptr); + error = git_attr_file__from_file(repo, path.ptr, &file); add_to_cache = (error == GIT_SUCCESS); } @@ -238,6 +267,9 @@ static int collect_attr_files( git_config *cfg; const char *workdir = git_repository_workdir(repo); + if ((error = attr_cache_init(repo)) < GIT_SUCCESS) + goto cleanup; + if ((error = git_vector_init(files, 4, NULL)) < GIT_SUCCESS) goto cleanup; @@ -288,8 +320,13 @@ static int collect_attr_files( git_config_free(cfg); } - if (error == GIT_SUCCESS) - error = push_attrs(repo, files, NULL, GIT_ATTR_FILE_SYSTEM); + if (error == GIT_SUCCESS) { + error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); + if (error == GIT_SUCCESS) + error = push_attrs(repo, files, NULL, dir.ptr); + else if (error == GIT_ENOTFOUND) + error = GIT_SUCCESS; + } cleanup: if (error < GIT_SUCCESS) { @@ -302,10 +339,64 @@ static int collect_attr_files( } -void git_repository__attr_cache_free(git_attr_cache *attrs) +static int attr_cache_init(git_repository *repo) { - if (attrs && attrs->files) { - git_hashtable_free(attrs->files); - attrs->files = NULL; + int error = GIT_SUCCESS; + git_attr_cache *cache = &repo->attrcache; + + if (cache->initialized) + return GIT_SUCCESS; + + if (cache->files == NULL) { + cache->files = git_hashtable_alloc( + 8, git_hash__strhash_cb, git_hash__strcmp_cb); + if (!cache->files) + return git__throw(GIT_ENOMEM, "Could not initialize attribute cache"); + } + + if (cache->macros == NULL) { + cache->macros = git_hashtable_alloc( + 8, git_hash__strhash_cb, git_hash__strcmp_cb); + if (!cache->macros) + return git__throw(GIT_ENOMEM, "Could not initialize attribute cache"); } + + cache->initialized = 1; + + /* insert default macros */ + error = git_attr_add_macro(repo, "binary", "-diff -crlf"); + + return error; +} + + +void git_attr_cache_flush( + git_repository *repo) +{ + if (!repo) + return; + + if (repo->attrcache.files) { + const void *GIT_UNUSED(name); + git_attr_file *file; + + GIT_HASHTABLE_FOREACH(repo->attrcache.files, name, file, + git_attr_file__free(file)); + + git_hashtable_free(repo->attrcache.files); + repo->attrcache.files = NULL; + } + + if (repo->attrcache.macros) { + const void *GIT_UNUSED(name); + git_attr_rule *rule; + + GIT_HASHTABLE_FOREACH(repo->attrcache.macros, name, rule, + git_attr_rule__free(rule)); + + git_hashtable_free(repo->attrcache.macros); + repo->attrcache.macros = NULL; + } + + repo->attrcache.initialized = 0; } diff --git a/src/attr.h b/src/attr.h deleted file mode 100644 index 518fb9d3b..000000000 --- a/src/attr.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2009-2011 the libgit2 contributors - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#ifndef INCLUDE_attr_h__ -#define INCLUDE_attr_h__ - -#include "hashtable.h" -#include "attr_file.h" - -/* EXPORT */ -typedef struct { - git_hashtable *files; /* hash path to git_attr_file */ -} git_attr_cache; - -extern void git_repository__attr_cache_free(git_attr_cache *attrs); - -#endif - diff --git a/src/attr_file.c b/src/attr_file.c index 5d159db00..0b1eb1f67 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -1,17 +1,33 @@ #include "common.h" -#include "attr_file.h" +#include "repository.h" #include "filebuf.h" #include <ctype.h> const char *git_attr__true = "[internal]__TRUE__"; const char *git_attr__false = "[internal]__FALSE__"; -static int parse_fnmatch(git_attr_fnmatch *spec, const char **base); -static int parse_assigns(git_vector *assigns, const char **base); -static int free_rule(git_attr_rule *rule); +static int git_attr_fnmatch__parse(git_attr_fnmatch *spec, const char **base); static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); -int git_attr_file__from_buffer(git_attr_file **out, const char *buffer) +int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) +{ + unsigned int i; + git_attr_assignment *assign; + + if (macro->assigns.length == 0) + return git__throw(GIT_EMISSINGOBJDATA, "git attribute macro with no values"); + + git_vector_foreach(¯o->assigns, i, assign) { + GIT_REFCOUNT_OWN(assign, macro); + GIT_REFCOUNT_INC(assign); + } + + return git_hashtable_insert( + repo->attrcache.macros, macro->match.pattern, macro); +} + +int git_attr_file__from_buffer( + git_repository *repo, const char *buffer, git_attr_file **out) { int error = GIT_SUCCESS; git_attr_file *attrs = NULL; @@ -42,13 +58,21 @@ int git_attr_file__from_buffer(git_attr_file **out, const char *buffer) } /* parse the next "pattern attr attr attr" line */ - if (!(error = parse_fnmatch(&rule->match, &scan)) && - !(error = parse_assigns(&rule->assigns, &scan))) - error = git_vector_insert(&attrs->rules, rule); + if (!(error = git_attr_fnmatch__parse(&rule->match, &scan)) && + !(error = git_attr_assignment__parse(repo, &rule->assigns, &scan))) + { + if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) + /* should generate error/warning if this is coming from any + * file other than .gitattributes at repo root. + */ + error = git_attr_cache__insert_macro(repo, rule); + else + error = git_vector_insert(&attrs->rules, rule); + } /* if the rule wasn't a pattern, on to the next */ if (error != GIT_SUCCESS) { - free_rule(rule); /* release anything partially allocated */ + git_attr_rule__free(rule); /* free anything partially allocated */ if (error == GIT_ENOTFOUND) error = GIT_SUCCESS; } else { @@ -58,6 +82,7 @@ int git_attr_file__from_buffer(git_attr_file **out, const char *buffer) cleanup: if (error != GIT_SUCCESS) { + git__free(rule); git_attr_file__free(attrs); git__free(attrs); } else { @@ -67,7 +92,8 @@ cleanup: return error; } -int git_attr_file__from_file(git_attr_file **out, const char *path) +int git_attr_file__from_file( + git_repository *repo, const char *path, git_attr_file **out) { int error = GIT_SUCCESS; git_fbuffer fbuf = GIT_FBUFFER_INIT; @@ -75,7 +101,7 @@ int git_attr_file__from_file(git_attr_file **out, const char *path) *out = NULL; if ((error = git_futils_readbuffer(&fbuf, path)) < GIT_SUCCESS || - (error = git_attr_file__from_buffer(out, fbuf.data)) < 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 { @@ -97,7 +123,7 @@ void git_attr_file__free(git_attr_file *file) return; git_vector_foreach(&file->rules, i, rule) { - free_rule(rule); + git_attr_rule__free(rule); } git_vector_free(&file->rules); @@ -153,15 +179,15 @@ int git_attr_rule__match_path( { int matched = FNM_NOMATCH; - if (rule->match.directory && !path->is_dir) + if (rule->match.flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir) return matched; - if (rule->match.fullpath) + if (rule->match.flags & GIT_ATTR_FNMATCH_FULLPATH) matched = p_fnmatch(rule->match.pattern, path->path, FNM_PATHNAME); else matched = p_fnmatch(rule->match.pattern, path->basename, 0); - if (rule->match.negative) + if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) matched = (matched == GIT_SUCCESS) ? FNM_NOMATCH : GIT_SUCCESS; return matched; @@ -232,7 +258,7 @@ int git_attr_path__init( * GIT_ENOTFOUND if the fnmatch does not require matching, or * another error code there was an actual problem. */ -static int parse_fnmatch( +static int git_attr_fnmatch__parse( git_attr_fnmatch *spec, const char **base) { @@ -251,21 +277,31 @@ static int parse_fnmatch( goto skip_to_eol; } + spec->flags = 0; + + if (*pattern == '[') { + if (strncmp(pattern, "[attr]", 6) == 0) { + spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO; + pattern += 6; + } else { + /* unrecognized meta instructions - skip the line */ + error = GIT_ENOTFOUND; + goto skip_to_eol; + } + } + if (*pattern == '!') { - spec->negative = 1; + spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE; pattern++; - } else { - spec->negative = 0; } - spec->fullpath = 0; slash_count = 0; for (scan = pattern; *scan != '\0'; ++scan) { if (isspace(*scan) && *(scan - 1) != '\\') break; if (*scan == '/') { - spec->fullpath = 1; + spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH; slash_count++; } } @@ -292,11 +328,9 @@ static int parse_fnmatch( if (pattern[spec->length - 1] == '/') { spec->length--; spec->pattern[spec->length] = '\0'; - spec->directory = 1; + spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY; if (--slash_count <= 0) - spec->fullpath = 0; - } else { - spec->directory = 0; + spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH; } return GIT_SUCCESS; @@ -323,7 +357,21 @@ static int sort_by_hash_and_name(const void *a_raw, const void *b_raw) return strcmp(b->name, a->name); } -static int parse_assigns( +static void free_assign(git_attr_assignment *assign) +{ + git__free(assign->name); + assign->name = NULL; + + if (assign->is_allocated) { + git__free((void *)assign->value); + assign->value = NULL; + } + + git__free(assign); +} + +int git_attr_assignment__parse( + git_repository *repo, git_vector *assigns, const char **base) { @@ -333,7 +381,7 @@ static int parse_assigns( assert(assigns && !assigns->length); - while (*scan && *scan != '\n') { + while (*scan && *scan != '\n' && error == GIT_SUCCESS) { const char *name_start, *value_start; /* skip leading blanks */ @@ -369,8 +417,7 @@ static int parse_assigns( ((assign->name_hash << 5) + assign->name_hash) + *scan; scan++; } - assign->name_len = scan - name_start; - if (assign->name_len <= 0) { + if (scan == name_start) { /* must have found lone prefix (" - ") or leading = ("=foo") * or end of buffer -- advance until whitespace and continue */ @@ -378,6 +425,13 @@ static int parse_assigns( continue; } + /* allocate permanent storage for name */ + assign->name = git__strndup(name_start, scan - name_start); + if (!assign->name) { + error = GIT_ENOMEM; + break; + } + /* if there is an equals sign, find the value */ if (*scan == '=') { for (value_start = ++scan; *scan && !isspace(*scan); ++scan); @@ -394,11 +448,34 @@ static int parse_assigns( } } - /* allocate permanent storage for name */ - assign->name = git__strndup(name_start, assign->name_len); - if (!assign->name) { - error = GIT_ENOMEM; - break; + /* expand macros (if given a repo) */ + if (repo != NULL) { + git_attr_rule *macro = + git_hashtable_lookup(repo->attrcache.macros, assign->name); + + if (macro != NULL) { + unsigned int i; + git_attr_assignment *massign; + + /* issue warning: if assign->value != GIT_ATTR_TRUE */ + + git__free(assign->name); + assign->name = NULL; + if (assign->is_allocated) { + git__free((void *)assign->value); + assign->value = NULL; + } + + git_vector_foreach(¯o->assigns, i, massign) { + error = git_vector_insert(assigns, massign); + if (error != GIT_SUCCESS) + break; + GIT_REFCOUNT_INC(&massign->rc); + } + + /* continue to next assignment */ + continue; + } } /* insert allocated assign into vector */ @@ -417,40 +494,34 @@ static int parse_assigns( git_vector_sort(assigns); } - if (assign != NULL) { - git__free(assign->name); - if (assign->is_allocated) - git__free((void *)assign->value); - git__free(assign); - } + if (assign != NULL) + free_assign(assign); while (*scan && *scan != '\n') scan++; + if (*scan == '\n') scan++; + *base = scan; return error; } -static int free_rule(git_attr_rule *rule) +void git_attr_rule__free(git_attr_rule *rule) { unsigned int i; git_attr_assignment *assign; if (!rule) - return GIT_SUCCESS; + return; git__free(rule->match.pattern); rule->match.pattern = NULL; rule->match.length = 0; git_vector_foreach(&rule->assigns, i, assign) { - git__free(assign->name); - assign->name = NULL; - - if (assign->is_allocated) { - git__free((void *)assign->value); - assign->value = NULL; - } + if (GIT_REFCOUNT_OWNER(assign) == rule) + GIT_REFCOUNT_OWN(assign, NULL); + GIT_REFCOUNT_DEC(assign, free_assign); } - return GIT_SUCCESS; + git_vector_free(&rule->assigns); } diff --git a/src/attr_file.h b/src/attr_file.h index 4774f148c..bed440d61 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -9,36 +9,41 @@ #include "git2/attr.h" #include "vector.h" +#include "hashtable.h" + +#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0) +#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1) +#define GIT_ATTR_FNMATCH_FULLPATH (1U << 2) +#define GIT_ATTR_FNMATCH_MACRO (1U << 3) typedef struct { char *pattern; size_t length; - int negative; - int directory; - int fullpath; + unsigned int flags; } git_attr_fnmatch; typedef struct { + git_refcount unused; const char *name; - unsigned long name_hash; + unsigned long name_hash; } git_attr_name; typedef struct { + git_refcount rc; /* for macros */ char *name; unsigned long name_hash; - size_t name_len; const char *value; int is_allocated; } git_attr_assignment; typedef struct { git_attr_fnmatch match; - git_vector assigns; /* <git_attr_assignment*> */ + git_vector assigns; /* vector of <git_attr_assignment*> */ } git_attr_rule; typedef struct { - char *path; - git_vector rules; /* <git_attr_rule*> */ + char *path; /* cache the path this was loaded from */ + git_vector rules; /* vector of <git_attr_rule*> */ } git_attr_file; typedef struct { @@ -47,12 +52,20 @@ typedef struct { int is_dir; } git_attr_path; +typedef struct { + int initialized; + git_hashtable *files; /* hash path to git_attr_file */ + git_hashtable *macros; /* hash name to vector<git_attr_assignment> */ +} git_attr_cache; + /* * git_attr_file API */ -extern int git_attr_file__from_buffer(git_attr_file **out, const char *buf); -extern int git_attr_file__from_file(git_attr_file **out, const char *path); +extern int git_attr_file__from_buffer( + git_repository *repo, const char *buf, git_attr_file **out); +extern int git_attr_file__from_file( + git_repository *repo, const char *path, git_attr_file **out); extern void git_attr_file__free(git_attr_file *file); @@ -74,6 +87,8 @@ extern unsigned long git_attr_file__name_hash(const char *name); * other utilities */ +extern void git_attr_rule__free(git_attr_rule *rule); + extern int git_attr_rule__match_path( git_attr_rule *rule, const git_attr_path *path); @@ -84,4 +99,12 @@ extern git_attr_assignment *git_attr_rule__lookup_assignment( extern int git_attr_path__init( git_attr_path *info, const char *path); +extern int git_attr_assignment__parse( + git_repository *repo, /* needed to expand macros */ + git_vector *assigns, + const char **scan); + +extern int git_attr_cache__insert_macro( + git_repository *repo, git_attr_rule *macro); + #endif diff --git a/src/config.c b/src/config.c index ed7c947ed..29f1ee27c 100644 --- a/src/config.c +++ b/src/config.c @@ -337,6 +337,11 @@ int git_config_get_string(git_config *cfg, const char *name, const char **out) return git__throw(error, "Config value '%s' not found", name); } +int git_config_find_global_r(git_buf *path) +{ + return git_futils_find_global_file(path, GIT_CONFIG_FILENAME); +} + int git_config_find_global(char *global_config_path) { git_buf path = GIT_BUF_INIT; @@ -354,79 +359,9 @@ int git_config_find_global(char *global_config_path) return error; } -int git_config_find_global_r(git_buf *path) +int git_config_find_system_r(git_buf *path) { - int error; - const char *home = getenv("HOME"); - -#ifdef GIT_WIN32 - if (home == NULL) - home = getenv("USERPROFILE"); -#endif - - if (home == NULL) - return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory"); - - if ((error = git_buf_joinpath(path, home, GIT_CONFIG_FILENAME)) < GIT_SUCCESS) - return error; - - if (git_futils_exists(path->ptr) < GIT_SUCCESS) { - git_buf_clear(path); - return git__throw(GIT_EOSERR, "Failed to open global config file. The file does not exist"); - } - - return GIT_SUCCESS; -} - - - -#if GIT_WIN32 -static int win32_find_system(git_buf *system_config_path) -{ - const wchar_t *query = L"%PROGRAMFILES%\\Git\\etc\\gitconfig"; - wchar_t *apphome_utf16; - char *apphome_utf8; - DWORD size, ret; - - size = ExpandEnvironmentStringsW(query, NULL, 0); - /* The function gave us the full size of the buffer in chars, including NUL */ - apphome_utf16 = git__malloc(size * sizeof(wchar_t)); - if (apphome_utf16 == NULL) - return GIT_ENOMEM; - - ret = ExpandEnvironmentStringsW(query, apphome_utf16, size); - if (ret != size) - return git__throw(GIT_ERROR, "Failed to expand environment strings"); - - if (_waccess(apphome_utf16, F_OK) < 0) { - git__free(apphome_utf16); - return GIT_ENOTFOUND; - } - - apphome_utf8 = gitwin_from_utf16(apphome_utf16); - git__free(apphome_utf16); - - git_buf_attach(system_config_path, apphome_utf8, 0); - - return GIT_SUCCESS; -} -#endif - -int git_config_find_system_r(git_buf *system_config_path) -{ - if (git_buf_sets(system_config_path, "/etc/gitconfig") < GIT_SUCCESS) - return git_buf_lasterror(system_config_path); - - if (git_futils_exists(system_config_path->ptr) == GIT_SUCCESS) - return GIT_SUCCESS; - - git_buf_clear(system_config_path); - -#if GIT_WIN32 - return win32_find_system(system_config_path); -#else - return GIT_ENOTFOUND; -#endif + return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM); } int git_config_find_system(char *system_config_path) diff --git a/src/config.h b/src/config.h index fc639c6d4..6345b0a5d 100644 --- a/src/config.h +++ b/src/config.h @@ -14,6 +14,7 @@ #define GIT_CONFIG_FILENAME ".gitconfig" #define GIT_CONFIG_FILENAME_INREPO "config" +#define GIT_CONFIG_FILENAME_SYSTEM "gitconfig" #define GIT_CONFIG_FILE_MODE 0666 struct git_config { diff --git a/src/fileops.c b/src/fileops.c index fb2f954d7..5eb7bf6ec 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -403,3 +403,134 @@ int git_futils_contains_file(git_buf *base, const char *file, int append_if_exis return _check_dir_contents(base, file, append_if_exists, &git_futils_isfile); } +int git_futils_find_global_file(git_buf *path, const char *filename) +{ + int error; + const char *home = getenv("HOME"); + +#ifdef GIT_WIN32 + if (home == NULL) + home = getenv("USERPROFILE"); +#endif + + if (home == NULL) + return git__throw(GIT_EOSERR, "Failed to open global %s file. " + "Cannot locate the user's home directory.", filename); + + if ((error = git_buf_joinpath(path, home, filename)) < GIT_SUCCESS) + return error; + + if (git_futils_exists(path->ptr) < GIT_SUCCESS) { + git_buf_clear(path); + return GIT_ENOTFOUND; + } + + return GIT_SUCCESS; +} + +#ifdef GIT_WIN32 +typedef struct { + wchar_t *path; + DWORD len; +} win32_path; + +static const win32_path *win32_system_root(void) +{ + static win32_path s_root = { 0, 0 }; + + if (s_root.path == NULL) { + const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\"; + + s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0); + + if (s_root.len <= 0) { + git__throw(GIT_EOSERR, "Failed to expand environment strings"); + return NULL; + } + + s_root.path = git__calloc(s_root.len, sizeof(wchar_t)); + if (s_root.path == NULL) + return NULL; + + if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) { + git__throw(GIT_EOSERR, "Failed to expand environment strings"); + git__free(s_root.path); + s_root.path = NULL; + return NULL; + } + } + + return &s_root; +} + +static int win32_find_system_file(git_buf *path, const char *filename) +{ + int error = GIT_SUCCESS; + const win32_path *root = win32_system_root(); + size_t len; + wchar_t *file_utf16 = NULL, *scan; + char *file_utf8 = NULL; + + if (!root || !filename || (len = strlen(filename)) == 0) + return GIT_ENOTFOUND; + + /* allocate space for wchar_t path to file */ + file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t)); + if (!file_utf16) + return GIT_ENOMEM; + + /* append root + '\\' + filename as wchar_t */ + memcpy(file_utf16, root->path, root->len * sizeof(wchar_t)); + + if (*filename == '/' || *filename == '\\') + filename++; + + if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) != + (int)len) { + error = git__throw(GIT_EOSERR, "Failed to build file path"); + goto cleanup; + } + + for (scan = file_utf16; *scan; scan++) + if (*scan == L'/') + *scan = L'\\'; + + /* check access */ + if (_waccess(file_utf16, F_OK) < 0) { + error = GIT_ENOTFOUND; + goto cleanup; + } + + /* convert to utf8 */ + if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL) + error = GIT_ENOMEM; + + if (file_utf8) { + git_path_mkposix(file_utf8); + git_buf_attach(path, file_utf8, 0); + } + +cleanup: + git__free(file_utf16); + + return error; +} +#endif + +int git_futils_find_system_file(git_buf *path, const char *filename) +{ + if (git_buf_joinpath(path, "/etc", filename) < GIT_SUCCESS) + return git_buf_lasterror(path); + + if (git_futils_exists(path->ptr) == GIT_SUCCESS) + return GIT_SUCCESS; + + git_buf_clear(path); + +#ifdef GIT_WIN32 + return win32_find_system_file(path, filename); +#else + return GIT_ENOTFOUND; +#endif +} + diff --git a/src/fileops.h b/src/fileops.h index df135d0db..31f3e6a91 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -165,4 +165,28 @@ extern int git_futils_direach( extern int git_futils_cmp_path(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2); +/** + * Find a "global" file (i.e. one in a user's home directory). + * + * @param pathbuf buffer to write the full path into + * @param filename name of file to find in the home directory + * @return + * - GIT_SUCCESS if found; + * - GIT_ENOTFOUND if not found; + * - GIT_EOSERR on an unspecified OS related error. + */ +extern int git_futils_find_global_file(git_buf *path, const char *filename); + +/** + * Find a "system" file (i.e. one shared for all users of the system). + * + * @param pathbuf buffer to write the full path into + * @param filename name of file to find in the home directory + * @return + * - GIT_SUCCESS if found; + * - GIT_ENOTFOUND if not found; + * - GIT_EOSERR on an unspecified OS related error. + */ +extern int git_futils_find_system_file(git_buf *path, const char *filename); + #endif /* INCLUDE_fileops_h__ */ diff --git a/src/repository.c b/src/repository.c index e0d4c6387..a94ecce55 100644 --- a/src/repository.c +++ b/src/repository.c @@ -59,7 +59,7 @@ void git_repository_free(git_repository *repo) git_cache_free(&repo->objects); git_repository__refcache_free(&repo->references); - git_repository__attr_cache_free(&repo->attrcache); + git_attr_cache_flush(repo); git__free(repo->path_repository); git__free(repo->workdir); diff --git a/src/repository.h b/src/repository.h index 5274fc1d0..82052158a 100644 --- a/src/repository.h +++ b/src/repository.h @@ -19,7 +19,7 @@ #include "refs.h" #include "buffer.h" #include "odb.h" -#include "attr.h" +#include "attr_file.h" #define DOT_GIT ".git" #define GIT_DIR DOT_GIT "/" diff --git a/src/util.h b/src/util.h index be978a6a5..4b1104b7b 100644 --- a/src/util.h +++ b/src/util.h @@ -109,7 +109,6 @@ extern void **git__bsearch(const void *key, void **base, size_t nmemb, int (*compar)(const void *, const void *)); extern int git__strcmp_cb(const void *a, const void *b); -extern uint32_t git__strhash_cb(const void *key, int hash_id); typedef struct { short refcount; diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c index b41c78f92..b1b838eb7 100644 --- a/src/win32/utf-conv.c +++ b/src/win32/utf-conv.c @@ -57,6 +57,11 @@ wchar_t* gitwin_to_utf16(const char* str) return ret; } +int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len) +{ + return MultiByteToWideChar(_active_codepage, 0, str, -1, buffer, len); +} + char* gitwin_from_utf16(const wchar_t* str) { char* ret; diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h index da03e3385..bbb5c4f69 100644 --- a/src/win32/utf-conv.h +++ b/src/win32/utf-conv.h @@ -11,6 +11,7 @@ #define INCLUDE_git_utfconv_h__ wchar_t* gitwin_to_utf16(const char* str); +int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len) char* gitwin_from_utf16(const wchar_t* str); #endif diff --git a/tests-clay/attr/file.c b/tests-clay/attr/file.c index 0a5bff59d..d9e2d5701 100644 --- a/tests-clay/attr/file.c +++ b/tests-clay/attr/file.c @@ -8,7 +8,7 @@ void test_attr_file__simple_read(void) { git_attr_file *file = NULL; - cl_git_pass(git_attr_file__from_file(&file, cl_fixture("attr/attr0"))); + 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); @@ -16,15 +16,12 @@ void test_attr_file__simple_read(void) cl_assert(rule != NULL); cl_assert_strequal("*", rule->match.pattern); cl_assert(rule->match.length == 1); - cl_assert(!rule->match.negative); - cl_assert(!rule->match.directory); - cl_assert(!rule->match.fullpath); + cl_assert(rule->match.flags == 0); cl_assert(rule->assigns.length == 1); git_attr_assignment *assign = get_assign(rule, 0); cl_assert(assign != NULL); cl_assert_strequal("binary", assign->name); - cl_assert(assign->name_len == 6); cl_assert(assign->value == GIT_ATTR_TRUE); cl_assert(!assign->is_allocated); @@ -37,7 +34,7 @@ void test_attr_file__match_variants(void) git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__from_file(&file, cl_fixture("attr/attr1"))); + 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); @@ -48,38 +45,31 @@ void test_attr_file__match_variants(void) cl_assert(rule); cl_assert_strequal("pat0", rule->match.pattern); cl_assert(rule->match.length == strlen("pat0")); - cl_assert(!rule->match.negative); - cl_assert(!rule->match.directory); - cl_assert(!rule->match.fullpath); + cl_assert(rule->match.flags == 0); cl_assert(rule->assigns.length == 1); assign = get_assign(rule,0); cl_assert_strequal("attr0", assign->name); cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name)); - cl_assert(assign->name_len == strlen("attr0")); cl_assert(assign->value == GIT_ATTR_TRUE); cl_assert(!assign->is_allocated); rule = get_rule(1); cl_assert_strequal("pat1", rule->match.pattern); cl_assert(rule->match.length == strlen("pat1")); - cl_assert(rule->match.negative); + cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_NEGATIVE); rule = get_rule(2); cl_assert_strequal("pat2", rule->match.pattern); cl_assert(rule->match.length == strlen("pat2")); - cl_assert(rule->match.directory); - cl_assert(!rule->match.fullpath); + cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_DIRECTORY); rule = get_rule(3); cl_assert_strequal("pat3dir/pat3file", rule->match.pattern); - cl_assert(!rule->match.directory); - cl_assert(rule->match.fullpath); + cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_FULLPATH); rule = get_rule(4); cl_assert_strequal("pat4.*", rule->match.pattern); - cl_assert(!rule->match.negative); - cl_assert(!rule->match.directory); - cl_assert(!rule->match.fullpath); + cl_assert(rule->match.flags == 0); rule = get_rule(5); cl_assert_strequal("*.pat5", rule->match.pattern); @@ -94,9 +84,7 @@ void test_attr_file__match_variants(void) rule = get_rule(8); cl_assert_strequal("pat8 with spaces", rule->match.pattern); cl_assert(rule->match.length == strlen("pat8 with spaces")); - cl_assert(!rule->match.negative); - cl_assert(!rule->match.directory); - cl_assert(!rule->match.fullpath); + cl_assert(rule->match.flags == 0); rule = get_rule(9); cl_assert_strequal("pat9", rule->match.pattern); @@ -120,7 +108,6 @@ static void check_one_assign( cl_assert(rule->assigns.length == 1); cl_assert_strequal(name, assign->name); cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name)); - cl_assert(assign->name_len == strlen(name)); cl_assert(assign->is_allocated == is_allocated); if (is_allocated) cl_assert_strequal(value, assign->value); @@ -134,7 +121,7 @@ void test_attr_file__assign_variants(void) git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__from_file(&file, cl_fixture("attr/attr2"))); + 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); @@ -199,7 +186,7 @@ void test_attr_file__check_attr_examples(void) git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__from_file(&file, cl_fixture("attr/attr3"))); + 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 870dcd343..fcade5225 100644 --- a/tests-clay/attr/lookup.c +++ b/tests-clay/attr/lookup.c @@ -1,5 +1,5 @@ #include "clay_libgit2.h" -#include "attr.h" +#include "attr_file.h" void test_attr_lookup__simple(void) { @@ -7,7 +7,7 @@ void test_attr_lookup__simple(void) git_attr_path path; const char *value = NULL; - cl_git_pass(git_attr_file__from_file(&file, cl_fixture("attr/attr0"))); + 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); @@ -41,10 +41,6 @@ static void run_test_cases(git_attr_file *file, test_case *cases) int error; for (c = cases; c->path != NULL; c++) { - /* Put this in because I was surprised that all the tests passed */ - /* fprintf(stderr, "checking '%s' attr %s == %s\n", */ - /* c->path, c->attr, c->expected); */ - cl_git_pass(git_attr_path__init(&path, c->path)); if (c->force_dir) @@ -136,7 +132,7 @@ void test_attr_lookup__match_variants(void) { NULL, NULL, NULL, 0, 0 } }; - cl_git_pass(git_attr_file__from_file(&file, cl_fixture("attr/attr1"))); + 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); @@ -194,7 +190,7 @@ void test_attr_lookup__assign_variants(void) { NULL, NULL, NULL, 0, 0 } }; - cl_git_pass(git_attr_file__from_file(&file, cl_fixture("attr/attr2"))); + 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); @@ -228,7 +224,31 @@ void test_attr_lookup__check_attr_examples(void) { NULL, NULL, NULL, 0, 0 } }; - cl_git_pass(git_attr_file__from_file(&file, cl_fixture("attr/attr3"))); + 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); + + git_attr_file__free(file); +} + +void test_attr_lookup__from_buffer(void) +{ + git_attr_file *file = NULL; + test_case cases[] = { + { "abc", "foo", GIT_ATTR_TRUE, 0, 0 }, + { "abc", "bar", GIT_ATTR_TRUE, 0, 0 }, + { "abc", "baz", GIT_ATTR_TRUE, 0, 0 }, + { "aaa", "foo", GIT_ATTR_TRUE, 0, 0 }, + { "aaa", "bar", NULL, 0, 0 }, + { "aaa", "baz", GIT_ATTR_TRUE, 0, 0 }, + { "qqq", "foo", NULL, 0, 0 }, + { "qqq", "bar", NULL, 0, 0 }, + { "qqq", "baz", GIT_ATTR_TRUE, 0, 0 }, + { NULL, NULL, NULL, 0, 0 } + }; + + 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 b6815f3ba..e80e24dbf 100644 --- a/tests-clay/attr/repo.c +++ b/tests-clay/attr/repo.c @@ -6,11 +6,14 @@ static git_repository *g_repo = NULL; void test_attr_repo__initialize(void) { - /* before each test, instantiate the attr repo from the fixtures and + /* 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 gitattributes to .gitattributes, because it contains + * macro definitions which are only allowed in the root. */ cl_fixture_sandbox("attr"); cl_git_pass(p_rename("attr/.gitted", "attr/.git")); + cl_git_pass(p_rename("attr/gitattributes", "attr/.gitattributes")); cl_git_pass(git_repository_open(&g_repo, "attr/.git")); } @@ -138,3 +141,46 @@ void test_attr_repo__foreach(void) &count_attrs, &count)); cl_assert(count == 5); /* repoattr, rootattr, subattr, 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_assert(value == GIT_ATTR_TRUE); + + cl_git_pass(git_attr_get(g_repo, "subdir/abc", "bar", &value)); + cl_assert(value == NULL); + + cl_git_pass(git_attr_get(g_repo, "subdir/abc", "baz", &value)); + cl_assert(value == GIT_ATTR_FALSE); + + cl_git_pass(git_attr_get(g_repo, "subdir/abc", "merge", &value)); + cl_assert_strequal("filfre", value); + + cl_git_pass(git_attr_get(g_repo, "subdir/abc", "frotz", &value)); + cl_assert(value == NULL); +} + +void test_attr_repo__macros(void) +{ + const char *names[5] = { "rootattr", "binary", "diff", "crlf", "frotz" }; + const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" }; + const char *values[5]; + + cl_git_pass(git_attr_get_many(g_repo, "binfile", 5, names, values)); + + cl_assert(values[0] == GIT_ATTR_TRUE); + cl_assert(values[1] == NULL); + cl_assert(values[2] == GIT_ATTR_FALSE); + cl_assert(values[3] == GIT_ATTR_FALSE); + cl_assert(values[4] == NULL); + + cl_git_pass(git_attr_get_many(g_repo, "macro_test", 5, names2, values)); + + cl_assert(values[0] == NULL); + cl_assert(values[1] == GIT_ATTR_TRUE); + cl_assert(values[2] == GIT_ATTR_FALSE); + cl_assert(values[3] == NULL); + cl_assert_strequal("77", values[4]); +} diff --git a/tests-clay/clay.h b/tests-clay/clay.h index 8237991c0..4a57926bf 100644 --- a/tests-clay/clay.h +++ b/tests-clay/clay.h @@ -65,6 +65,7 @@ extern void test_attr_file__match_variants(void); extern void test_attr_file__simple_read(void); extern void test_attr_lookup__assign_variants(void); extern void test_attr_lookup__check_attr_examples(void); +extern void test_attr_lookup__from_buffer(void); extern void test_attr_lookup__match_variants(void); extern void test_attr_lookup__simple(void); extern void test_attr_repo__cleanup(void); @@ -72,6 +73,8 @@ extern void test_attr_repo__foreach(void); extern void test_attr_repo__get_many(void); extern void test_attr_repo__get_one(void); extern void test_attr_repo__initialize(void); +extern void test_attr_repo__macros(void); +extern void test_attr_repo__manpage_example(void); extern void test_buf_basic__printf(void); extern void test_buf_basic__resize(void); extern void test_config_add__cleanup(void); diff --git a/tests-clay/clay_main.c b/tests-clay/clay_main.c index 49a867698..ce881e45d 100644 --- a/tests-clay/clay_main.c +++ b/tests-clay/clay_main.c @@ -117,13 +117,16 @@ static const struct clay_func _clay_cb_attr_file[] = { static const struct clay_func _clay_cb_attr_lookup[] = { {"assign_variants", &test_attr_lookup__assign_variants}, {"check_attr_examples", &test_attr_lookup__check_attr_examples}, + {"from_buffer", &test_attr_lookup__from_buffer}, {"match_variants", &test_attr_lookup__match_variants}, {"simple", &test_attr_lookup__simple} }; static const struct clay_func _clay_cb_attr_repo[] = { {"foreach", &test_attr_repo__foreach}, {"get_many", &test_attr_repo__get_many}, - {"get_one", &test_attr_repo__get_one} + {"get_one", &test_attr_repo__get_one}, + {"macros", &test_attr_repo__macros}, + {"manpage_example", &test_attr_repo__manpage_example} }; static const struct clay_func _clay_cb_buf_basic[] = { {"printf", &test_buf_basic__printf}, @@ -329,13 +332,13 @@ static const struct clay_suite _clay_suites[] = { "attr::lookup", {NULL, NULL}, {NULL, NULL}, - _clay_cb_attr_lookup, 4 + _clay_cb_attr_lookup, 5 }, { "attr::repo", {"initialize", &test_attr_repo__initialize}, {"cleanup", &test_attr_repo__cleanup}, - _clay_cb_attr_repo, 3 + _clay_cb_attr_repo, 5 }, { "buf::basic", @@ -556,7 +559,7 @@ static const struct clay_suite _clay_suites[] = { }; static size_t _clay_suite_count = 39; -static size_t _clay_callback_count = 131; +static size_t _clay_callback_count = 134; /* Core test functions */ static void diff --git a/tests/resources/attr/.gitattributes b/tests/resources/attr/.gitattributes deleted file mode 100644 index f2c6d717c..000000000 --- a/tests/resources/attr/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -* rootattr -root_test2 -rootattr -root_test3 !rootattr - diff --git a/tests/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index Binary files differindex 6841fb2ec..9c5907386 100644 --- a/tests/resources/attr/.gitted/index +++ b/tests/resources/attr/.gitted/index diff --git a/tests/resources/attr/.gitted/info/attributes b/tests/resources/attr/.gitted/info/attributes index 93efc0c34..2e9643a53 100644 --- a/tests/resources/attr/.gitted/info/attributes +++ b/tests/resources/attr/.gitted/info/attributes @@ -1,2 +1,2 @@ * repoattr - +a* foo !bar -baz diff --git a/tests/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD index cfd1f9525..3c4045173 100644 --- a/tests/resources/attr/.gitted/logs/HEAD +++ b/tests/resources/attr/.gitted/logs/HEAD @@ -1 +1,2 @@ 0000000000000000000000000000000000000000 6bab5c79cd5140d0f800917f550eb2a3dc32b0da Russell Belfer <arrbee@arrbee.com> 1324416995 -0800 commit (initial): initial test data +6bab5c79cd5140d0f800917f550eb2a3dc32b0da 605812ab7fe421fdd325a935d35cb06a9234a7d7 Russell Belfer <arrbee@arrbee.com> 1325143098 -0800 commit: latest test updates diff --git a/tests/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master index cfd1f9525..3c4045173 100644 --- a/tests/resources/attr/.gitted/logs/refs/heads/master +++ b/tests/resources/attr/.gitted/logs/refs/heads/master @@ -1 +1,2 @@ 0000000000000000000000000000000000000000 6bab5c79cd5140d0f800917f550eb2a3dc32b0da Russell Belfer <arrbee@arrbee.com> 1324416995 -0800 commit (initial): initial test data +6bab5c79cd5140d0f800917f550eb2a3dc32b0da 605812ab7fe421fdd325a935d35cb06a9234a7d7 Russell Belfer <arrbee@arrbee.com> 1325143098 -0800 commit: latest test updates diff --git a/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 Binary files differnew file mode 100644 index 000000000..091d79b14 --- /dev/null +++ b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 diff --git a/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 new file mode 100644 index 000000000..b0cc51ee6 --- /dev/null +++ b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 @@ -0,0 +1,2 @@ +xN
0;S˻BU J ?lٖygcáU RbacG;l㠝Dq֠ZʫAH<Ǒ3N=J2d3[0= +}ۤIjM"x/[TwU&[/k(tJL
\ No newline at end of file diff --git a/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 Binary files differnew file mode 100644 index 000000000..a9ddf5d20 --- /dev/null +++ b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 diff --git a/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 Binary files differnew file mode 100644 index 000000000..8f5acc70a --- /dev/null +++ b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 diff --git a/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 Binary files differnew file mode 100644 index 000000000..7663ad0ad --- /dev/null +++ b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 diff --git a/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 Binary files differnew file mode 100644 index 000000000..d898ae9b8 --- /dev/null +++ b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 diff --git a/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 Binary files differnew file mode 100644 index 000000000..83f3b726d --- /dev/null +++ b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 diff --git a/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b Binary files differnew file mode 100644 index 000000000..b736c0b2b --- /dev/null +++ b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b diff --git a/tests/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master index 279272e5c..1049fe4b7 100644 --- a/tests/resources/attr/.gitted/refs/heads/master +++ b/tests/resources/attr/.gitted/refs/heads/master @@ -1 +1 @@ -6bab5c79cd5140d0f800917f550eb2a3dc32b0da +605812ab7fe421fdd325a935d35cb06a9234a7d7 diff --git a/tests/resources/attr/binfile b/tests/resources/attr/binfile new file mode 100644 index 000000000..d800886d9 --- /dev/null +++ b/tests/resources/attr/binfile @@ -0,0 +1 @@ +123
\ No newline at end of file diff --git a/tests/resources/attr/gitattributes b/tests/resources/attr/gitattributes new file mode 100644 index 000000000..94da4faa0 --- /dev/null +++ b/tests/resources/attr/gitattributes @@ -0,0 +1,7 @@ +* rootattr +root_test2 -rootattr +root_test3 !rootattr +binfile binary +abc foo bar baz +[attr]mymacro positive -negative !rootattr +macro* mymacro another=77 diff --git a/tests/resources/attr/macro_test b/tests/resources/attr/macro_test new file mode 100644 index 000000000..ff69f8639 --- /dev/null +++ b/tests/resources/attr/macro_test @@ -0,0 +1 @@ +Yo diff --git a/tests/resources/attr/subdir/.gitattributes b/tests/resources/attr/subdir/.gitattributes index 210f3a8ba..99eae4768 100644 --- a/tests/resources/attr/subdir/.gitattributes +++ b/tests/resources/attr/subdir/.gitattributes @@ -1,3 +1,5 @@ * 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 new file mode 100644 index 000000000..3e42ffc54 --- /dev/null +++ b/tests/resources/attr/subdir/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 |