summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/attr.h24
-rw-r--r--src/attr.c131
-rw-r--r--src/attr.h21
-rw-r--r--src/attr_file.c171
-rw-r--r--src/attr_file.h43
-rw-r--r--src/config.c79
-rw-r--r--src/config.h1
-rw-r--r--src/fileops.c131
-rw-r--r--src/fileops.h24
-rw-r--r--src/repository.c2
-rw-r--r--src/repository.h2
-rw-r--r--src/util.h1
-rw-r--r--src/win32/utf-conv.c5
-rw-r--r--src/win32/utf-conv.h1
-rw-r--r--tests-clay/attr/file.c35
-rw-r--r--tests-clay/attr/lookup.c38
-rw-r--r--tests-clay/attr/repo.c48
-rw-r--r--tests-clay/clay.h3
-rw-r--r--tests-clay/clay_main.c11
-rw-r--r--tests/resources/attr/.gitattributes4
-rw-r--r--tests/resources/attr/.gitted/indexbin1072 -> 1304 bytes
-rw-r--r--tests/resources/attr/.gitted/info/attributes2
-rw-r--r--tests/resources/attr/.gitted/logs/HEAD1
-rw-r--r--tests/resources/attr/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249bin0 -> 596 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d72
-rw-r--r--tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857bin0 -> 124 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9bin0 -> 95 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9bin0 -> 151 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320bin0 -> 351 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2bin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2bbin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/refs/heads/master2
-rw-r--r--tests/resources/attr/binfile1
-rw-r--r--tests/resources/attr/gitattributes7
-rw-r--r--tests/resources/attr/macro_test1
-rw-r--r--tests/resources/attr/subdir/.gitattributes4
-rw-r--r--tests/resources/attr/subdir/abc37
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, &macro->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(&macro->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(&macro->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
index 6841fb2ec..9c5907386 100644
--- a/tests/resources/attr/.gitted/index
+++ b/tests/resources/attr/.gitted/index
Binary files differ
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
new file mode 100644
index 000000000..091d79b14
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
Binary files differ
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=
+}ۤI™jM"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
new file mode 100644
index 000000000..a9ddf5d20
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
new file mode 100644
index 000000000..8f5acc70a
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
new file mode 100644
index 000000000..7663ad0ad
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
new file mode 100644
index 000000000..d898ae9b8
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
new file mode 100644
index 000000000..83f3b726d
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
new file mode 100644
index 000000000..b736c0b2b
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
Binary files differ
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