Commit 823c0e9c by Russell Belfer

Fix broken logic for attr cache invalidation

The checks to see if files were out of date in the attibute cache
was wrong because the cache-breaker data wasn't getting stored
correctly.  Additionally, when the cache-breaker triggered, the
old file data was being leaked.
parent e6e8530a
...@@ -305,6 +305,11 @@ ELSE () ...@@ -305,6 +305,11 @@ ELSE ()
ENDIF () ENDIF ()
IF (APPLE) # Apple deprecated OpenSSL IF (APPLE) # Apple deprecated OpenSSL
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")
# With clang, disable some annoying extra warnings
IF (NOT CMAKE_COMPILER_IS_GNUCC)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-const-variable -Wno-unused-function")
ENDIF()
ENDIF () ENDIF ()
IF (PROFILE) IF (PROFILE)
SET(CMAKE_C_FLAGS "-pg ${CMAKE_C_FLAGS}") SET(CMAKE_C_FLAGS "-pg ${CMAKE_C_FLAGS}")
......
...@@ -260,26 +260,26 @@ typedef struct { ...@@ -260,26 +260,26 @@ typedef struct {
} attr_walk_up_info; } attr_walk_up_info;
static int attr_decide_sources( static int attr_decide_sources(
uint32_t flags, bool has_wd, bool has_index, git_attr_cache_source *srcs) uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
{ {
int count = 0; int count = 0;
switch (flags & 0x03) { switch (flags & 0x03) {
case GIT_ATTR_CHECK_FILE_THEN_INDEX: case GIT_ATTR_CHECK_FILE_THEN_INDEX:
if (has_wd) if (has_wd)
srcs[count++] = GIT_ATTR_CACHE__FROM_FILE; srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
if (has_index) if (has_index)
srcs[count++] = GIT_ATTR_CACHE__FROM_INDEX; srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
break; break;
case GIT_ATTR_CHECK_INDEX_THEN_FILE: case GIT_ATTR_CHECK_INDEX_THEN_FILE:
if (has_index) if (has_index)
srcs[count++] = GIT_ATTR_CACHE__FROM_INDEX; srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
if (has_wd) if (has_wd)
srcs[count++] = GIT_ATTR_CACHE__FROM_FILE; srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
break; break;
case GIT_ATTR_CHECK_INDEX_ONLY: case GIT_ATTR_CHECK_INDEX_ONLY:
if (has_index) if (has_index)
srcs[count++] = GIT_ATTR_CACHE__FROM_INDEX; srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
break; break;
} }
...@@ -289,7 +289,7 @@ static int attr_decide_sources( ...@@ -289,7 +289,7 @@ static int attr_decide_sources(
static int push_attr_file( static int push_attr_file(
git_repository *repo, git_repository *repo,
git_vector *list, git_vector *list,
git_attr_cache_source source, git_attr_file_source source,
const char *base, const char *base,
const char *filename) const char *filename)
{ {
...@@ -297,7 +297,7 @@ static int push_attr_file( ...@@ -297,7 +297,7 @@ static int push_attr_file(
git_attr_file *file = NULL; git_attr_file *file = NULL;
error = git_attr_cache__get( error = git_attr_cache__get(
&file, repo, source, base, filename, git_attr_file__parse_buffer, NULL); &file, repo, source, base, filename, git_attr_file__parse_buffer);
if (error < 0) if (error < 0)
return error; return error;
...@@ -313,7 +313,7 @@ static int push_one_attr(void *ref, git_buf *path) ...@@ -313,7 +313,7 @@ static int push_one_attr(void *ref, git_buf *path)
{ {
int error = 0, n_src, i; int error = 0, n_src, i;
attr_walk_up_info *info = (attr_walk_up_info *)ref; attr_walk_up_info *info = (attr_walk_up_info *)ref;
git_attr_cache_source src[2]; git_attr_file_source src[2];
n_src = attr_decide_sources( n_src = attr_decide_sources(
info->flags, info->workdir != NULL, info->index != NULL, src); info->flags, info->workdir != NULL, info->index != NULL, src);
...@@ -367,7 +367,7 @@ static int collect_attr_files( ...@@ -367,7 +367,7 @@ static int collect_attr_files(
*/ */
error = push_attr_file( error = push_attr_file(
repo, files, GIT_ATTR_CACHE__FROM_FILE, repo, files, GIT_ATTR_FILE__FROM_FILE,
git_repository_path(repo), GIT_ATTR_FILE_INREPO); git_repository_path(repo), GIT_ATTR_FILE_INREPO);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
...@@ -385,7 +385,7 @@ static int collect_attr_files( ...@@ -385,7 +385,7 @@ static int collect_attr_files(
if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
error = push_attr_file( error = push_attr_file(
repo, files, GIT_ATTR_CACHE__FROM_FILE, repo, files, GIT_ATTR_FILE__FROM_FILE,
NULL, git_repository_attr_cache(repo)->cfg_attr_file); NULL, git_repository_attr_cache(repo)->cfg_attr_file);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
...@@ -395,7 +395,7 @@ static int collect_attr_files( ...@@ -395,7 +395,7 @@ static int collect_attr_files(
error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
if (!error) if (!error)
error = push_attr_file( error = push_attr_file(
repo, files, GIT_ATTR_CACHE__FROM_FILE, NULL, dir.ptr); repo, files, GIT_ATTR_FILE__FROM_FILE, NULL, dir.ptr);
else if (error == GIT_ENOTFOUND) { else if (error == GIT_ENOTFOUND) {
giterr_clear(); giterr_clear();
error = 0; error = 0;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include "repository.h" #include "repository.h"
#include "filebuf.h" #include "filebuf.h"
#include "attr_file.h" #include "attr_file.h"
#include "attrcache.h"
#include "git2/blob.h" #include "git2/blob.h"
#include "git2/tree.h" #include "git2/tree.h"
#include "index.h" #include "index.h"
...@@ -22,8 +23,8 @@ static void attr_file_free(git_attr_file *file) ...@@ -22,8 +23,8 @@ static void attr_file_free(git_attr_file *file)
int git_attr_file__new( int git_attr_file__new(
git_attr_file **out, git_attr_file **out,
git_attr_cache_entry *ce, git_attr_file_entry *entry,
git_attr_cache_source source) git_attr_file_source source)
{ {
git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file)); git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
GITERR_CHECK_ALLOC(attrs); GITERR_CHECK_ALLOC(attrs);
...@@ -40,7 +41,7 @@ int git_attr_file__new( ...@@ -40,7 +41,7 @@ int git_attr_file__new(
} }
GIT_REFCOUNT_INC(attrs); GIT_REFCOUNT_INC(attrs);
attrs->ce = ce; attrs->entry = entry;
attrs->source = source; attrs->source = source;
*out = attrs; *out = attrs;
return 0; return 0;
...@@ -95,42 +96,77 @@ static int attr_file_oid_from_index( ...@@ -95,42 +96,77 @@ static int attr_file_oid_from_index(
int git_attr_file__load( int git_attr_file__load(
git_attr_file **out, git_attr_file **out,
git_repository *repo, git_repository *repo,
git_attr_cache_entry *ce, git_attr_file_entry *entry,
git_attr_cache_source source, git_attr_file_source source,
git_attr_cache_parser parser, git_attr_file_parser parser)
void *payload)
{ {
int error = 0; int error = 0;
git_blob *blob = NULL; git_blob *blob = NULL;
git_buf content = GIT_BUF_INIT; git_buf content = GIT_BUF_INIT;
const char *data = NULL; const char *data = NULL;
git_attr_file *file; git_attr_file *file;
struct stat st;
*out = NULL; *out = NULL;
if (source == GIT_ATTR_CACHE__FROM_INDEX) { switch (source) {
case GIT_ATTR_FILE__IN_MEMORY:
/* in-memory attribute file doesn't need data */
break;
case GIT_ATTR_FILE__FROM_INDEX: {
git_oid id; git_oid id;
if ((error = attr_file_oid_from_index(&id, repo, ce->path)) < 0 || if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
(error = git_blob_lookup(&blob, repo, &id)) < 0) (error = git_blob_lookup(&blob, repo, &id)) < 0)
return error; return error;
data = git_blob_rawcontent(blob); data = git_blob_rawcontent(blob);
} else { break;
if ((error = git_futils_readbuffer(&content, ce->fullpath)) < 0) }
/* always return ENOTFOUND so item will just be skipped */ case GIT_ATTR_FILE__FROM_FILE: {
/* TODO: issue a warning once warnings API is available */ int fd;
if (p_stat(entry->fullpath, &st) < 0)
return git_path_set_error(errno, entry->fullpath, "stat");
if (S_ISDIR(st.st_mode))
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
/* For open or read errors, return ENOTFOUND to skip item */
/* TODO: issue warning when warning API is available */
if ((fd = git_futils_open_ro(entry->fullpath)) < 0)
return GIT_ENOTFOUND;
error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size);
p_close(fd);
if (error < 0)
return GIT_ENOTFOUND;
data = content.ptr; data = content.ptr;
break;
}
default:
giterr_set(GITERR_INVALID, "Unknown file source %d", source);
return -1;
} }
if ((error = git_attr_file__new(&file, ce, source)) < 0) if ((error = git_attr_file__new(&file, entry, source)) < 0)
goto cleanup; goto cleanup;
if (parser && (error = parser(repo, file, data, payload)) < 0) if (parser && (error = parser(repo, file, data)) < 0) {
git_attr_file__free(file); git_attr_file__free(file);
else goto cleanup;
*out = file; }
/* write cache breaker */
if (source == GIT_ATTR_FILE__FROM_INDEX)
git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
else if (source == GIT_ATTR_FILE__FROM_FILE)
git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
/* else always cacheable */
*out = file;
cleanup: cleanup:
git_blob_free(blob); git_blob_free(blob);
...@@ -144,18 +180,29 @@ int git_attr_file__out_of_date(git_repository *repo, git_attr_file *file) ...@@ -144,18 +180,29 @@ int git_attr_file__out_of_date(git_repository *repo, git_attr_file *file)
if (!file) if (!file)
return 1; return 1;
if (file->source == GIT_ATTR_CACHE__FROM_INDEX) { switch (file->source) {
case GIT_ATTR_FILE__IN_MEMORY:
return 0;
case GIT_ATTR_FILE__FROM_FILE:
return git_futils_filestamp_check(
&file->cache_data.stamp, file->entry->fullpath);
case GIT_ATTR_FILE__FROM_INDEX: {
int error; int error;
git_oid id; git_oid id;
if ((error = attr_file_oid_from_index(&id, repo, file->ce->path)) < 0) if ((error = attr_file_oid_from_index(
&id, repo, file->entry->path)) < 0)
return error; return error;
return (git_oid__cmp(&file->cache_data.oid, &id) != 0); return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
} }
return git_futils_filestamp_check( default:
&file->cache_data.stamp, file->ce->fullpath); giterr_set(GITERR_INVALID, "Invalid file type %d", file->source);
return -1;
}
} }
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
...@@ -166,22 +213,17 @@ static bool parse_optimized_patterns( ...@@ -166,22 +213,17 @@ static bool parse_optimized_patterns(
const char *pattern); const char *pattern);
int git_attr_file__parse_buffer( int git_attr_file__parse_buffer(
git_repository *repo, git_repository *repo, git_attr_file *attrs, const char *data)
git_attr_file *attrs,
const char *data,
void *payload)
{ {
int error = 0; int error = 0;
const char *scan = data, *context = NULL; const char *scan = data, *context = NULL;
git_attr_rule *rule = NULL; git_attr_rule *rule = NULL;
GIT_UNUSED(payload);
/* if subdir file path, convert context for file paths */ /* if subdir file path, convert context for file paths */
if (attrs->ce && if (attrs->entry &&
git_path_root(attrs->ce->path) < 0 && git_path_root(attrs->entry->path) < 0 &&
!git__suffixcmp(attrs->ce->path, "/" GIT_ATTR_FILE)) !git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE))
context = attrs->ce->path; context = attrs->entry->path;
if (git_mutex_lock(&attrs->lock) < 0) { if (git_mutex_lock(&attrs->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock attribute file"); giterr_set(GITERR_OS, "Failed to lock attribute file");
...@@ -268,19 +310,18 @@ int git_attr_file__lookup_one( ...@@ -268,19 +310,18 @@ int git_attr_file__lookup_one(
return 0; return 0;
} }
int git_attr_file__load_standalone( int git_attr_file__load_standalone(git_attr_file **out, const char *path)
git_attr_file **out,
const char *path)
{ {
int error; int error;
git_attr_file *file; git_attr_file *file;
git_buf content = GIT_BUF_INIT; git_buf content = GIT_BUF_INIT;
error = git_attr_file__new(&file, NULL, GIT_ATTR_CACHE__FROM_FILE); error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE);
if (error < 0) if (error < 0)
return error; return error;
error = git_attr_cache_entry__new(&file->ce, NULL, path, &file->pool); error = git_attr_cache__alloc_file_entry(
&file->entry, NULL, path, &file->pool);
if (error < 0) { if (error < 0) {
git_attr_file__free(file); git_attr_file__free(file);
return error; return error;
...@@ -290,7 +331,7 @@ int git_attr_file__load_standalone( ...@@ -290,7 +331,7 @@ int git_attr_file__load_standalone(
*/ */
if (!(error = git_futils_readbuffer(&content, path))) { if (!(error = git_futils_readbuffer(&content, path))) {
error = git_attr_file__parse_buffer(NULL, file, content.ptr, NULL); error = git_attr_file__parse_buffer(NULL, file, content.ptr);
git_buf_free(&content); git_buf_free(&content);
} }
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include "pool.h" #include "pool.h"
#include "buffer.h" #include "buffer.h"
#include "fileops.h" #include "fileops.h"
#include "attrcache.h"
#define GIT_ATTR_FILE ".gitattributes" #define GIT_ATTR_FILE ".gitattributes"
#define GIT_ATTR_FILE_INREPO "info/attributes" #define GIT_ATTR_FILE_INREPO "info/attributes"
...@@ -36,6 +35,14 @@ ...@@ -36,6 +35,14 @@
(GIT_ATTR_FNMATCH_ALLOWSPACE | \ (GIT_ATTR_FNMATCH_ALLOWSPACE | \
GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO) GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
typedef enum {
GIT_ATTR_FILE__IN_MEMORY = 0,
GIT_ATTR_FILE__FROM_FILE = 1,
GIT_ATTR_FILE__FROM_INDEX = 2,
GIT_ATTR_FILE_NUM_SOURCES = 3
} git_attr_file_source;
extern const char *git_attr__true; extern const char *git_attr__true;
extern const char *git_attr__false; extern const char *git_attr__false;
extern const char *git_attr__unset; extern const char *git_attr__unset;
...@@ -46,10 +53,10 @@ typedef struct { ...@@ -46,10 +53,10 @@ typedef struct {
unsigned int flags; unsigned int flags;
} git_attr_fnmatch; } git_attr_fnmatch;
struct git_attr_rule { typedef struct {
git_attr_fnmatch match; git_attr_fnmatch match;
git_vector assigns; /* vector of <git_attr_assignment*> */ git_vector assigns; /* vector of <git_attr_assignment*> */
}; } git_attr_rule;
typedef struct { typedef struct {
git_refcount unused; git_refcount unused;
...@@ -64,19 +71,32 @@ typedef struct { ...@@ -64,19 +71,32 @@ typedef struct {
const char *value; const char *value;
} git_attr_assignment; } git_attr_assignment;
struct git_attr_file { typedef struct git_attr_file_entry git_attr_file_entry;
typedef struct {
git_refcount rc; git_refcount rc;
git_mutex lock; git_mutex lock;
git_attr_cache_entry *ce; git_attr_file_entry *entry;
git_attr_cache_source source; git_attr_file_source source;
git_vector rules; /* vector of <rule*> or <fnmatch*> */ git_vector rules; /* vector of <rule*> or <fnmatch*> */
git_pool pool; git_pool pool;
union { union {
git_oid oid; git_oid oid;
git_futils_filestamp stamp; git_futils_filestamp stamp;
} cache_data; } cache_data;
} git_attr_file;
struct git_attr_file_entry {
git_attr_file *file[GIT_ATTR_FILE_NUM_SOURCES];
const char *path; /* points into fullpath */
char fullpath[GIT_FLEX_ARRAY];
}; };
typedef int (*git_attr_file_parser)(
git_repository *repo,
git_attr_file *file,
const char *data);
typedef struct { typedef struct {
git_buf full; git_buf full;
char *path; char *path;
...@@ -90,31 +110,26 @@ typedef struct { ...@@ -90,31 +110,26 @@ typedef struct {
int git_attr_file__new( int git_attr_file__new(
git_attr_file **out, git_attr_file **out,
git_attr_cache_entry *ce, git_attr_file_entry *entry,
git_attr_cache_source source); git_attr_file_source source);
void git_attr_file__free(git_attr_file *file); void git_attr_file__free(git_attr_file *file);
int git_attr_file__load( int git_attr_file__load(
git_attr_file **out, git_attr_file **out,
git_repository *repo, git_repository *repo,
git_attr_cache_entry *ce, git_attr_file_entry *ce,
git_attr_cache_source source, git_attr_file_source source,
git_attr_cache_parser parser, git_attr_file_parser parser);
void *payload);
int git_attr_file__load_standalone( int git_attr_file__load_standalone(
git_attr_file **out, git_attr_file **out, const char *path);
const char *path);
int git_attr_file__out_of_date( int git_attr_file__out_of_date(
git_repository *repo, git_attr_file *file); git_repository *repo, git_attr_file *file);
int git_attr_file__parse_buffer( int git_attr_file__parse_buffer(
git_repository *repo, git_repository *repo, git_attr_file *attrs, const char *data);
git_attr_file *attrs,
const char *data,
void *payload);
int git_attr_file__clear_rules( int git_attr_file__clear_rules(
git_attr_file *file, bool need_lock); git_attr_file *file, bool need_lock);
......
...@@ -24,7 +24,7 @@ GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache) ...@@ -24,7 +24,7 @@ GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache)
git_mutex_unlock(&cache->lock); git_mutex_unlock(&cache->lock);
} }
GIT_INLINE(git_attr_cache_entry *) attr_cache_lookup_entry( GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry(
git_attr_cache *cache, const char *path) git_attr_cache *cache, const char *path)
{ {
khiter_t pos = git_strmap_lookup_index(cache->files, path); khiter_t pos = git_strmap_lookup_index(cache->files, path);
...@@ -35,15 +35,15 @@ GIT_INLINE(git_attr_cache_entry *) attr_cache_lookup_entry( ...@@ -35,15 +35,15 @@ GIT_INLINE(git_attr_cache_entry *) attr_cache_lookup_entry(
return NULL; return NULL;
} }
int git_attr_cache_entry__new( int git_attr_cache__alloc_file_entry(
git_attr_cache_entry **out, git_attr_file_entry **out,
const char *base, const char *base,
const char *path, const char *path,
git_pool *pool) git_pool *pool)
{ {
size_t baselen = 0, pathlen = strlen(path); size_t baselen = 0, pathlen = strlen(path);
size_t cachesize = sizeof(git_attr_cache_entry) + pathlen + 1; size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1;
git_attr_cache_entry *ce; git_attr_file_entry *ce;
if (base != NULL && git_path_root(path) < 0) { if (base != NULL && git_path_root(path) < 0) {
baselen = strlen(base); baselen = strlen(base);
...@@ -72,41 +72,41 @@ int git_attr_cache_entry__new( ...@@ -72,41 +72,41 @@ int git_attr_cache_entry__new(
/* call with attrcache locked */ /* call with attrcache locked */
static int attr_cache_make_entry( static int attr_cache_make_entry(
git_attr_cache_entry **out, git_repository *repo, const char *path) git_attr_file_entry **out, git_repository *repo, const char *path)
{ {
int error = 0; int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_cache_entry *ce = NULL; git_attr_file_entry *entry = NULL;
error = git_attr_cache_entry__new( error = git_attr_cache__alloc_file_entry(
&ce, git_repository_workdir(repo), path, &cache->pool); &entry, git_repository_workdir(repo), path, &cache->pool);
if (!error) { if (!error) {
git_strmap_insert(cache->files, ce->path, ce, error); git_strmap_insert(cache->files, entry->path, entry, error);
if (error > 0) if (error > 0)
error = 0; error = 0;
} }
*out = ce; *out = entry;
return error; return error;
} }
/* insert entry or replace existing if we raced with another thread */ /* insert entry or replace existing if we raced with another thread */
static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file) static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file)
{ {
git_attr_cache_entry *ce; git_attr_file_entry *entry;
git_attr_file *old; git_attr_file *old;
if (attr_cache_lock(cache) < 0) if (attr_cache_lock(cache) < 0)
return -1; return -1;
ce = attr_cache_lookup_entry(cache, file->ce->path); entry = attr_cache_lookup_entry(cache, file->entry->path);
old = ce->file[file->source];
GIT_REFCOUNT_OWN(file, ce); GIT_REFCOUNT_OWN(file, entry);
GIT_REFCOUNT_INC(file); GIT_REFCOUNT_INC(file);
ce->file[file->source] = file;
old = git__compare_and_swap(
&entry->file[file->source], entry->file[file->source], file);
if (old) { if (old) {
GIT_REFCOUNT_OWN(old, NULL); GIT_REFCOUNT_OWN(old, NULL);
...@@ -120,7 +120,7 @@ static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file) ...@@ -120,7 +120,7 @@ static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file)
static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
{ {
int error = 0; int error = 0;
git_attr_cache_entry *ce; git_attr_file_entry *entry;
bool found = false; bool found = false;
if (!file) if (!file)
...@@ -128,27 +128,29 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) ...@@ -128,27 +128,29 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
if ((error = attr_cache_lock(cache)) < 0) if ((error = attr_cache_lock(cache)) < 0)
return error; return error;
if ((ce = attr_cache_lookup_entry(cache, file->ce->path)) != NULL && if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
ce->file[file->source] == file) file = git__compare_and_swap(&entry->file[file->source], file, NULL);
{
ce->file[file->source] = NULL;
GIT_REFCOUNT_OWN(file, NULL);
found = true;
}
attr_cache_unlock(cache); attr_cache_unlock(cache);
if (found) if (found) {
GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(file); git_attr_file__free(file);
}
return error; return error;
} }
/* Look up cache entry and file.
* - If entry is not present, create it while the cache is locked.
* - If file is present, increment refcount before returning it, so the
* cache can be unlocked and it won't go away.
*/
static int attr_cache_lookup( static int attr_cache_lookup(
git_attr_file **out_file, git_attr_file **out_file,
git_attr_cache_entry **out_ce, git_attr_file_entry **out_entry,
git_repository *repo, git_repository *repo,
git_attr_cache_source source, git_attr_file_source source,
const char *base, const char *base,
const char *filename) const char *filename)
{ {
...@@ -156,7 +158,7 @@ static int attr_cache_lookup( ...@@ -156,7 +158,7 @@ static int attr_cache_lookup(
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
const char *wd = git_repository_workdir(repo), *relfile; const char *wd = git_repository_workdir(repo), *relfile;
git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_cache_entry *ce = NULL; git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL; git_attr_file *file = NULL;
/* join base and path as needed */ /* join base and path as needed */
...@@ -174,20 +176,20 @@ static int attr_cache_lookup( ...@@ -174,20 +176,20 @@ static int attr_cache_lookup(
if ((error = attr_cache_lock(cache)) < 0) if ((error = attr_cache_lock(cache)) < 0)
goto cleanup; goto cleanup;
ce = attr_cache_lookup_entry(cache, relfile); entry = attr_cache_lookup_entry(cache, relfile);
if (!ce) { if (!entry) {
if ((error = attr_cache_make_entry(&ce, repo, relfile)) < 0) if ((error = attr_cache_make_entry(&entry, repo, relfile)) < 0)
goto cleanup; goto cleanup;
} else if (ce->file[source] != NULL) { } else if (entry->file[source] != NULL) {
file = ce->file[source]; file = entry->file[source];
GIT_REFCOUNT_INC(file); GIT_REFCOUNT_INC(file);
} }
attr_cache_unlock(cache); attr_cache_unlock(cache);
cleanup: cleanup:
*out_file = file; *out_file = file;
*out_ce = ce; *out_entry = entry;
git_buf_free(&path); git_buf_free(&path);
return error; return error;
...@@ -196,29 +198,26 @@ cleanup: ...@@ -196,29 +198,26 @@ cleanup:
int git_attr_cache__get( int git_attr_cache__get(
git_attr_file **out, git_attr_file **out,
git_repository *repo, git_repository *repo,
git_attr_cache_source source, git_attr_file_source source,
const char *base, const char *base,
const char *filename, const char *filename,
git_attr_cache_parser parser, git_attr_file_parser parser)
void *payload)
{ {
int error = 0; int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_cache_entry *ce = NULL; git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL; git_attr_file *file = NULL;
if ((error = attr_cache_lookup(&file, &ce, repo, source, base, filename)) < 0) if ((error = attr_cache_lookup(
&file, &entry, repo, source, base, filename)) < 0)
goto cleanup; goto cleanup;
/* if this is not a file backed entry, just create a new empty one */ /* if file not found or out of date, load up-to-date data and replace */
if (!parser) { if (!file || (error = git_attr_file__out_of_date(repo, file)) > 0) {
if (!file && !(error = git_attr_file__new(&file, ce, source))) /* decrement refcount (if file was found) b/c we will not return it */
error = attr_cache_upsert(cache, file); git_attr_file__free(file);
}
/* otherwise load and/or reload as needed */ if (!(error = git_attr_file__load(&file, repo, entry, source, parser)))
else if (!file || (error = git_attr_file__out_of_date(repo, file)) > 0) {
if (!(error = git_attr_file__load(
&file, repo, ce, source, parser, payload)))
error = attr_cache_upsert(cache, file); error = attr_cache_upsert(cache, file);
} }
...@@ -245,13 +244,13 @@ cleanup: ...@@ -245,13 +244,13 @@ cleanup:
bool git_attr_cache__is_cached( bool git_attr_cache__is_cached(
git_repository *repo, git_repository *repo,
git_attr_cache_source source, git_attr_file_source source,
const char *filename) const char *filename)
{ {
git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_cache *cache = git_repository_attr_cache(repo);
git_strmap *files; git_strmap *files;
khiter_t pos; khiter_t pos;
git_attr_cache_entry *ce; git_attr_file_entry *entry;
if (!(cache = git_repository_attr_cache(repo)) || if (!(cache = git_repository_attr_cache(repo)) ||
!(files = cache->files)) !(files = cache->files))
...@@ -261,9 +260,9 @@ bool git_attr_cache__is_cached( ...@@ -261,9 +260,9 @@ bool git_attr_cache__is_cached(
if (!git_strmap_valid_index(files, pos)) if (!git_strmap_valid_index(files, pos))
return false; return false;
ce = git_strmap_value_at(files, pos); entry = git_strmap_value_at(files, pos);
return ce && (ce->file[source] != NULL); return entry && (entry->file[source] != NULL);
} }
...@@ -307,14 +306,15 @@ static void attr_cache__free(git_attr_cache *cache) ...@@ -307,14 +306,15 @@ static void attr_cache__free(git_attr_cache *cache)
unlock = (git_mutex_lock(&cache->lock) == 0); unlock = (git_mutex_lock(&cache->lock) == 0);
if (cache->files != NULL) { if (cache->files != NULL) {
git_attr_cache_entry *ce; git_attr_file_entry *entry;
git_attr_file *file;
int i; int i;
git_strmap_foreach_value(cache->files, ce, { git_strmap_foreach_value(cache->files, entry, {
for (i = 0; i < GIT_ATTR_CACHE_NUM_SOURCES; ++i) { for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) {
if (ce->file[i]) { if ((file = git__swap(entry->file[i], NULL)) != NULL) {
GIT_REFCOUNT_OWN(ce->file[i], NULL); GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(ce->file[i]); git_attr_file__free(file);
} }
} }
}); });
...@@ -345,7 +345,7 @@ static void attr_cache__free(git_attr_cache *cache) ...@@ -345,7 +345,7 @@ static void attr_cache__free(git_attr_cache *cache)
git__free(cache); git__free(cache);
} }
int git_attr_cache__init(git_repository *repo) int git_attr_cache__do_init(git_repository *repo)
{ {
int ret = 0; int ret = 0;
git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_cache *cache = git_repository_attr_cache(repo);
......
...@@ -7,9 +7,8 @@ ...@@ -7,9 +7,8 @@
#ifndef INCLUDE_attrcache_h__ #ifndef INCLUDE_attrcache_h__
#define INCLUDE_attrcache_h__ #define INCLUDE_attrcache_h__
#include "pool.h" #include "attr_file.h"
#include "strmap.h" #include "strmap.h"
#include "buffer.h"
#define GIT_ATTR_CONFIG "core.attributesfile" #define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_IGNORE_CONFIG "core.excludesfile" #define GIT_IGNORE_CONFIG "core.excludesfile"
...@@ -23,55 +22,35 @@ typedef struct { ...@@ -23,55 +22,35 @@ typedef struct {
git_pool pool; git_pool pool;
} git_attr_cache; } git_attr_cache;
extern int git_attr_cache__init(git_repository *repo); extern int git_attr_cache__do_init(git_repository *repo);
typedef enum { #define git_attr_cache__init(REPO) \
GIT_ATTR_CACHE__FROM_FILE = 0, (git_repository_attr_cache(REPO) ? 0 : git_attr_cache__do_init(REPO))
GIT_ATTR_CACHE__FROM_INDEX = 1,
GIT_ATTR_CACHE_NUM_SOURCES = 2
} git_attr_cache_source;
typedef struct git_attr_file git_attr_file;
typedef struct git_attr_rule git_attr_rule;
typedef struct {
git_attr_file *file[GIT_ATTR_CACHE_NUM_SOURCES];
const char *path; /* points into fullpath */
char fullpath[GIT_FLEX_ARRAY];
} git_attr_cache_entry;
typedef int (*git_attr_cache_parser)(
git_repository *repo,
git_attr_file *file,
const char *data,
void *payload);
/* get file - loading and reload as needed */ /* get file - loading and reload as needed */
extern int git_attr_cache__get( extern int git_attr_cache__get(
git_attr_file **file, git_attr_file **file,
git_repository *repo, git_repository *repo,
git_attr_cache_source source, git_attr_file_source source,
const char *base, const char *base,
const char *filename, const char *filename,
git_attr_cache_parser parser, git_attr_file_parser parser);
void *payload);
extern bool git_attr_cache__is_cached( extern bool git_attr_cache__is_cached(
git_repository *repo, git_repository *repo,
git_attr_cache_source source, git_attr_file_source source,
const char *path); const char *path);
extern int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
const char *base,
const char *path,
git_pool *pool);
extern int git_attr_cache__insert_macro( extern int git_attr_cache__insert_macro(
git_repository *repo, git_attr_rule *macro); git_repository *repo, git_attr_rule *macro);
extern git_attr_rule *git_attr_cache__lookup_macro( extern git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name); git_repository *repo, const char *name);
extern int git_attr_cache_entry__new(
git_attr_cache_entry **out,
const char *base,
const char *path,
git_pool *pool);
#endif #endif
...@@ -132,6 +132,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) ...@@ -132,6 +132,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
if (read_size != (ssize_t)len) { if (read_size != (ssize_t)len) {
giterr_set(GITERR_OS, "Failed to read descriptor"); giterr_set(GITERR_OS, "Failed to read descriptor");
git_buf_free(buf);
return -1; return -1;
} }
...@@ -829,3 +830,16 @@ void git_futils_filestamp_set( ...@@ -829,3 +830,16 @@ void git_futils_filestamp_set(
else else
memset(target, 0, sizeof(*target)); memset(target, 0, sizeof(*target));
} }
void git_futils_filestamp_set_from_stat(
git_futils_filestamp *stamp, struct stat *st)
{
if (st) {
stamp->mtime = (git_time_t)st->st_mtime;
stamp->size = (git_off_t)st->st_size;
stamp->ino = (unsigned int)st->st_ino;
} else {
memset(stamp, 0, sizeof(*stamp));
}
}
...@@ -317,4 +317,10 @@ extern int git_futils_filestamp_check( ...@@ -317,4 +317,10 @@ extern int git_futils_filestamp_check(
extern void git_futils_filestamp_set( extern void git_futils_filestamp_set(
git_futils_filestamp *tgt, const git_futils_filestamp *src); git_futils_filestamp *tgt, const git_futils_filestamp *src);
/**
* Set file stamp data from stat structure
*/
extern void git_futils_filestamp_set_from_stat(
git_futils_filestamp *stamp, struct stat *st);
#endif /* INCLUDE_fileops_h__ */ #endif /* INCLUDE_fileops_h__ */
#include "git2/ignore.h" #include "git2/ignore.h"
#include "common.h" #include "common.h"
#include "ignore.h" #include "ignore.h"
#include "attr_file.h" #include "attrcache.h"
#include "path.h" #include "path.h"
#include "config.h" #include "config.h"
...@@ -10,30 +10,24 @@ ...@@ -10,30 +10,24 @@
#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n" #define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
static int parse_ignore_file( static int parse_ignore_file(
git_repository *repo, git_repository *repo, git_attr_file *attrs, const char *data)
git_attr_file *attrs,
const char *data,
void *payload)
{ {
int error = 0; int error = 0;
int ignore_case = false; int ignore_case = false;
const char *scan = data, *context = NULL; const char *scan = data, *context = NULL;
git_attr_fnmatch *match = NULL; git_attr_fnmatch *match = NULL;
/* either read ignore_case from ignores structure or use repo config */ if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0)
if (payload != NULL)
ignore_case = ((git_ignores *)payload)->ignore_case;
else if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0)
giterr_clear(); giterr_clear();
/* if subdir file path, convert context for file paths */ /* if subdir file path, convert context for file paths */
if (attrs->ce && if (attrs->entry &&
git_path_root(attrs->ce->path) < 0 && git_path_root(attrs->entry->path) < 0 &&
!git__suffixcmp(attrs->ce->path, "/" GIT_IGNORE_FILE)) !git__suffixcmp(attrs->entry->path, "/" GIT_IGNORE_FILE))
context = attrs->ce->path; context = attrs->entry->path;
if (git_mutex_lock(&attrs->lock) < 0) { if (git_mutex_lock(&attrs->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock attribute file"); giterr_set(GITERR_OS, "Failed to lock ignore file");
return -1; return -1;
} }
...@@ -84,8 +78,8 @@ static int push_ignore_file( ...@@ -84,8 +78,8 @@ static int push_ignore_file(
git_attr_file *file = NULL; git_attr_file *file = NULL;
error = git_attr_cache__get( error = git_attr_cache__get(
&file, ignores->repo, GIT_ATTR_CACHE__FROM_FILE, &file, ignores->repo, GIT_ATTR_FILE__FROM_FILE,
base, filename, parse_ignore_file, ignores); base, filename, parse_ignore_file);
if (error < 0) if (error < 0)
return error; return error;
...@@ -111,14 +105,12 @@ static int get_internal_ignores(git_attr_file **out, git_repository *repo) ...@@ -111,14 +105,12 @@ static int get_internal_ignores(git_attr_file **out, git_repository *repo)
if ((error = git_attr_cache__init(repo)) < 0) if ((error = git_attr_cache__init(repo)) < 0)
return error; return error;
/* get with NULL parser, gives existing or empty git_attr_file */
error = git_attr_cache__get( error = git_attr_cache__get(
out, repo, GIT_ATTR_CACHE__FROM_FILE, out, repo, GIT_ATTR_FILE__IN_MEMORY, NULL, GIT_IGNORE_INTERNAL, NULL);
NULL, GIT_IGNORE_INTERNAL, NULL, NULL);
/* if internal rules list is empty, insert default rules */ /* if internal rules list is empty, insert default rules */
if (!error && !(*out)->rules.length) if (!error && !(*out)->rules.length)
error = parse_ignore_file(repo, *out, GIT_IGNORE_DEFAULT_RULES, NULL); error = parse_ignore_file(repo, *out, GIT_IGNORE_DEFAULT_RULES);
return error; return error;
} }
...@@ -199,7 +191,7 @@ int git_ignore__pop_dir(git_ignores *ign) ...@@ -199,7 +191,7 @@ int git_ignore__pop_dir(git_ignores *ign)
{ {
if (ign->ign_path.length > 0) { if (ign->ign_path.length > 0) {
git_attr_file *file = git_vector_last(&ign->ign_path); git_attr_file *file = git_vector_last(&ign->ign_path);
const char *start = file->ce->path, *end; const char *start = file->entry->path, *end;
/* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/") /* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/")
* - file->path looks something like "a/b/.gitignore * - file->path looks something like "a/b/.gitignore
...@@ -302,35 +294,33 @@ cleanup: ...@@ -302,35 +294,33 @@ cleanup:
return 0; return 0;
} }
int git_ignore_add_rule( int git_ignore_add_rule(git_repository *repo, const char *rules)
git_repository *repo,
const char *rules)
{ {
int error; int error;
git_attr_file *ign_internal = NULL; git_attr_file *ign_internal = NULL;
if (!(error = get_internal_ignores(&ign_internal, repo))) { if ((error = get_internal_ignores(&ign_internal, repo)) < 0)
error = parse_ignore_file(repo, ign_internal, rules, NULL); return error;
git_attr_file__free(ign_internal);
} error = parse_ignore_file(repo, ign_internal, rules);
git_attr_file__free(ign_internal);
return error; return error;
} }
int git_ignore_clear_internal_rules( int git_ignore_clear_internal_rules(git_repository *repo)
git_repository *repo)
{ {
int error; int error;
git_attr_file *ign_internal; git_attr_file *ign_internal;
if (!(error = get_internal_ignores(&ign_internal, repo))) { if ((error = get_internal_ignores(&ign_internal, repo)) < 0)
if (!(error = git_attr_file__clear_rules(ign_internal, true))) return error;
error = parse_ignore_file(
repo, ign_internal, GIT_IGNORE_DEFAULT_RULES, NULL);
git_attr_file__free(ign_internal); if (!(error = git_attr_file__clear_rules(ign_internal, true)))
} error = parse_ignore_file(
repo, ign_internal, GIT_IGNORE_DEFAULT_RULES);
git_attr_file__free(ign_internal);
return error; return error;
} }
......
...@@ -13,7 +13,7 @@ void test_attr_file__simple_read(void) ...@@ -13,7 +13,7 @@ void test_attr_file__simple_read(void)
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0"))); cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0")));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->ce->path); cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path);
cl_assert(file->rules.length == 1); cl_assert(file->rules.length == 1);
rule = get_rule(0); rule = get_rule(0);
...@@ -39,7 +39,7 @@ void test_attr_file__match_variants(void) ...@@ -39,7 +39,7 @@ void test_attr_file__match_variants(void)
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1"))); cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1")));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->ce->path); cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path);
cl_assert(file->rules.length == 10); cl_assert(file->rules.length == 10);
/* let's do a thorough check of this rule, then just verify /* let's do a thorough check of this rule, then just verify
...@@ -123,7 +123,7 @@ void test_attr_file__assign_variants(void) ...@@ -123,7 +123,7 @@ void test_attr_file__assign_variants(void)
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2"))); cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2")));
cl_assert_equal_s(cl_fixture("attr/attr2"), file->ce->path); cl_assert_equal_s(cl_fixture("attr/attr2"), file->entry->path);
cl_assert(file->rules.length == 11); cl_assert(file->rules.length == 11);
check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL); check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
...@@ -188,7 +188,7 @@ void test_attr_file__check_attr_examples(void) ...@@ -188,7 +188,7 @@ void test_attr_file__check_attr_examples(void)
git_attr_assignment *assign; git_attr_assignment *assign;
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3"))); cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3")));
cl_assert_equal_s(cl_fixture("attr/attr3"), file->ce->path); cl_assert_equal_s(cl_fixture("attr/attr3"), file->entry->path);
cl_assert(file->rules.length == 3); cl_assert(file->rules.length == 3);
rule = get_rule(0); rule = get_rule(0);
......
...@@ -10,7 +10,7 @@ void test_attr_lookup__simple(void) ...@@ -10,7 +10,7 @@ void test_attr_lookup__simple(void)
const char *value = NULL; const char *value = NULL;
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0"))); cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0")));
cl_assert_equal_s(cl_fixture("attr/attr0"), file->ce->path); cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path);
cl_assert(file->rules.length == 1); cl_assert(file->rules.length == 1);
cl_git_pass(git_attr_path__init(&path, "test", NULL)); cl_git_pass(git_attr_path__init(&path, "test", NULL));
...@@ -130,7 +130,7 @@ void test_attr_lookup__match_variants(void) ...@@ -130,7 +130,7 @@ void test_attr_lookup__match_variants(void)
}; };
cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1"))); cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1")));
cl_assert_equal_s(cl_fixture("attr/attr1"), file->ce->path); cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path);
cl_assert(file->rules.length == 10); cl_assert(file->rules.length == 10);
cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL)); cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL));
...@@ -252,7 +252,7 @@ void test_attr_lookup__from_buffer(void) ...@@ -252,7 +252,7 @@ void test_attr_lookup__from_buffer(void)
cl_git_pass(git_attr_file__new(&file, NULL, 0)); cl_git_pass(git_attr_file__new(&file, NULL, 0));
cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz", NULL)); cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz"));
cl_assert(file->rules.length == 3); cl_assert(file->rules.length == 3);
......
...@@ -68,9 +68,12 @@ void test_attr_repo__get_one(void) ...@@ -68,9 +68,12 @@ void test_attr_repo__get_one(void)
attr_check_expected(scan->expected, scan->expected_str, scan->attr, value); attr_check_expected(scan->expected, scan->expected_str, scan->attr, value);
} }
cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/attributes")); cl_assert(git_attr_cache__is_cached(
cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitattributes")); g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(g_repo, 0, "sub/.gitattributes")); cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
} }
void test_attr_repo__get_many(void) void test_attr_repo__get_many(void)
......
...@@ -54,8 +54,10 @@ void test_status_ignore__0(void) ...@@ -54,8 +54,10 @@ void test_status_ignore__0(void)
} }
/* confirm that ignore files were cached */ /* confirm that ignore files were cached */
cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/exclude")); cl_assert(git_attr_cache__is_cached(
cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitignore")); g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/exclude"));
cl_assert(git_attr_cache__is_cached(
g_repo, GIT_ATTR_FILE__FROM_FILE, ".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