Commit 4ef14af9 by Vicent Martí

Merge pull request #664 from arrbee/attrs-from-index

Support git attrs from index (and bare repo)
parents f95e8cc0 282283ac
......@@ -43,6 +43,7 @@ Ramsay Jones
Robert G. Jakabosky
Romain Geissler
Romain Muller
Russell Belfer
Sakari Jokinen
Sam
Sarath Lakshman
......
......@@ -19,42 +19,186 @@
*/
GIT_BEGIN_DECL
/**
* GIT_ATTR_TRUE checks if an attribute is set on. In core git
* parlance, this the value for "Set" attributes.
*
* For example, if the attribute file contains:
*
* *.c foo
*
* Then for file `xyz.c` looking up attribute "foo" gives a value for
* which `GIT_ATTR_TRUE(value)` is true.
*/
#define GIT_ATTR_TRUE(attr) ((attr) == git_attr__true)
/**
* GIT_ATTR_FALSE checks if an attribute is set off. In core git
* parlance, this is the value for attributes that are "Unset" (not to
* be confused with values that a "Unspecified").
*
* For example, if the attribute file contains:
*
* *.h -foo
*
* Then for file `zyx.h` looking up attribute "foo" gives a value for
* which `GIT_ATTR_FALSE(value)` is true.
*/
#define GIT_ATTR_FALSE(attr) ((attr) == git_attr__false)
#define GIT_ATTR_UNSPECIFIED(attr) ((attr) == NULL)
/**
* GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified. This
* may be due to the attribute not being mentioned at all or because
* the attribute was explicitly set unspecified via the `!` operator.
*
* For example, if the attribute file contains:
*
* *.c foo
* *.h -foo
* onefile.c !foo
*
* Then for `onefile.c` looking up attribute "foo" yields a value with
* `GIT_ATTR_UNSPECIFIED(value)` of true. Also, looking up "foo" on
* file `onefile.rb` or looking up "bar" on any file will all give
* `GIT_ATTR_UNSPECIFIED(value)` of true.
*/
#define GIT_ATTR_UNSPECIFIED(attr) (!(attr) || (attr) == git_attr__unset)
/**
* GIT_ATTR_SET_TO_VALUE checks if an attribute is set to a value (as
* opposied to TRUE, FALSE or UNSPECIFIED). This would be the case if
* for a file with something like:
*
* *.txt eol=lf
*
* Given this, looking up "eol" for `onefile.txt` will give back the
* string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true.
*/
#define GIT_ATTR_SET_TO_VALUE(attr) \
((attr) && (attr) != git_attr__unset && \
(attr) != git_attr__true && (attr) != git_attr__false)
GIT_EXTERN(const char *) git_attr__true;
GIT_EXTERN(const char *) git_attr__false;
GIT_EXTERN(const char *) git_attr__unset;
/**
* Check attribute flags: Reading values from index and working directory.
*
* When checking attributes, it is possible to check attribute files
* in both the working directory (if there is one) and the index (if
* there is one). You can explicitly choose where to check and in
* which order using the following flags.
*
* Core git usually checks the working directory then the index,
* except during a checkout when it checks the index first. It will
* use index only for creating archives or for a bare repo (if an
* index has been specified for the bare repo).
*/
#define GIT_ATTR_CHECK_FILE_THEN_INDEX 0
#define GIT_ATTR_CHECK_INDEX_THEN_FILE 1
#define GIT_ATTR_CHECK_INDEX_ONLY 2
/**
* Check attribute flags: Using the system attributes file.
*
* Normally, attribute checks include looking in the /etc (or system
* equivalent) directory for a `gitattributes` file. Passing this
* flag will cause attribute checks to ignore that file.
*/
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
/**
* Lookup attribute for path returning string caller must free
* Look up the value of one git attribute for path.
*
* @param repo The repository containing the path.
* @param flags A combination of GIT_ATTR_CHECK... flags.
* @param path The path to check for attributes. Relative paths are
* interpreted relative to the repo root. The file does
* not have to exist, but if it does not, then it will be
* treated as a plain file (not a directory).
* @param name The name of the attribute to look up.
* @param value Output of the value of the attribute. Use the GIT_ATTR_...
* macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
* use the string value for attributes set to a value. You
* should NOT modify or free this value.
*/
GIT_EXTERN(int) git_attr_get(
git_repository *repo, const char *path, const char *name,
git_repository *repo,
uint32_t flags,
const char *path,
const char *name,
const char **value);
/**
* Lookup list of attributes for path, populating array of strings
* Look up a list of git attributes for path.
*
* Use this if you have a known list of attributes that you want to
* look up in a single call. This is somewhat more efficient than
* calling `git_attr_get()` multiple times.
*
* For example, you might write:
*
* const char *attrs[] = { "crlf", "diff", "foo" };
* const char **values[3];
* git_attr_get_many(repo, 0, "my/fun/file.c", 3, attrs, values);
*
* Then you could loop through the 3 values to get the settings for
* the three attributes you asked about.
*
* @param repo The repository containing the path.
* @param flags A combination of GIT_ATTR_CHECK... flags.
* @param path The path inside the repo to check attributes. This
* does not have to exist, but if it does not, then
* it will be treated as a plain file (i.e. not a directory).
* @param num_attr The number of attributes being looked up
* @param names An array of num_attr strings containing attribute names.
* @param values An array of num_attr entries that will have string
* pointers written into it for the values of the attributes.
* You should not modify or free the values that are written
* into this array (although of course, you should free the
* array itself if you allocated it).
*/
GIT_EXTERN(int) git_attr_get_many(
git_repository *repo, const char *path,
size_t num_attr, const char **names,
git_repository *repo,
uint32_t flags,
const char *path,
size_t num_attr,
const char **names,
const char **values);
/**
* Perform an operation on each attribute of a path.
* Loop over all the git attributes for a path.
*
* @param repo The repository containing the path.
* @param flags A combination of GIT_ATTR_CHECK... flags.
* @param path The path inside the repo to check attributes. This
* does not have to exist, but if it does not, then
* it will be treated as a plain file (i.e. not a directory).
* @param callback The function that will be invoked on each attribute
* and attribute value. The name parameter will be the name
* of the attribute and the value will be the value it is
* set to, including possibly NULL if the attribute is
* explicitly set to UNSPECIFIED using the ! sign. This
* will be invoked only once per attribute name, even if
* there are multiple rules for a given file. The highest
* priority rule will be used.
* @param payload Passed on as extra parameter to callback function.
*/
GIT_EXTERN(int) git_attr_foreach(
git_repository *repo, const char *path,
git_repository *repo,
uint32_t flags,
const char *path,
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.
* Call this if you have reason to believe that the attributes files on
* disk no longer match the cached contents of memory. This will cause
* the attributes files to be reloaded the next time that an attribute
* access function is called.
*/
GIT_EXTERN(void) git_attr_cache_flush(
git_repository *repo);
......@@ -62,7 +206,7 @@ GIT_EXTERN(void) git_attr_cache_flush(
/**
* Add a macro definition.
*
* Macros will automatically be loaded from the top level .gitattributes
* 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:
......
......@@ -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,231 @@ 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;
}
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;
if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
cache_key += strlen(git_repository_workdir(repo));
entry = git_index_get(index, error);
return git_strmap_exists(files, cache_key);
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;
khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename);
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);
if (git_strmap_valid_index(cache->files, cache_pos)) {
*file = git_strmap_value_at(cache->files, cache_pos);
return 0;
}
if (loader && git_path_exists(filename) == false) {
*file_ptr = NULL;
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);
git_strmap_insert(cache->files, (*file)->key + 2, *file, error);
if (error > 0)
error = 0;
}
if (error < 0) {
git_attr_file__free(file);
file = NULL;
}
*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 *))
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);
error = git_attr_cache__lookup_or_create_file(
repo, cache_key, filename, loader, &file);
/* 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 (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 ((error = git_attr_file__new(&file, source, relfile, &cache->pool)) < 0)
goto finish;
if (parse && (error = parse(repo, content, file)) < 0)
goto finish;
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 +450,11 @@ static int collect_attr_files(
git_vector_init(files, 4, NULL) < 0)
return -1;
/* 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 +465,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.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;
}
if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
if (!error)
error = push_attrs(repo, files, NULL, dir.ptr);
error = push_attr_file(repo, files, NULL, dir.ptr);
else if (error == GIT_ENOTFOUND)
error = 0;
}
cleanup:
if (error < 0)
......
......@@ -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(
git_repository *repo,
const char *key,
const char *filename,
int (*loader)(git_repository *, const char *, git_attr_file *),
git_attr_file **file_ptr);
extern 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 *));
git_attr_file_source source,
git_attr_file_parser parse,
git_vector *stack);
extern int git_attr_cache__internal_file(
git_repository *repo,
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
#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;
assert(path && file);
git_buf content = GIT_BUF_INIT;
if (file->path == NULL && git_attr_file__set_path(repo, path, file) < 0)
return -1;
if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)
return error;
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;
......
......@@ -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,
......
......@@ -265,6 +265,7 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
cvar_free(old_var);
if (config_write(b, key, NULL, value) < 0) {
git_strmap_delete(b->values, var->key);
cvar_free(var);
return -1;
}
......
......@@ -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;
......
......@@ -506,7 +506,7 @@ static int diff_from_iterators(
git_diff_list **diff_ptr)
{
const git_index_entry *oitem, *nitem;
char *ignore_prefix = NULL;
git_buf ignore_prefix = GIT_BUF_INIT;
git_diff_list *diff = git_diff_list_alloc(repo, opts);
if (!diff)
goto fail;
......@@ -536,8 +536,8 @@ static int diff_from_iterators(
git_delta_t delta_type = GIT_DELTA_ADDED;
/* contained in ignored parent directory, so this can be skipped. */
if (ignore_prefix != NULL &&
git__prefixcmp(nitem->path, ignore_prefix) == 0)
if (git_buf_len(&ignore_prefix) &&
git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
{
if (git_iterator_advance(new_iter, &nitem) < 0)
goto fail;
......@@ -555,7 +555,7 @@ static int diff_from_iterators(
(oitem && git__prefixcmp(oitem->path, nitem->path) == 0))
{
if (is_ignored)
ignore_prefix = nitem->path;
git_buf_sets(&ignore_prefix, nitem->path);
if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
goto fail;
......@@ -589,12 +589,16 @@ static int diff_from_iterators(
git_iterator_free(old_iter);
git_iterator_free(new_iter);
git_buf_free(&ignore_prefix);
*diff_ptr = diff;
return 0;
fail:
git_iterator_free(old_iter);
git_iterator_free(new_iter);
git_buf_free(&ignore_prefix);
git_diff_list_free(diff);
*diff_ptr = NULL;
return -1;
......
......@@ -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))
......
......@@ -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;
int error = 0;
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 (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;
......
......@@ -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
......@@ -146,7 +146,7 @@ int git_object_lookup_prefix(
}
if (error < 0)
return -1;
return error;
if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
git_odb_object_free(odb_obj);
......@@ -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;
}
......@@ -589,7 +589,7 @@ int git_odb_read_prefix(
}
if (found == 0)
return git_odb__error_notfound("no match for prefix");
return git_odb__error_notfound("no match for prefix", short_id);
if (found > 1)
return git_odb__error_ambiguous("multiple matches for prefix");
......@@ -684,9 +684,15 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi
return error;
}
int git_odb__error_notfound(const char *message)
int git_odb__error_notfound(const char *message, const git_oid *oid)
{
if (oid != NULL) {
char oid_str[GIT_OID_HEXSZ + 1];
git_oid_tostr(oid_str, sizeof(oid_str), oid);
giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str);
} else
giterr_set(GITERR_ODB, "Object not found - %s", message);
return GIT_ENOTFOUND;
}
......
......@@ -70,7 +70,7 @@ int git_odb__hashlink(git_oid *out, const char *path);
/*
* Generate a GIT_ENOTFOUND error for the ODB.
*/
int git_odb__error_notfound(const char *message);
int git_odb__error_notfound(const char *message, const git_oid *oid);
/*
* Generate a GIT_EAMBIGUOUS error for the ODB.
......
......@@ -528,7 +528,7 @@ static int locate_object_short_oid(
/* Check that directory exists */
if (git_path_isdir(object_location->ptr) == false)
return git_odb__error_notfound("failed to locate from short oid");
return git_odb__error_notfound("no matching loose object for prefix", short_oid);
state.dir_len = git_buf_len(object_location);
state.short_oid_len = len;
......@@ -541,7 +541,7 @@ static int locate_object_short_oid(
return error;
if (!state.found)
return git_odb__error_notfound("failed to locate from short oid");
return git_odb__error_notfound("no matching loose object for prefix", short_oid);
/* Convert obtained hex formatted oid to raw */
error = git_oid_fromstr(res_oid, (char *)state.res_oid);
......@@ -590,7 +590,7 @@ static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_
raw.type = GIT_OBJ_BAD;
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
error = git_odb__error_notfound("in loose backend");
error = git_odb__error_notfound("no matching loose object", oid);
else if ((error = read_header_loose(&raw, &object_path)) == 0) {
*len_p = raw.len;
*type_p = raw.type;
......@@ -610,7 +610,7 @@ static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p
assert(backend && oid);
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
error = git_odb__error_notfound("in loose backend");
error = git_odb__error_notfound("no matching loose object", oid);
else if ((error = read_loose(&raw, &object_path)) == 0) {
*buffer_p = raw.data;
*len_p = raw.len;
......
......@@ -242,7 +242,7 @@ static int packfile_refresh_all(struct pack_backend *backend)
return 0;
if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
return git_odb__error_notfound("failed to refresh packfiles");
return git_odb__error_notfound("failed to refresh packfiles", NULL);
if (st.st_mtime != backend->pack_folder_mtime) {
git_buf path = GIT_BUF_INIT;
......@@ -288,7 +288,7 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
}
}
return git_odb__error_notfound("failed to find pack entry");
return git_odb__error_notfound("failed to find pack entry", oid);
}
static int pack_entry_find_prefix(
......@@ -330,7 +330,7 @@ static int pack_entry_find_prefix(
}
if (!found)
return git_odb__error_notfound("failed to find pack entry");
return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
else if (found > 1)
return git_odb__error_ambiguous("found multiple pack entries");
else
......
......@@ -375,6 +375,18 @@ int git_packfile_unpack(
return error;
}
static void *use_git_alloc(void *opaq, unsigned int count, unsigned int size)
{
GIT_UNUSED(opaq);
return git__calloc(count, size);
}
static void use_git_free(void *opaq, void *ptr)
{
GIT_UNUSED(opaq);
git__free(ptr);
}
int packfile_unpack_compressed(
git_rawobj *obj,
struct git_pack_file *p,
......@@ -393,6 +405,8 @@ int packfile_unpack_compressed(
memset(&stream, 0, sizeof(stream));
stream.next_out = buffer;
stream.avail_out = (uInt)size + 1;
stream.zalloc = use_git_alloc;
stream.zfree = use_git_free;
st = inflateInit(&stream);
if (st != Z_OK) {
......@@ -541,7 +555,7 @@ static int packfile_open(struct git_pack_file *p)
assert(p->index_map.data);
if (!p->index_map.data && pack_index_open(p) < 0)
return git_odb__error_notfound("failed to open packfile");
return git_odb__error_notfound("failed to open packfile", NULL);
/* TODO: open with noatime */
p->mwf.fd = git_futils_open_ro(p->pack_name);
......@@ -615,7 +629,7 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
path_len -= strlen(".idx");
if (path_len < 1) {
git__free(p);
return git_odb__error_notfound("invalid packfile path");
return git_odb__error_notfound("invalid packfile path", NULL);
}
memcpy(p->pack_name, path, path_len);
......@@ -627,7 +641,7 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
strcpy(p->pack_name + path_len, ".pack");
if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
git__free(p);
return git_odb__error_notfound("packfile not found");
return git_odb__error_notfound("packfile not found", NULL);
}
/* ok, it looks sane as far as we can check without
......@@ -733,11 +747,10 @@ static int pack_entry_find_offset(
if (pos < (int)p->num_objects) {
current = index + pos * stride;
if (!git_oid_ncmp(short_oid, (const git_oid *)current, len)) {
if (!git_oid_ncmp(short_oid, (const git_oid *)current, len))
found = 1;
}
}
}
if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)p->num_objects) {
/* Check for ambiguousity */
......@@ -749,7 +762,7 @@ static int pack_entry_find_offset(
}
if (!found)
return git_odb__error_notfound("failed to find offset for pack entry");
return git_odb__error_notfound("failed to find offset for pack entry", short_oid);
if (found > 1)
return git_odb__error_ambiguous("found multiple offsets for pack entry");
*offset_out = nth_packed_object_offset(p, pos);
......
......@@ -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;
}
......@@ -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.
*
......
......@@ -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) {
......
......@@ -36,18 +36,28 @@ typedef khash_t(str) git_strmap;
#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
#define git_strmap_delete_at(h, idx) kh_del(str, h, idx)
#define git_strmap_insert(h, key, val, err) do { \
khiter_t __pos = kh_put(str, h, key, &err); \
if (err >= 0) kh_val(h, __pos) = val; \
} while (0)
#define git_strmap_insert2(h, key, val, old, err) do { \
khiter_t __pos = kh_put(str, h, key, &err); \
if (err >= 0) { \
old = (err == 0) ? kh_val(h, __pos) : NULL; \
#define git_strmap_insert(h, key, val, rval) do { \
khiter_t __pos = kh_put(str, h, key, &rval); \
if (rval >= 0) { \
if (rval == 0) kh_key(h, __pos) = key; \
kh_val(h, __pos) = val; \
} } while (0)
#define git_strmap_insert2(h, key, val, oldv, rval) do { \
khiter_t __pos = kh_put(str, h, key, &rval); \
if (rval >= 0) { \
if (rval == 0) { \
oldv = kh_val(h, __pos); \
kh_key(h, __pos) = key; \
} else { oldv = NULL; } \
kh_val(h, __pos) = val; \
} } while (0)
#define git_strmap_delete(h, key) do { \
khiter_t __pos = git_strmap_lookup_index(h, key); \
if (git_strmap_valid_index(h, __pos)) \
git_strmap_delete_at(h, __pos); } while (0)
#define git_strmap_foreach kh_foreach
#define git_strmap_foreach_value kh_foreach_value
......
......@@ -11,9 +11,9 @@ void test_attr_file__simple_read(void)
git_attr_assignment *assign;
git_attr_rule *rule;
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->path);
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr0")));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2);
cl_assert(file->rules.length == 1);
rule = get_rule(0);
......@@ -37,9 +37,9 @@ void test_attr_file__match_variants(void)
git_attr_rule *rule;
git_attr_assignment *assign;
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->path);
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr1")));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2);
cl_assert(file->rules.length == 10);
/* let's do a thorough check of this rule, then just verify
......@@ -123,9 +123,9 @@ void test_attr_file__assign_variants(void)
git_attr_rule *rule;
git_attr_assignment *assign;
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file));
cl_assert_equal_s(cl_fixture("attr/attr2"), file->path);
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr2")));
cl_assert_equal_s(cl_fixture("attr/attr2"), file->key + 2);
cl_assert(file->rules.length == 11);
check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
......@@ -189,9 +189,8 @@ void test_attr_file__check_attr_examples(void)
git_attr_rule *rule;
git_attr_assignment *assign;
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file));
cl_assert_equal_s(cl_fixture("attr/attr3"), file->path);
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3")));
cl_assert_equal_s(cl_fixture("attr/attr3"), file->key + 2);
cl_assert(file->rules.length == 3);
rule = get_rule(0);
......@@ -214,7 +213,7 @@ void test_attr_file__check_attr_examples(void)
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule, 0);
cl_assert_equal_s("myAttr", assign->name);
cl_assert(assign->value == NULL);
cl_assert(GIT_ATTR_UNSPECIFIED(assign->value));
rule = get_rule(2);
cl_assert_equal_s("README", rule->match.pattern);
......
#include "clar_libgit2.h"
#include "git2/attr.h"
void test_attr_flags__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_attr_flags__bare(void)
{
git_repository *repo = cl_git_sandbox_init("testrepo.git");
const char *value;
cl_assert(git_repository_is_bare(repo));
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM, "README.md", "diff", &value));
cl_assert(GIT_ATTR_UNSPECIFIED(value));
}
void test_attr_flags__index_vs_workdir(void)
{
git_repository *repo = cl_git_sandbox_init("attr_index");
const char *value;
cl_assert(!git_repository_is_bare(repo));
/* wd then index */
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"README.md", "bar", &value));
cl_assert(GIT_ATTR_FALSE(value));
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"README.md", "blargh", &value));
cl_assert_equal_s(value, "goop");
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"README.txt", "foo", &value));
cl_assert(GIT_ATTR_FALSE(value));
/* index then wd */
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"README.md", "bar", &value));
cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"README.md", "blargh", &value));
cl_assert_equal_s(value, "garble");
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"README.txt", "foo", &value));
cl_assert(GIT_ATTR_TRUE(value));
}
void test_attr_flags__subdir(void)
{
git_repository *repo = cl_git_sandbox_init("attr_index");
const char *value;
/* wd then index */
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"sub/sub/README.md", "bar", &value));
cl_assert_equal_s(value, "1234");
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"sub/sub/README.txt", "another", &value));
cl_assert_equal_s(value, "one");
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"sub/sub/README.txt", "again", &value));
cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
"sub/sub/README.txt", "beep", &value));
cl_assert_equal_s(value, "10");
/* index then wd */
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"sub/sub/README.md", "bar", &value));
cl_assert_equal_s(value, "1337");
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"sub/sub/README.txt", "another", &value));
cl_assert_equal_s(value, "one");
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"sub/sub/README.txt", "again", &value));
cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_get(
repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
"sub/sub/README.txt", "beep", &value));
cl_assert_equal_s(value, "5");
}
......@@ -9,9 +9,8 @@ void test_attr_lookup__simple(void)
git_attr_path path;
const char *value = NULL;
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->path);
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr0")));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2);
cl_assert(file->rules.length == 1);
cl_git_pass(git_attr_path__init(&path, "test", NULL));
......@@ -130,9 +129,8 @@ void test_attr_lookup__match_variants(void)
{ NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->path);
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr1")));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2);
cl_assert(file->rules.length == 10);
cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL));
......@@ -192,8 +190,7 @@ void test_attr_lookup__assign_variants(void)
{ NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file));
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr2")));
cl_assert(file->rules.length == 11);
run_test_cases(file, cases, 0);
......@@ -228,8 +225,7 @@ void test_attr_lookup__check_attr_examples(void)
{ NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file));
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3")));
cl_assert(file->rules.length == 3);
run_test_cases(file, cases, 0);
......@@ -254,8 +250,10 @@ void test_attr_lookup__from_buffer(void)
{ NULL, NULL, 0, NULL }
};
cl_git_pass(git_attr_file__new(&file, NULL));
cl_git_pass(git_attr_file__from_buffer(NULL, "a* foo\nabc bar\n* baz", file));
cl_git_pass(git_attr_file__new(&file, 0, NULL, NULL));
cl_git_pass(git_attr_file__parse_buffer(NULL, "a* foo\nabc bar\n* baz", file));
cl_assert(file->rules.length == 3);
run_test_cases(file, cases, 0);
......
......@@ -64,13 +64,13 @@ void test_attr_repo__get_one(void)
for (scan = test_cases; scan->path != NULL; scan++) {
const char *value;
cl_git_pass(git_attr_get(g_repo, scan->path, scan->attr, &value));
cl_git_pass(git_attr_get(g_repo, 0, scan->path, scan->attr, &value));
attr_check_expected(scan->expected, scan->expected_str, value);
}
cl_assert(git_attr_cache__is_cached(g_repo, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(g_repo, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(g_repo, "sub/.gitattributes"));
cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(g_repo, 0, "sub/.gitattributes"));
}
void test_attr_repo__get_many(void)
......@@ -78,21 +78,21 @@ void test_attr_repo__get_many(void)
const char *names[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
const char *values[4];
cl_git_pass(git_attr_get_many(g_repo, "root_test1", 4, names, values));
cl_git_pass(git_attr_get_many(g_repo, 0, "root_test1", 4, names, values));
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(g_repo, "root_test2", 4, names, values));
cl_git_pass(git_attr_get_many(g_repo, 0, "root_test2", 4, names, values));
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_FALSE(values[1]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(g_repo, "sub/subdir_test1", 4, names, values));
cl_git_pass(git_attr_get_many(g_repo, 0, "sub/subdir_test1", 4, names, values));
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
......@@ -118,16 +118,17 @@ void test_attr_repo__foreach(void)
int count;
count = 0;
cl_git_pass(git_attr_foreach(g_repo, "root_test1", &count_attrs, &count));
cl_git_pass(git_attr_foreach(
g_repo, 0, "root_test1", &count_attrs, &count));
cl_assert(count == 2);
count = 0;
cl_git_pass(git_attr_foreach(g_repo, "sub/subdir_test1",
cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test1",
&count_attrs, &count));
cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */
count = 0;
cl_git_pass(git_attr_foreach(g_repo, "sub/subdir_test2.txt",
cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt",
&count_attrs, &count));
cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */
}
......@@ -136,19 +137,19 @@ void test_attr_repo__manpage_example(void)
{
const char *value;
cl_git_pass(git_attr_get(g_repo, "sub/abc", "foo", &value));
cl_git_pass(git_attr_get(g_repo, 0, "sub/abc", "foo", &value));
cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "bar", &value));
cl_git_pass(git_attr_get(g_repo, 0, "sub/abc", "bar", &value));
cl_assert(GIT_ATTR_UNSPECIFIED(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "baz", &value));
cl_git_pass(git_attr_get(g_repo, 0, "sub/abc", "baz", &value));
cl_assert(GIT_ATTR_FALSE(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "merge", &value));
cl_git_pass(git_attr_get(g_repo, 0, "sub/abc", "merge", &value));
cl_assert_equal_s("filfre", value);
cl_git_pass(git_attr_get(g_repo, "sub/abc", "frotz", &value));
cl_git_pass(git_attr_get(g_repo, 0, "sub/abc", "frotz", &value));
cl_assert(GIT_ATTR_UNSPECIFIED(value));
}
......@@ -159,7 +160,7 @@ void test_attr_repo__macros(void)
const char *names3[3] = { "macro2", "multi2", "multi3" };
const char *values[5];
cl_git_pass(git_attr_get_many(g_repo, "binfile", 5, names, values));
cl_git_pass(git_attr_get_many(g_repo, 0, "binfile", 5, names, values));
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
......@@ -167,7 +168,7 @@ void test_attr_repo__macros(void)
cl_assert(GIT_ATTR_FALSE(values[3]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
cl_git_pass(git_attr_get_many(g_repo, "macro_test", 5, names2, values));
cl_git_pass(git_attr_get_many(g_repo, 0, "macro_test", 5, names2, values));
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
......@@ -175,7 +176,7 @@ void test_attr_repo__macros(void)
cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_assert_equal_s("77", values[4]);
cl_git_pass(git_attr_get_many(g_repo, "macro_test", 3, names3, values));
cl_git_pass(git_attr_get_many(g_repo, 0, "macro_test", 3, names3, values));
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_FALSE(values[1]));
......@@ -188,7 +189,7 @@ void test_attr_repo__bad_macros(void)
"firstmacro", "secondmacro", "thirdmacro" };
const char *values[6];
cl_git_pass(git_attr_get_many(g_repo, "macro_bad", 6, names, values));
cl_git_pass(git_attr_get_many(g_repo, 0, "macro_bad", 6, names, values));
/* these three just confirm that the "mymacro" rule ran */
cl_assert(GIT_ATTR_UNSPECIFIED(values[0]));
......
......@@ -5,7 +5,6 @@ static git_repository *g_repo = NULL;
void test_diff_tree__initialize(void)
{
g_repo = cl_git_sandbox_init("attr");
}
void test_diff_tree__cleanup(void)
......@@ -19,15 +18,16 @@ void test_diff_tree__0(void)
const char *a_commit = "605812a";
const char *b_commit = "370fe9ec22";
const char *c_commit = "f5b0af1fb4f5c";
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
git_tree *c = resolve_commit_oid_to_tree(g_repo, c_commit);
git_tree *a, *b, *c;
git_diff_options opts = {0};
git_diff_list *diff = NULL;
diff_expects exp;
cl_assert(a);
cl_assert(b);
g_repo = cl_git_sandbox_init("attr");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
opts.context_lines = 1;
opts.interhunk_lines = 1;
......@@ -87,12 +87,7 @@ void test_diff_tree__options(void)
const char *b_commit = "605812ab7fe421fdd";
const char *c_commit = "f5b0af1fb4f5";
const char *d_commit = "a97cc019851";
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
git_tree *c = resolve_commit_oid_to_tree(g_repo, c_commit);
git_tree *d = resolve_commit_oid_to_tree(g_repo, d_commit);
git_tree *a, *b, *c, *d;
git_diff_options opts = {0};
git_diff_list *diff = NULL;
diff_expects actual;
......@@ -133,8 +128,12 @@ void test_diff_tree__options(void)
diff_expects *expected;
int i;
cl_assert(a);
cl_assert(b);
g_repo = cl_git_sandbox_init("attr");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
cl_assert((d = resolve_commit_oid_to_tree(g_repo, d_commit)) != NULL);
for (i = 0; test_expects[i].files > 0; i++) {
memset(&actual, 0, sizeof(actual)); /* clear accumulator */
......@@ -168,3 +167,44 @@ void test_diff_tree__options(void)
git_tree_free(c);
git_tree_free(d);
}
void test_diff_tree__bare(void)
{
const char *a_commit = "8496071c1b46c85";
const char *b_commit = "be3563ae3f79";
git_tree *a, *b;
git_diff_options opts = {0};
git_diff_list *diff = NULL;
diff_expects exp;
g_repo = cl_git_sandbox_init("testrepo.git");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
opts.context_lines = 1;
opts.interhunk_lines = 1;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, a, b, &diff));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 3);
cl_assert(exp.file_adds == 2);
cl_assert(exp.file_dels == 0);
cl_assert(exp.file_mods == 1);
cl_assert(exp.hunks == 3);
cl_assert(exp.lines == 4);
cl_assert(exp.line_ctxt == 0);
cl_assert(exp.line_adds == 3);
cl_assert(exp.line_dels == 1);
git_diff_list_free(diff);
git_tree_free(a);
git_tree_free(b);
}
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
Unnamed repository; edit this file 'description' to name the repository.
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 refs/heads/master
0000000000000000000000000000000000000000 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817070 -0700 commit (initial): Initial commit
67c1640e91ccbaf0793591be09bf572cf40c9a53 d441d7d88f52c28c2b23940ce4c33756748425f9 Russell Belfer <rb@github.com> 1335817296 -0700 commit: Adding some files in subtrees
d441d7d88f52c28c2b23940ce4c33756748425f9 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817353 -0700 HEAD^: updating HEAD
67c1640e91ccbaf0793591be09bf572cf40c9a53 58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 Russell Belfer <rb@github.com> 1335817372 -0700 commit: Adding subtree data
0000000000000000000000000000000000000000 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817070 -0700 commit (initial): Initial commit
67c1640e91ccbaf0793591be09bf572cf40c9a53 d441d7d88f52c28c2b23940ce4c33756748425f9 Russell Belfer <rb@github.com> 1335817296 -0700 commit: Adding some files in subtrees
d441d7d88f52c28c2b23940ce4c33756748425f9 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817353 -0700 HEAD^: updating HEAD
67c1640e91ccbaf0793591be09bf572cf40c9a53 58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 Russell Belfer <rb@github.com> 1335817372 -0700 commit: Adding subtree data
P pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
# pack-refs with: peeled
58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 refs/heads/master
3812cfef36615db1788d4e63f90028007e17a348
This is contains tests for when the index and work dir differ
This contains files for testing when the index and the workdir differ
* bar
*.txt -foo beep=10
*.md blargh=goop -bar
......@@ -47,8 +47,8 @@ void test_status_ignore__0(void)
}
/* confirm that ignore files were cached */
cl_assert(git_attr_cache__is_cached(g_repo, ".git/info/exclude"));
cl_assert(git_attr_cache__is_cached(g_repo, ".gitignore"));
cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/exclude"));
cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitignore"));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment