summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2012-05-03 16:37:25 -0700
committerRussell Belfer <rb@github.com>2012-05-03 16:37:25 -0700
commitf917481ee84cbba481c1854cccdedb2d98377d43 (patch)
treeb6276158b43f8d54358df5609fd0e508928306b2 /src
parent3fbcac89c47cb66ea193f66da6d93d1c36ed0f5e (diff)
downloadlibgit2-f917481ee84cbba481c1854cccdedb2d98377d43.tar.gz
Support reading attributes from index
Depending on the operation, we need to consider gitattributes in both the work dir and the index. This adds a parameter to all of the gitattributes related functions that allows user control of attribute reading behavior (i.e. prefer workdir, prefer index, only use index). This fix also covers allowing us to check attributes (and hence do diff and status) on bare repositories. This was a somewhat larger change that I hoped because it had to change the cache key used for gitattributes files.
Diffstat (limited to 'src')
-rw-r--r--src/attr.c286
-rw-r--r--src/attr.h26
-rw-r--r--src/attr_file.c90
-rw-r--r--src/attr_file.h21
-rw-r--r--src/crlf.c3
-rw-r--r--src/diff_output.c2
-rw-r--r--src/ignore.c69
-rw-r--r--src/ignore.h5
-rw-r--r--src/object.c39
-rw-r--r--src/repository.c20
-rw-r--r--src/repository.h4
-rw-r--r--src/status.c44
12 files changed, 395 insertions, 214 deletions
diff --git a/src/attr.c b/src/attr.c
index 120d12737..56d04d3a9 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -6,12 +6,18 @@
GIT__USE_STRMAP;
static int collect_attr_files(
- git_repository *repo, const char *path, git_vector *files);
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ git_vector *files);
int git_attr_get(
- git_repository *repo, const char *pathname,
- const char *name, const char **value)
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ const char *name,
+ const char **value)
{
int error;
git_attr_path path;
@@ -26,7 +32,7 @@ int git_attr_get(
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1;
- if ((error = collect_attr_files(repo, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
goto cleanup;
attr.name = name;
@@ -58,8 +64,12 @@ typedef struct {
} attr_get_many_info;
int git_attr_get_many(
- git_repository *repo, const char *pathname,
- size_t num_attr, const char **names, const char **values)
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ size_t num_attr,
+ const char **names,
+ const char **values)
{
int error;
git_attr_path path;
@@ -75,7 +85,7 @@ int git_attr_get_many(
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1;
- if ((error = collect_attr_files(repo, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
goto cleanup;
info = git__calloc(num_attr, sizeof(attr_get_many_info));
@@ -119,7 +129,9 @@ cleanup:
int git_attr_foreach(
- git_repository *repo, const char *pathname,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
int (*callback)(const char *name, const char *value, void *payload),
void *payload)
{
@@ -135,7 +147,7 @@ int git_attr_foreach(
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1;
- if ((error = collect_attr_files(repo, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
goto cleanup;
seen = git_strmap_alloc();
@@ -203,113 +215,230 @@ int git_attr_add_macro(
return error;
}
-bool git_attr_cache__is_cached(git_repository *repo, const char *path)
+bool git_attr_cache__is_cached(
+ git_repository *repo, git_attr_file_source source, const char *path)
{
- const char *cache_key = path;
+ git_buf cache_key = GIT_BUF_INIT;
git_strmap *files = git_repository_attr_cache(repo)->files;
+ const char *workdir = git_repository_workdir(repo);
+ bool rval;
+
+ if (workdir && git__prefixcmp(path, workdir) == 0)
+ path += strlen(workdir);
+ if (git_buf_printf(&cache_key, "%d#%s", (int)source, path) < 0)
+ return false;
+
+ rval = git_strmap_exists(files, git_buf_cstr(&cache_key));
+
+ git_buf_free(&cache_key);
+
+ return rval;
+}
+
+static int load_attr_file(const char *filename, const char **data)
+{
+ int error;
+ git_buf content = GIT_BUF_INIT;
+
+ error = git_futils_readbuffer(&content, filename);
+ *data = error ? NULL : git_buf_detach(&content);
+
+ return error;
+}
- if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
- cache_key += strlen(git_repository_workdir(repo));
+static int load_attr_blob_from_index(
+ git_repository *repo, const char *filename, git_blob **blob)
+{
+ int error;
+ git_index *index;
+ git_index_entry *entry;
+
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+ (error = git_index_find(index, filename)) < 0)
+ return error;
- return git_strmap_exists(files, cache_key);
+ entry = git_index_get(index, error);
+
+ return git_blob_lookup(blob, repo, &entry->oid);
}
-int git_attr_cache__lookup_or_create_file(
+int git_attr_cache__internal_file(
git_repository *repo,
- const char *key,
const char *filename,
- int (*loader)(git_repository *, const char *, git_attr_file *),
- git_attr_file **file_ptr)
+ git_attr_file **file)
{
- int error;
+ int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
- git_attr_file *file = NULL;
- khiter_t pos;
-
- pos = git_strmap_lookup_index(cache->files, key);
- if (git_strmap_valid_index(cache->files, pos)) {
- *file_ptr = git_strmap_value_at(cache->files, pos);
- return 0;
- }
+ khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename);
- if (loader && git_path_exists(filename) == false) {
- *file_ptr = NULL;
+ if (git_strmap_valid_index(cache->files, cache_pos)) {
+ *file = git_strmap_value_at(cache->files, cache_pos);
return 0;
}
- if (git_attr_file__new(&file, &cache->pool) < 0)
+ if (git_attr_file__new(file, 0, filename, &cache->pool) < 0)
return -1;
- if (loader)
- error = loader(repo, filename, file);
- else
- error = git_attr_file__set_path(repo, key, file);
-
- if (!error) {
- git_strmap_insert(cache->files, file->path, file, error);
- if (error > 0)
- error = 0;
- }
-
- if (error < 0) {
- git_attr_file__free(file);
- file = NULL;
- }
+ git_strmap_insert(cache->files, (*file)->key + 2, *file, error);
+ if (error > 0)
+ error = 0;
- *file_ptr = file;
return error;
}
-/* add git_attr_file to vector of files, loading if needed */
int git_attr_cache__push_file(
git_repository *repo,
- git_vector *stack,
- const char *base,
- const char *filename,
- int (*loader)(git_repository *, const char *, git_attr_file *))
+ const char *base,
+ const char *filename,
+ git_attr_file_source source,
+ git_attr_file_parser parse,
+ git_vector *stack)
{
- int error;
+ int error = 0;
git_buf path = GIT_BUF_INIT;
+ const char *workdir = git_repository_workdir(repo);
+ const char *relfile, *content = NULL;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file *file = NULL;
- const char *cache_key;
+ git_blob *blob = NULL;
- if (base != NULL) {
+ assert(filename && stack);
+
+ /* join base and path as needed */
+ if (base != NULL && git_path_root(filename) < 0) {
if (git_buf_joinpath(&path, base, filename) < 0)
return -1;
filename = path.ptr;
}
- /* either get attr_file from cache or read from disk */
- cache_key = filename;
- if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
- cache_key += strlen(git_repository_workdir(repo));
+ relfile = filename;
+ if (workdir && git__prefixcmp(relfile, workdir) == 0)
+ relfile += strlen(workdir);
+
+ /* check cache */
+ if (cache && cache->files) {
+ git_buf cache_key = GIT_BUF_INIT;
+ khiter_t cache_pos;
+
+ if (git_buf_printf(&cache_key, "%d#%s", (int)source, relfile) < 0)
+ return -1;
+
+ cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr);
+
+ git_buf_free(&cache_key);
+
+ if (git_strmap_valid_index(cache->files, cache_pos)) {
+ file = git_strmap_value_at(cache->files, cache_pos);
+ goto finish;
+ }
+ }
+
+ /* if not in cache, load data, parse, and cache */
+ if (git_attr_file__new(&file, source, relfile, &cache->pool) < 0)
+ return -1;
+
+ if (source == GIT_ATTR_FILE_FROM_FILE)
+ error = load_attr_file(filename, &content);
+ else
+ error = load_attr_blob_from_index(repo, relfile, &blob);
+
+ if (error) {
+ /* not finding a file is not an error for this function */
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
+ }
+ goto finish;
+ }
+
+ if (blob)
+ content = git_blob_rawcontent(blob);
+
+ if (parse && (error = parse(repo, content, file)) < 0)
+ goto finish;
- error = git_attr_cache__lookup_or_create_file(
- repo, cache_key, filename, loader, &file);
+ git_strmap_insert(cache->files, file->key, file, error);
+ if (error > 0)
+ error = 0;
+finish:
+ /* push file onto vector if we found one*/
if (!error && file != NULL)
error = git_vector_insert(stack, file);
+ if (error != 0)
+ git_attr_file__free(file);
+
+ if (blob)
+ git_blob_free(blob);
+ else
+ git__free((void *)content);
+
git_buf_free(&path);
+
return error;
}
-#define push_attrs(R,S,B,F) \
- git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file)
+#define push_attr_file(R,S,B,F) \
+ git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S))
typedef struct {
git_repository *repo;
+ uint32_t flags;
+ const char *workdir;
+ git_index *index;
git_vector *files;
} attr_walk_up_info;
+int git_attr_cache__decide_sources(
+ uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
+{
+ int count = 0;
+
+ switch (flags & 0x03) {
+ case GIT_ATTR_CHECK_FILE_THEN_INDEX:
+ if (has_wd)
+ srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ break;
+ case GIT_ATTR_CHECK_INDEX_THEN_FILE:
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ if (has_wd)
+ srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
+ break;
+ case GIT_ATTR_CHECK_INDEX_ONLY:
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ break;
+ }
+
+ return count;
+}
+
static int push_one_attr(void *ref, git_buf *path)
{
+ int error = 0, n_src, i;
attr_walk_up_info *info = (attr_walk_up_info *)ref;
- return push_attrs(info->repo, info->files, path->ptr, GIT_ATTR_FILE);
+ git_attr_file_source src[2];
+
+ n_src = git_attr_cache__decide_sources(
+ info->flags, info->workdir != NULL, info->index != NULL, src);
+
+ for (i = 0; !error && i < n_src; ++i)
+ error = git_attr_cache__push_file(
+ info->repo, path->ptr, GIT_ATTR_FILE, src[i],
+ git_attr_file__parse_buffer, info->files);
+
+ return error;
}
static int collect_attr_files(
- git_repository *repo, const char *path, git_vector *files)
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ git_vector *files)
{
int error;
git_buf dir = GIT_BUF_INIT;
@@ -320,7 +449,11 @@ static int collect_attr_files(
git_vector_init(files, 4, NULL) < 0)
return -1;
- error = git_path_find_dir(&dir, path, workdir);
+ /* given a unrooted path in a non-bare repo, resolve it */
+ if (workdir && git_path_root(path) < 0)
+ error = git_path_find_dir(&dir, path, workdir);
+ else
+ error = git_buf_sets(&dir, path);
if (error < 0)
goto cleanup;
@@ -331,29 +464,36 @@ static int collect_attr_files(
* - $GIT_PREFIX/etc/gitattributes
*/
- error = push_attrs(
+ error = push_attr_file(
repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);
if (error < 0)
goto cleanup;
- info.repo = repo;
+ info.repo = repo;
+ info.flags = flags;
+ info.workdir = workdir;
+ if (git_repository_index__weakptr(&info.index, repo) < 0)
+ giterr_clear(); /* no error even if there is no index */
info.files = files;
+
error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
if (error < 0)
goto cleanup;
if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
- error = push_attrs(
+ error = push_attr_file(
repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
if (error < 0)
goto cleanup;
}
- error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
- if (!error)
- error = push_attrs(repo, files, NULL, dir.ptr);
- else if (error == GIT_ENOTFOUND)
- error = 0;
+ if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
+ error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
+ if (!error)
+ error = push_attr_file(repo, files, NULL, dir.ptr);
+ else if (error == GIT_ENOTFOUND)
+ error = 0;
+ }
cleanup:
if (error < 0)
diff --git a/src/attr.h b/src/attr.h
index 43caf1b81..a35b1160f 100644
--- a/src/attr.h
+++ b/src/attr.h
@@ -22,6 +22,9 @@ typedef struct {
const char *cfg_excl_file; /* cached value of core.excludesfile */
} git_attr_cache;
+typedef int (*git_attr_file_parser)(
+ git_repository *, const char *, git_attr_file *);
+
extern int git_attr_cache__init(git_repository *repo);
extern int git_attr_cache__insert_macro(
@@ -30,21 +33,24 @@ extern int git_attr_cache__insert_macro(
extern git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name);
-extern int git_attr_cache__lookup_or_create_file(
+extern int git_attr_cache__push_file(
git_repository *repo,
- const char *key,
+ const char *base,
const char *filename,
- int (*loader)(git_repository *, const char *, git_attr_file *),
- git_attr_file **file_ptr);
+ git_attr_file_source source,
+ git_attr_file_parser parse,
+ git_vector *stack);
-extern int git_attr_cache__push_file(
+extern int git_attr_cache__internal_file(
git_repository *repo,
- git_vector *stack,
- const char *base,
- const char *filename,
- int (*loader)(git_repository *, const char *, git_attr_file *));
+ const char *key,
+ git_attr_file **file_ptr);
/* returns true if path is in cache */
-extern bool git_attr_cache__is_cached(git_repository *repo, const char *path);
+extern bool git_attr_cache__is_cached(
+ git_repository *repo, git_attr_file_source source, const char *path);
+
+extern int git_attr_cache__decide_sources(
+ uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs);
#endif
diff --git a/src/attr_file.c b/src/attr_file.c
index 25c21b1fd..ab320a6c4 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -1,15 +1,22 @@
#include "common.h"
#include "repository.h"
#include "filebuf.h"
+#include "git2/blob.h"
+#include "git2/tree.h"
#include <ctype.h>
const char *git_attr__true = "[internal]__TRUE__";
const char *git_attr__false = "[internal]__FALSE__";
+const char *git_attr__unset = "[internal]__UNSET__";
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
static void git_attr_rule__clear(git_attr_rule *rule);
-int git_attr_file__new(git_attr_file **attrs_ptr, git_pool *pool)
+int git_attr_file__new(
+ git_attr_file **attrs_ptr,
+ git_attr_file_source from,
+ const char *path,
+ git_pool *pool)
{
git_attr_file *attrs = NULL;
@@ -25,6 +32,18 @@ int git_attr_file__new(git_attr_file **attrs_ptr, git_pool *pool)
attrs->pool_is_allocated = true;
}
+ if (path) {
+ size_t len = strlen(path);
+
+ attrs->key = git_pool_malloc(attrs->pool, len + 3);
+ GITERR_CHECK_ALLOC(attrs->key);
+
+ attrs->key[0] = '0' + from;
+ attrs->key[1] = '#';
+ memcpy(&attrs->key[2], path, len);
+ attrs->key[len + 2] = '\0';
+ }
+
if (git_vector_init(&attrs->rules, 4, NULL) < 0)
goto fail;
@@ -37,31 +56,7 @@ fail:
return -1;
}
-int git_attr_file__set_path(
- git_repository *repo, const char *path, git_attr_file *file)
-{
- if (file->path != NULL) {
- git__free(file->path);
- file->path = NULL;
- }
-
- if (repo == NULL)
- file->path = git__strdup(path);
- else {
- const char *workdir = git_repository_workdir(repo);
-
- if (workdir && git__prefixcmp(path, workdir) == 0)
- file->path = git__strdup(path + strlen(workdir));
- else
- file->path = git__strdup(path);
- }
-
- GITERR_CHECK_ALLOC(file->path);
-
- return 0;
-}
-
-int git_attr_file__from_buffer(
+int git_attr_file__parse_buffer(
git_repository *repo, const char *buffer, git_attr_file *attrs)
{
int error = 0;
@@ -73,10 +68,10 @@ int git_attr_file__from_buffer(
scan = buffer;
- if (attrs->path && git__suffixcmp(attrs->path, GIT_ATTR_FILE) == 0) {
- context = git__strndup(attrs->path,
- strlen(attrs->path) - strlen(GIT_ATTR_FILE));
- GITERR_CHECK_ALLOC(context);
+ /* if subdir file path, convert context for file paths */
+ if (attrs->key && git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) {
+ context = attrs->key + 2;
+ context[strlen(context) - strlen(GIT_ATTR_FILE)] = '\0';
}
while (!error && *scan) {
@@ -112,28 +107,34 @@ int git_attr_file__from_buffer(
}
git_attr_rule__free(rule);
- git__free(context);
+
+ /* restore file path used for context */
+ if (context)
+ context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */
return error;
}
-int git_attr_file__from_file(
- git_repository *repo, const char *path, git_attr_file *file)
+int git_attr_file__new_and_load(
+ git_attr_file **attrs_ptr,
+ const char *path)
{
int error;
- git_buf fbuf = GIT_BUF_INIT;
+ git_buf content = GIT_BUF_INIT;
- assert(path && file);
+ if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)
+ return error;
- if (file->path == NULL && git_attr_file__set_path(repo, path, file) < 0)
- return -1;
-
- if (git_futils_readbuffer(&fbuf, path) < 0)
- return -1;
+ if (!(error = git_futils_readbuffer(&content, path)))
+ error = git_attr_file__parse_buffer(
+ NULL, git_buf_cstr(&content), *attrs_ptr);
- error = git_attr_file__from_buffer(repo, fbuf.ptr, file);
+ git_buf_free(&content);
- git_buf_free(&fbuf);
+ if (error) {
+ git_attr_file__free(*attrs_ptr);
+ *attrs_ptr = NULL;
+ }
return error;
}
@@ -151,9 +152,6 @@ void git_attr_file__free(git_attr_file *file)
git_vector_free(&file->rules);
- git__free(file->path);
- file->path = NULL;
-
if (file->pool_is_allocated) {
git_pool_clear(file->pool);
git__free(file->pool);
@@ -504,7 +502,7 @@ int git_attr_assignment__parse(
assign->value = git_attr__false;
scan++;
} else if (*scan == '!') {
- assign->value = NULL; /* explicit unspecified state */
+ assign->value = git_attr__unset; /* explicit unspecified state */
scan++;
} else if (*scan == '#') /* comment rest of line */
break;
diff --git a/src/attr_file.h b/src/attr_file.h
index 10851bc49..ec488c4dc 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -48,7 +48,7 @@ typedef struct {
} git_attr_assignment;
typedef struct {
- char *path; /* cache the path this was loaded from */
+ char *key; /* cache "source#path" this was loaded from */
git_vector rules; /* vector of <rule*> or <fnmatch*> */
git_pool *pool;
bool pool_is_allocated;
@@ -61,20 +61,25 @@ typedef struct {
int is_dir;
} git_attr_path;
+typedef enum {
+ GIT_ATTR_FILE_FROM_FILE = 0,
+ GIT_ATTR_FILE_FROM_INDEX = 1
+} git_attr_file_source;
+
/*
* git_attr_file API
*/
-extern int git_attr_file__new(git_attr_file **attrs_ptr, git_pool *pool);
+extern int git_attr_file__new(
+ git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool);
+
+extern int git_attr_file__new_and_load(
+ git_attr_file **attrs_ptr, const char *path);
+
extern void git_attr_file__free(git_attr_file *file);
-extern int git_attr_file__from_buffer(
+extern int git_attr_file__parse_buffer(
git_repository *repo, const char *buf, git_attr_file *file);
-extern int git_attr_file__from_file(
- git_repository *repo, const char *path, git_attr_file *file);
-
-extern int git_attr_file__set_path(
- git_repository *repo, const char *path, git_attr_file *file);
extern int git_attr_file__lookup_one(
git_attr_file *file,
diff --git a/src/crlf.c b/src/crlf.c
index b495d2de0..5d09a1f40 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -82,7 +82,8 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con
const char *attr_vals[NUM_CONV_ATTRS];
int error;
- error = git_attr_get_many(repo, path, NUM_CONV_ATTRS, attr_names, attr_vals);
+ error = git_attr_get_many(
+ repo, 0, path, NUM_CONV_ATTRS, attr_names, attr_vals);
if (error == GIT_ENOTFOUND) {
ca->crlf_action = GIT_CRLF_GUESS;
diff --git a/src/diff_output.c b/src/diff_output.c
index ca28fd01e..c380db996 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -103,7 +103,7 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
{
const char *value;
- if (git_attr_get(repo, file->path, "diff", &value) < 0)
+ if (git_attr_get(repo, 0, file->path, "diff", &value) < 0)
return -1;
if (GIT_ATTR_FALSE(value))
diff --git a/src/ignore.c b/src/ignore.c
index 20b96c602..6f70b972d 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -5,29 +5,22 @@
#define GIT_IGNORE_FILE_INREPO "info/exclude"
#define GIT_IGNORE_FILE ".gitignore"
-static int load_ignore_file(
- git_repository *repo, const char *path, git_attr_file *ignores)
+static int parse_ignore_file(
+ git_repository *repo, const char *buffer, git_attr_file *ignores)
{
int error;
- git_buf fbuf = GIT_BUF_INIT;
git_attr_fnmatch *match = NULL;
const char *scan = NULL;
char *context = NULL;
- if (ignores->path == NULL) {
- if (git_attr_file__set_path(repo, path, ignores) < 0)
- return -1;
- }
+ GIT_UNUSED(repo);
- if (git__suffixcmp(ignores->path, GIT_IGNORE_FILE) == 0) {
- context = git__strndup(ignores->path,
- strlen(ignores->path) - strlen(GIT_IGNORE_FILE));
- GITERR_CHECK_ALLOC(context);
+ if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) {
+ context = ignores->key + 2;
+ context[strlen(context) - strlen(GIT_IGNORE_FILE)] = '\0';
}
- error = git_futils_readbuffer(&fbuf, path);
-
- scan = fbuf.ptr;
+ scan = buffer;
while (!error && *scan) {
if (!match) {
@@ -54,23 +47,27 @@ static int load_ignore_file(
}
}
- git_buf_free(&fbuf);
git__free(match);
- git__free(context);
+ /* restore file path used for context */
+ if (context)
+ context[strlen(context)] = '.'; /* first char of GIT_IGNORE_FILE */
return error;
}
-#define push_ignore(R,S,B,F) \
- git_attr_cache__push_file((R),(S),(B),(F),load_ignore_file)
+#define push_ignore_file(R,S,B,F) \
+ git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(S))
static int push_one_ignore(void *ref, git_buf *path)
{
git_ignores *ign = (git_ignores *)ref;
- return push_ignore(ign->repo, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
+ return push_ignore_file(ign->repo, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
}
-int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ignores)
+int git_ignore__for_path(
+ git_repository *repo,
+ const char *path,
+ git_ignores *ignores)
{
int error = 0;
const char *workdir = git_repository_workdir(repo);
@@ -86,30 +83,37 @@ int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ig
(error = git_attr_cache__init(repo)) < 0)
goto cleanup;
- /* translate path into directory within workdir */
- if ((error = git_path_find_dir(&ignores->dir, path, workdir)) < 0)
+ /* given a unrooted path in a non-bare repo, resolve it */
+ if (workdir && git_path_root(path) < 0)
+ error = git_path_find_dir(&ignores->dir, path, workdir);
+ else
+ error = git_buf_sets(&ignores->dir, path);
+ if (error < 0)
goto cleanup;
/* set up internals */
- error = git_attr_cache__lookup_or_create_file(
- repo, GIT_IGNORE_INTERNAL, NULL, NULL, &ignores->ign_internal);
+ error = git_attr_cache__internal_file(
+ repo, GIT_IGNORE_INTERNAL, &ignores->ign_internal);
if (error < 0)
goto cleanup;
/* load .gitignore up the path */
- error = git_path_walk_up(&ignores->dir, workdir, push_one_ignore, ignores);
- if (error < 0)
- goto cleanup;
+ if (workdir != NULL) {
+ error = git_path_walk_up(
+ &ignores->dir, workdir, push_one_ignore, ignores);
+ if (error < 0)
+ goto cleanup;
+ }
/* load .git/info/exclude */
- error = push_ignore(repo, &ignores->ign_global,
+ error = push_ignore_file(repo, &ignores->ign_global,
git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
if (error < 0)
goto cleanup;
/* load core.excludesfile */
if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
- error = push_ignore(repo, &ignores->ign_global, NULL,
+ error = push_ignore_file(repo, &ignores->ign_global, NULL,
git_repository_attr_cache(repo)->cfg_excl_file);
cleanup:
@@ -124,7 +128,7 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir)
if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
return -1;
else
- return push_ignore(
+ return push_ignore_file(
ign->repo, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
}
@@ -132,7 +136,7 @@ int git_ignore__pop_dir(git_ignores *ign)
{
if (ign->ign_path.length > 0) {
git_attr_file *file = git_vector_last(&ign->ign_path);
- if (git__suffixcmp(ign->dir.ptr, file->path) == 0)
+ if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)
git_vector_pop(&ign->ign_path);
git_buf_rtruncate_at_char(&ign->dir, '/');
}
@@ -163,7 +167,8 @@ static bool ignore_lookup_in_rules(
return false;
}
-int git_ignore__lookup(git_ignores *ignores, const char *pathname, int *ignored)
+int git_ignore__lookup(
+ git_ignores *ignores, const char *pathname, int *ignored)
{
unsigned int i;
git_attr_file *file;
diff --git a/src/ignore.h b/src/ignore.h
index 49f72bf25..809d2edbd 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -25,13 +25,14 @@ typedef struct {
git_vector ign_global;
} git_ignores;
-extern int git_ignore__for_path(
- git_repository *repo, const char *path, git_ignores *ign);
+extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
+
extern int git_ignore__pop_dir(git_ignores *ign);
extern void git_ignore__free(git_ignores *ign);
+
extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored);
#endif
diff --git a/src/object.c b/src/object.c
index 8e8eac4e3..7189d60b1 100644
--- a/src/object.c
+++ b/src/object.c
@@ -292,3 +292,42 @@ size_t git_object__size(git_otype type)
return git_objects_table[type].size;
}
+int git_object__resolve_to_type(git_object **obj, git_otype type)
+{
+ int error = 0;
+ git_object *scan, *next;
+
+ if (type == GIT_OBJ_ANY)
+ return 0;
+
+ scan = *obj;
+
+ while (!error && scan && git_object_type(scan) != type) {
+
+ switch (git_object_type(scan)) {
+ case GIT_OBJ_COMMIT:
+ {
+ git_tree *tree = NULL;
+ error = git_commit_tree(&tree, (git_commit *)scan);
+ next = (git_object *)tree;
+ break;
+ }
+
+ case GIT_OBJ_TAG:
+ error = git_tag_target(&next, (git_tag *)scan);
+ break;
+
+ default:
+ giterr_set(GITERR_REFERENCE, "Object does not resolve to type");
+ error = -1;
+ next = NULL;
+ break;
+ }
+
+ git_object_free(scan);
+ scan = next;
+ }
+
+ *obj = scan;
+ return error;
+}
diff --git a/src/repository.c b/src/repository.c
index cfabee420..d4de38104 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -862,3 +862,23 @@ int git_repository_is_bare(git_repository *repo)
assert(repo);
return repo->is_bare;
}
+
+int git_repository_head_tree(git_tree **tree, git_repository *repo)
+{
+ git_oid head_oid;
+ git_object *obj = NULL;
+
+ if (git_reference_name_to_oid(&head_oid, repo, GIT_HEAD_FILE) < 0) {
+ /* cannot resolve HEAD - probably brand new repo */
+ giterr_clear();
+ *tree = NULL;
+ return 0;
+ }
+
+ if (git_object_lookup(&obj, repo, &head_oid, GIT_OBJ_ANY) < 0 ||
+ git_object__resolve_to_type(&obj, GIT_OBJ_TREE) < 0)
+ return -1;
+
+ *tree = (git_tree *)obj;
+ return 0;
+}
diff --git a/src/repository.h b/src/repository.h
index 1ffac58f1..91c69a655 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -98,6 +98,8 @@ struct git_repository {
* export */
void git_object__free(void *object);
+int git_object__resolve_to_type(git_object **obj, git_otype type);
+
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
@@ -106,6 +108,8 @@ GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
return &repo->attrcache;
}
+int git_repository_head_tree(git_tree **tree, git_repository *repo);
+
/*
* Weak pointers to repository internals.
*
diff --git a/src/status.c b/src/status.c
index 356cbeb98..ff8535c66 100644
--- a/src/status.c
+++ b/src/status.c
@@ -18,41 +18,6 @@
#include "git2/diff.h"
#include "diff.h"
-static int resolve_head_to_tree(git_tree **tree, git_repository *repo)
-{
- git_oid head_oid;
- git_object *obj = NULL;
-
- if (git_reference_name_to_oid(&head_oid, repo, GIT_HEAD_FILE) < 0) {
- /* cannot resolve HEAD - probably brand new repo */
- giterr_clear();
- *tree = NULL;
- return 0;
- }
-
- if (git_object_lookup(&obj, repo, &head_oid, GIT_OBJ_ANY) < 0)
- goto fail;
-
- switch (git_object_type(obj)) {
- case GIT_OBJ_TREE:
- *tree = (git_tree *)obj;
- break;
- case GIT_OBJ_COMMIT:
- if (git_commit_tree(tree, (git_commit *)obj) < 0)
- goto fail;
- git_object_free(obj);
- break;
- default:
- goto fail;
- }
-
- return 0;
-
-fail:
- git_object_free(obj);
- return -1;
-}
-
static unsigned int index_delta2status(git_delta_t index_status)
{
unsigned int st = GIT_STATUS_CURRENT;
@@ -120,11 +85,8 @@ int git_status_foreach_ext(
assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
- switch (resolve_head_to_tree(&head, repo)) {
- case 0: break;
- case GIT_ENOTFOUND: return 0;
- default: return -1;
- }
+ if ((err = git_repository_head_tree(&head, repo)) < 0)
+ return err;
memset(&diffopt, 0, sizeof(diffopt));
memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
@@ -405,7 +367,7 @@ int git_status_file(
status_entry_update_from_index(e, index);
/* Try to find file in HEAD */
- if ((error = resolve_head_to_tree(&tree, repo)) < 0)
+ if ((error = git_repository_head_tree(&tree, repo)) < 0)
goto cleanup;
if (tree != NULL) {