summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVicent Marti <vicent@github.com>2014-04-19 13:05:32 +0200
committerVicent Marti <vicent@github.com>2014-04-19 13:05:32 +0200
commit7b0f8ba9a8d714de3d4432c9b5902f3a8d8889d7 (patch)
treef9a21fca19b8f024810f3278a8c99b18b715d403 /src
parent386777fd0dd7665a0aad65bf3589d7bf71024c0e (diff)
parentac16bd0a94e1f7254112c7585b843bdc2d0659c1 (diff)
downloadlibgit2-7b0f8ba9a8d714de3d4432c9b5902f3a8d8889d7.tar.gz
Merge pull request #2279 from libgit2/rb/moar-eegnöre-fîxés
Fix several ignore and attribute file behavior bugs
Diffstat (limited to 'src')
-rw-r--r--src/attr.c74
-rw-r--r--src/attr_file.c14
-rw-r--r--src/attr_file.h6
-rw-r--r--src/ignore.c13
-rw-r--r--src/ignore.h1
-rw-r--r--src/path.c2
-rw-r--r--src/pathspec.c3
7 files changed, 98 insertions, 15 deletions
diff --git a/src/attr.c b/src/attr.c
index 622874348..05b0c1b3c 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -217,6 +217,74 @@ cleanup:
return error;
}
+static int preload_attr_file(
+ git_repository *repo,
+ git_attr_file_source source,
+ const char *base,
+ const char *file)
+{
+ int error;
+ git_attr_file *preload = NULL;
+
+ if (!file)
+ return 0;
+ if (!(error = git_attr_cache__get(
+ &preload, repo, source, base, file, git_attr_file__parse_buffer)))
+ git_attr_file__free(preload);
+
+ return error;
+}
+
+static int attr_setup(git_repository *repo)
+{
+ int error = 0;
+ const char *workdir = git_repository_workdir(repo);
+ git_index *idx = NULL;
+ git_buf sys = GIT_BUF_INIT;
+
+ if ((error = git_attr_cache__init(repo)) < 0)
+ return error;
+
+ /* preload attribute files that could contain macros so the
+ * definitions will be available for later file parsing
+ */
+
+ if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) {
+ error = preload_attr_file(
+ repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr);
+ git_buf_free(&sys);
+ }
+ if (error < 0) {
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
+ } else
+ return error;
+ }
+
+ if ((error = preload_attr_file(
+ repo, GIT_ATTR_FILE__FROM_FILE,
+ NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
+ return error;
+
+ if ((error = preload_attr_file(
+ repo, GIT_ATTR_FILE__FROM_FILE,
+ git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0)
+ return error;
+
+ if (workdir != NULL &&
+ (error = preload_attr_file(
+ repo, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0)
+ return error;
+
+ if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
+ (error = preload_attr_file(
+ repo, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0)
+ return error;
+
+ return error;
+}
+
int git_attr_add_macro(
git_repository *repo,
const char *name,
@@ -226,8 +294,8 @@ int git_attr_add_macro(
git_attr_rule *macro = NULL;
git_pool *pool;
- if (git_attr_cache__init(repo) < 0)
- return -1;
+ if ((error = git_attr_cache__init(repo)) < 0)
+ return error;
macro = git__calloc(1, sizeof(git_attr_rule));
GITERR_CHECK_ALLOC(macro);
@@ -348,7 +416,7 @@ static int collect_attr_files(
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL };
- if ((error = git_attr_cache__init(repo)) < 0)
+ if ((error = attr_setup(repo)) < 0)
return error;
/* Resolve path in a non-bare repo */
diff --git a/src/attr_file.c b/src/attr_file.c
index 65bbf78e8..d107b5ab0 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -248,9 +248,7 @@ int git_attr_file__parse_buffer(
repo, &attrs->pool, &rule->assigns, &scan)))
{
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
- /* should generate error/warning if this is coming from any
- * file other than .gitattributes at repo root.
- */
+ /* TODO: warning if macro found in file below repo root */
error = git_attr_cache__insert_macro(repo, rule);
else
error = git_vector_insert(&attrs->rules, rule);
@@ -355,6 +353,8 @@ bool git_attr_fnmatch__match(
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
flags |= FNM_CASEFOLD;
+ if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
+ flags |= FNM_LEADING_DIR;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
filename = path->path;
@@ -545,6 +545,14 @@ int git_attr_fnmatch__parse(
if (--slash_count <= 0)
spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
}
+ if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0 &&
+ spec->length >= 2 &&
+ pattern[spec->length - 1] == '*' &&
+ pattern[spec->length - 2] == '/') {
+ spec->length -= 2;
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_LEADINGDIR;
+ /* leave FULLPATH match on, however */
+ }
if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
context != NULL && git_path_root(pattern) < 0)
diff --git a/src/attr_file.h b/src/attr_file.h
index c906be44d..e50aec07c 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -30,10 +30,12 @@
#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
+#define GIT_ATTR_FNMATCH_LEADINGDIR (1U << 11)
+#define GIT_ATTR_FNMATCH_NOLEADINGDIR (1U << 12)
#define GIT_ATTR_FNMATCH__INCOMING \
- (GIT_ATTR_FNMATCH_ALLOWSPACE | \
- GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
+ (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | \
+ GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR)
typedef enum {
GIT_ATTR_FILE__IN_MEMORY = 0,
diff --git a/src/ignore.c b/src/ignore.c
index deae204f8..b08ff2200 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -123,7 +123,7 @@ int git_ignore__for_path(
int error = 0;
const char *workdir = git_repository_workdir(repo);
- assert(ignores);
+ assert(ignores && path);
memset(ignores, 0, sizeof(*ignores));
ignores->repo = repo;
@@ -140,10 +140,13 @@ int git_ignore__for_path(
if (workdir && git_path_root(path) < 0)
error = git_path_find_dir(&ignores->dir, path, workdir);
else
- error = git_buf_sets(&ignores->dir, path);
+ error = git_buf_joinpath(&ignores->dir, path, "");
if (error < 0)
goto cleanup;
+ if (workdir && !git__prefixcmp(ignores->dir.ptr, workdir))
+ ignores->dir_root = strlen(workdir);
+
/* set up internals */
if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0)
goto cleanup;
@@ -204,10 +207,10 @@ int git_ignore__pop_dir(git_ignores *ign)
if ((end = strrchr(start, '/')) != NULL) {
size_t dirlen = (end - start) + 1;
+ const char *relpath = ign->dir.ptr + ign->dir_root;
+ size_t pathlen = ign->dir.size - ign->dir_root;
- if (ign->dir.size >= dirlen &&
- !memcmp(ign->dir.ptr + ign->dir.size - dirlen, start, dirlen))
- {
+ if (pathlen == dirlen && !memcmp(relpath, start, dirlen)) {
git_vector_pop(&ign->ign_path);
git_attr_file__free(file);
}
diff --git a/src/ignore.h b/src/ignore.h
index 46172c72f..ff9369000 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -28,6 +28,7 @@ typedef struct {
git_attr_file *ign_internal;
git_vector ign_path;
git_vector ign_global;
+ size_t dir_root; /* offset in dir to repo root */
int ignore_case;
int depth;
} git_ignores;
diff --git a/src/path.c b/src/path.c
index 7cad28d45..a990b005f 100644
--- a/src/path.c
+++ b/src/path.c
@@ -624,7 +624,7 @@ int git_path_find_dir(git_buf *dir, const char *path, const char *base)
/* call dirname if this is not a directory */
if (!error) /* && git_path_isdir(dir->ptr) == false) */
- error = git_path_dirname_r(dir, dir->ptr);
+ error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0;
if (!error)
error = git_path_to_dir(dir);
diff --git a/src/pathspec.c b/src/pathspec.c
index 09650de7c..a01d74f07 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -83,7 +83,8 @@ int git_pathspec__vinit(
if (!match)
return -1;
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE |
+ GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_NOLEADINGDIR;
ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
if (ret == GIT_ENOTFOUND) {