Commit 28fd7206 by Vicent Marti

Merge pull request #2108 from libgit2/rb/threadsafe-index-iterator

Make index iterator thread safe
parents 2bed3553 83038272
...@@ -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}")
......
...@@ -77,7 +77,12 @@ typedef struct git_index_entry { ...@@ -77,7 +77,12 @@ typedef struct git_index_entry {
#define GIT_IDXENTRY_VALID (0x8000) #define GIT_IDXENTRY_VALID (0x8000)
#define GIT_IDXENTRY_STAGESHIFT 12 #define GIT_IDXENTRY_STAGESHIFT 12
#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) #define GIT_IDXENTRY_STAGE(E) \
(((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT)
#define GIT_IDXENTRY_STAGE_SET(E,S) do { \
(E)->flags = ((E)->flags & ~GIT_IDXENTRY_STAGEMASK) | \
(((S) & 0x03) << GIT_IDXENTRY_STAGESHIFT); } while (0)
/** /**
* Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended` * Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended`
...@@ -327,12 +332,14 @@ GIT_EXTERN(size_t) git_index_entrycount(const git_index *index); ...@@ -327,12 +332,14 @@ GIT_EXTERN(size_t) git_index_entrycount(const git_index *index);
/** /**
* Clear the contents (all the entries) of an index object. * Clear the contents (all the entries) of an index object.
* This clears the index object in memory; changes must be manually *
* written to disk for them to take effect. * This clears the index object in memory; changes must be explicitly
* written to disk for them to take effect persistently.
* *
* @param index an existing index object * @param index an existing index object
* @return 0 on success, error code < 0 on failure
*/ */
GIT_EXTERN(void) git_index_clear(git_index *index); GIT_EXTERN(int) git_index_clear(git_index *index);
/** /**
* Get a pointer to one of the entries in the index * Get a pointer to one of the entries in the index
...@@ -568,8 +575,7 @@ GIT_EXTERN(int) git_index_update_all( ...@@ -568,8 +575,7 @@ GIT_EXTERN(int) git_index_update_all(
* @param at_pos the address to which the position of the index entry is written (optional) * @param at_pos the address to which the position of the index entry is written (optional)
* @param index an existing index object * @param index an existing index object
* @param path path to search * @param path path to search
* @return a zero-based position in the index if found; * @return a zero-based position in the index if found; GIT_ENOTFOUND otherwise
* GIT_ENOTFOUND otherwise
*/ */
GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path); GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path);
...@@ -613,6 +619,7 @@ GIT_EXTERN(int) git_index_conflict_add( ...@@ -613,6 +619,7 @@ GIT_EXTERN(int) git_index_conflict_add(
* @param their_out Pointer to store the their entry * @param their_out Pointer to store the their entry
* @param index an existing index object * @param index an existing index object
* @param path path to search * @param path path to search
* @return 0 or an error code
*/ */
GIT_EXTERN(int) git_index_conflict_get( GIT_EXTERN(int) git_index_conflict_get(
const git_index_entry **ancestor_out, const git_index_entry **ancestor_out,
...@@ -625,16 +632,18 @@ GIT_EXTERN(int) git_index_conflict_get( ...@@ -625,16 +632,18 @@ GIT_EXTERN(int) git_index_conflict_get(
* Removes the index entries that represent a conflict of a single file. * Removes the index entries that represent a conflict of a single file.
* *
* @param index an existing index object * @param index an existing index object
* @param path to search * @param path path to remove conflicts for
* @return 0 or an error code
*/ */
GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path); GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path);
/** /**
* Remove all conflicts in the index (entries with a stage greater than 0.) * Remove all conflicts in the index (entries with a stage greater than 0).
* *
* @param index an existing index object * @param index an existing index object
* @return 0 or an error code
*/ */
GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index); GIT_EXTERN(int) git_index_conflict_cleanup(git_index *index);
/** /**
* Determine if the index contains entries representing file conflicts. * Determine if the index contains entries representing file conflicts.
...@@ -644,9 +653,12 @@ GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index); ...@@ -644,9 +653,12 @@ GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index);
GIT_EXTERN(int) git_index_has_conflicts(const git_index *index); GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
/** /**
* Create an iterator for the conflicts in the index. You may not modify the * Create an iterator for the conflicts in the index.
* index while iterating, the results are undefined. *
* The index must not be modified while iterating; the results are undefined.
* *
* @param iterator_out The newly created conflict iterator
* @param index The index to scan
* @return 0 or an error code * @return 0 or an error code
*/ */
GIT_EXTERN(int) git_index_conflict_iterator_new( GIT_EXTERN(int) git_index_conflict_iterator_new(
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_sys_git_diff_h__
#define INCLUDE_sys_git_diff_h__
#include "git2/common.h"
#include "git2/types.h"
#include "git2/oid.h"
/**
* @file git2/sys/diff.h
* @brief Low-level Git diff utilities
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Diff print callback that writes to a git_buf.
*
* This function is provided not for you to call it directly, but instead
* so you can use it as a function pointer to the `git_diff_print` or
* `git_patch_print` APIs. When using those APIs, you specify a callback
* to actually handle the diff and/or patch data.
*
* Use this callback to easily write that data to a `git_buf` buffer. You
* must pass a `git_buf *` value as the payload to the `git_diff_print`
* and/or `git_patch_print` function. The data will be appended to the
* buffer (after any existing content).
*/
GIT_EXTERN(int) git_diff_print_callback__to_buf(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload); /*< payload must be a `git_buf *` */
/**
* Diff print callback that writes to stdio FILE handle.
*
* This function is provided not for you to call it directly, but instead
* so you can use it as a function pointer to the `git_diff_print` or
* `git_patch_print` APIs. When using those APIs, you specify a callback
* to actually handle the diff and/or patch data.
*
* Use this callback to easily write that data to a stdio FILE handle. You
* must pass a `FILE *` value (such as `stdout` or `stderr` or the return
* value from `fopen()`) as the payload to the `git_diff_print`
* and/or `git_patch_print` function. If you pass NULL, this will write
* data to `stdout`.
*/
GIT_EXTERN(int) git_diff_print_callback__to_file_handle(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload); /*< payload must be a `FILE *` */
/** @} */
GIT_END_DECL
#endif
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#include "repository.h" #include "repository.h"
#include "sysdir.h" #include "sysdir.h"
#include "config.h" #include "config.h"
#include "attr.h" #include "attr_file.h"
#include "ignore.h" #include "ignore.h"
#include "git2/oid.h" #include "git2/oid.h"
#include <ctype.h> #include <ctype.h>
...@@ -33,6 +33,7 @@ static int collect_attr_files( ...@@ -33,6 +33,7 @@ static int collect_attr_files(
const char *path, const char *path,
git_vector *files); git_vector *files);
static void release_attr_files(git_vector *files);
int git_attr_get( int git_attr_get(
const char **value, const char **value,
...@@ -59,6 +60,7 @@ int git_attr_get( ...@@ -59,6 +60,7 @@ int git_attr_get(
if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
goto cleanup; goto cleanup;
memset(&attr, 0, sizeof(attr));
attr.name = name; attr.name = name;
attr.name_hash = git_attr_file__name_hash(name); attr.name_hash = git_attr_file__name_hash(name);
...@@ -76,7 +78,7 @@ int git_attr_get( ...@@ -76,7 +78,7 @@ int git_attr_get(
} }
cleanup: cleanup:
git_vector_free(&files); release_attr_files(&files);
git_attr_path__free(&path); git_attr_path__free(&path);
return error; return error;
...@@ -152,7 +154,7 @@ int git_attr_get_many( ...@@ -152,7 +154,7 @@ int git_attr_get_many(
} }
cleanup: cleanup:
git_vector_free(&files); release_attr_files(&files);
git_attr_path__free(&path); git_attr_path__free(&path);
git__free(info); git__free(info);
...@@ -181,12 +183,10 @@ int git_attr_foreach( ...@@ -181,12 +183,10 @@ int git_attr_foreach(
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1; return -1;
if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0 ||
(error = git_strmap_alloc(&seen)) < 0)
goto cleanup; goto cleanup;
seen = git_strmap_alloc();
GITERR_CHECK_ALLOC(seen);
git_vector_foreach(&files, i, file) { git_vector_foreach(&files, i, file) {
git_attr_file__foreach_matching_rule(file, &path, j, rule) { git_attr_file__foreach_matching_rule(file, &path, j, rule) {
...@@ -211,13 +211,12 @@ int git_attr_foreach( ...@@ -211,13 +211,12 @@ int git_attr_foreach(
cleanup: cleanup:
git_strmap_free(seen); git_strmap_free(seen);
git_vector_free(&files); release_attr_files(&files);
git_attr_path__free(&path); git_attr_path__free(&path);
return error; return error;
} }
int git_attr_add_macro( int git_attr_add_macro(
git_repository *repo, git_repository *repo,
const char *name, const char *name,
...@@ -252,237 +251,6 @@ int git_attr_add_macro( ...@@ -252,237 +251,6 @@ int git_attr_add_macro(
return error; return error;
} }
bool git_attr_cache__is_cached(
git_repository *repo, git_attr_file_source source, const char *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 **data,
git_futils_filestamp *stamp,
const char *filename)
{
int error;
git_buf content = GIT_BUF_INIT;
error = git_futils_filestamp_check(stamp, filename);
if (error < 0)
return error;
/* if error == 0, then file is up to date. By returning GIT_ENOTFOUND,
* we tell the caller not to reparse this file...
*/
if (!error)
return GIT_ENOTFOUND;
error = git_futils_readbuffer(&content, filename);
if (error < 0) {
/* convert error into ENOTFOUND so failed permissions / invalid
* file type don't actually stop the operation in progress.
*/
return GIT_ENOTFOUND;
/* TODO: once warnings are available, issue a warning callback */
}
*data = git_buf_detach(&content);
return 0;
}
static int load_attr_blob_from_index(
const char **content,
git_blob **blob,
git_repository *repo,
const git_oid *old_oid,
const char *relfile)
{
int error;
size_t pos;
git_index *index;
const git_index_entry *entry;
if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
(error = git_index_find(&pos, index, relfile)) < 0)
return error;
entry = git_index_get_byindex(index, pos);
if (old_oid && git_oid__cmp(old_oid, &entry->id) == 0)
return GIT_ENOTFOUND;
if ((error = git_blob_lookup(blob, repo, &entry->id)) < 0)
return error;
*content = git_blob_rawcontent(*blob);
return 0;
}
static int load_attr_from_cache(
git_attr_file **file,
git_attr_cache *cache,
git_attr_file_source source,
const char *relative_path)
{
git_buf cache_key = GIT_BUF_INIT;
khiter_t cache_pos;
*file = NULL;
if (!cache || !cache->files)
return 0;
if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 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);
return 0;
}
int git_attr_cache__internal_file(
git_repository *repo,
const char *filename,
git_attr_file **file)
{
int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename);
if (git_strmap_valid_index(cache->files, cache_pos)) {
*file = git_strmap_value_at(cache->files, cache_pos);
return 0;
}
if (git_attr_file__new(file, 0, filename, &cache->pool) < 0)
return -1;
git_strmap_insert(cache->files, (*file)->key + 2, *file, error);
if (error > 0)
error = 0;
return error;
}
int git_attr_cache__push_file(
git_repository *repo,
const char *base,
const char *filename,
git_attr_file_source source,
git_attr_file_parser parse,
void* parsedata,
git_vector *stack)
{
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;
git_blob *blob = NULL;
git_futils_filestamp stamp;
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;
}
relfile = filename;
if (workdir && git__prefixcmp(relfile, workdir) == 0)
relfile += strlen(workdir);
/* check cache */
if (load_attr_from_cache(&file, cache, source, relfile) < 0)
return -1;
/* if not in cache, load data, parse, and cache */
if (source == GIT_ATTR_FILE_FROM_FILE) {
git_futils_filestamp_set(
&stamp, file ? &file->cache_data.stamp : NULL);
error = load_attr_file(&content, &stamp, filename);
} else {
error = load_attr_blob_from_index(&content, &blob,
repo, file ? &file->cache_data.oid : NULL, relfile);
}
if (error) {
/* not finding a file is not an error for this function */
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
}
goto finish;
}
/* if we got here, we have to parse and/or reparse the file */
if (file)
git_attr_file__clear_rules(file);
else {
error = git_attr_file__new(&file, source, relfile, &cache->pool);
if (error < 0)
goto finish;
}
if (parse && (error = parse(repo, parsedata, content, file)) < 0)
goto finish;
git_strmap_insert(cache->files, file->key, file, error); //-V595
if (error > 0)
error = 0;
/* remember "cache buster" file signature */
if (blob)
git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));
else
git_futils_filestamp_set(&file->cache_data.stamp, &stamp);
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_attr_file(R,S,B,F) \
git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,NULL,(S))
typedef struct { typedef struct {
git_repository *repo; git_repository *repo;
uint32_t flags; uint32_t flags;
...@@ -491,7 +259,7 @@ typedef struct { ...@@ -491,7 +259,7 @@ typedef struct {
git_vector *files; git_vector *files;
} attr_walk_up_info; } attr_walk_up_info;
int git_attr_cache__decide_sources( static int attr_decide_sources(
uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs) uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
{ {
int count = 0; int count = 0;
...@@ -499,42 +267,76 @@ int git_attr_cache__decide_sources( ...@@ -499,42 +267,76 @@ int git_attr_cache__decide_sources(
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_FILE_FROM_FILE; srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
if (has_index) if (has_index)
srcs[count++] = GIT_ATTR_FILE_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_FILE_FROM_INDEX; srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
if (has_wd) if (has_wd)
srcs[count++] = GIT_ATTR_FILE_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_FILE_FROM_INDEX; srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
break; break;
} }
return count; return count;
} }
static int push_attr_file(
git_repository *repo,
git_vector *list,
git_attr_file_source source,
const char *base,
const char *filename)
{
int error = 0;
git_attr_file *file = NULL;
error = git_attr_cache__get(
&file, repo, source, base, filename, git_attr_file__parse_buffer);
if (error < 0)
return error;
if (file != NULL) {
if ((error = git_vector_insert(list, file)) < 0)
git_attr_file__free(file);
}
return error;
}
static int push_one_attr(void *ref, git_buf *path) 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_file_source src[2]; git_attr_file_source src[2];
n_src = git_attr_cache__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);
for (i = 0; !error && i < n_src; ++i) for (i = 0; !error && i < n_src; ++i)
error = git_attr_cache__push_file( error = push_attr_file(
info->repo, path->ptr, GIT_ATTR_FILE, src[i], info->repo, info->files, src[i], path->ptr, GIT_ATTR_FILE);
git_attr_file__parse_buffer, NULL, info->files);
return error; return error;
} }
static void release_attr_files(git_vector *files)
{
size_t i;
git_attr_file *file;
git_vector_foreach(files, i, file) {
git_attr_file__free(file);
files->contents[i] = NULL;
}
git_vector_free(files);
}
static int collect_attr_files( static int collect_attr_files(
git_repository *repo, git_repository *repo,
uint32_t flags, uint32_t flags,
...@@ -546,9 +348,8 @@ static int collect_attr_files( ...@@ -546,9 +348,8 @@ static int collect_attr_files(
const char *workdir = git_repository_workdir(repo); const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL }; attr_walk_up_info info = { NULL };
if (git_attr_cache__init(repo) < 0 || if ((error = git_attr_cache__init(repo)) < 0)
git_vector_init(files, 4, NULL) < 0) return error;
return -1;
/* Resolve path in a non-bare repo */ /* Resolve path in a non-bare repo */
if (workdir != NULL) if (workdir != NULL)
...@@ -566,7 +367,8 @@ static int collect_attr_files( ...@@ -566,7 +367,8 @@ static int collect_attr_files(
*/ */
error = push_attr_file( error = push_attr_file(
repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO); repo, files, GIT_ATTR_FILE__FROM_FILE,
git_repository_path(repo), GIT_ATTR_FILE_INREPO);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
...@@ -583,7 +385,8 @@ static int collect_attr_files( ...@@ -583,7 +385,8 @@ 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, NULL, git_repository_attr_cache(repo)->cfg_attr_file); repo, files, GIT_ATTR_FILE__FROM_FILE,
NULL, git_repository_attr_cache(repo)->cfg_attr_file);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
} }
...@@ -591,7 +394,8 @@ static int collect_attr_files( ...@@ -591,7 +394,8 @@ static int collect_attr_files(
if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
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(repo, files, NULL, dir.ptr); error = push_attr_file(
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;
...@@ -600,153 +404,8 @@ static int collect_attr_files( ...@@ -600,153 +404,8 @@ static int collect_attr_files(
cleanup: cleanup:
if (error < 0) if (error < 0)
git_vector_free(files); release_attr_files(files);
git_buf_free(&dir); git_buf_free(&dir);
return error; return error;
} }
static int attr_cache__lookup_path(
char **out, git_config *cfg, const char *key, const char *fallback)
{
git_buf buf = GIT_BUF_INIT;
int error;
const git_config_entry *entry = NULL;
*out = NULL;
if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0)
return error;
if (entry) {
const char *cfgval = entry->value;
/* expand leading ~/ as needed */
if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
!git_sysdir_find_global_file(&buf, &cfgval[2]))
*out = git_buf_detach(&buf);
else if (cfgval)
*out = git__strdup(cfgval);
}
else if (!git_sysdir_find_xdg_file(&buf, fallback))
*out = git_buf_detach(&buf);
git_buf_free(&buf);
return error;
}
int git_attr_cache__init(git_repository *repo)
{
int ret;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_config *cfg;
if (cache->initialized)
return 0;
/* cache config settings for attributes and ignores */
if (git_repository_config__weakptr(&cfg, repo) < 0)
return -1;
ret = attr_cache__lookup_path(
&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
if (ret < 0)
return ret;
ret = attr_cache__lookup_path(
&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
if (ret < 0)
return ret;
/* allocate hashtable for attribute and ignore file contents */
if (cache->files == NULL) {
cache->files = git_strmap_alloc();
GITERR_CHECK_ALLOC(cache->files);
}
/* allocate hashtable for attribute macros */
if (cache->macros == NULL) {
cache->macros = git_strmap_alloc();
GITERR_CHECK_ALLOC(cache->macros);
}
/* allocate string pool */
if (git_pool_init(&cache->pool, 1, 0) < 0)
return -1;
cache->initialized = 1;
/* insert default macros */
return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
}
void git_attr_cache_flush(
git_repository *repo)
{
git_attr_cache *cache;
if (!repo)
return;
cache = git_repository_attr_cache(repo);
if (cache->files != NULL) {
git_attr_file *file;
git_strmap_foreach_value(cache->files, file, {
git_attr_file__free(file);
});
git_strmap_free(cache->files);
}
if (cache->macros != NULL) {
git_attr_rule *rule;
git_strmap_foreach_value(cache->macros, rule, {
git_attr_rule__free(rule);
});
git_strmap_free(cache->macros);
}
git_pool_clear(&cache->pool);
git__free(cache->cfg_attr_file);
cache->cfg_attr_file = NULL;
git__free(cache->cfg_excl_file);
cache->cfg_excl_file = NULL;
cache->initialized = 0;
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
git_strmap *macros = git_repository_attr_cache(repo)->macros;
int error;
/* TODO: generate warning log if (macro->assigns.length == 0) */
if (macro->assigns.length == 0)
return 0;
git_strmap_insert(macros, macro->match.pattern, macro, error);
return (error < 0) ? -1 : 0;
}
git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name)
{
git_strmap *macros = git_repository_attr_cache(repo)->macros;
khiter_t pos;
pos = git_strmap_lookup_index(macros, name);
if (!git_strmap_valid_index(macros, pos))
return NULL;
return (git_attr_rule *)git_strmap_value_at(macros, pos);
}
...@@ -8,38 +8,6 @@ ...@@ -8,38 +8,6 @@
#define INCLUDE_attr_h__ #define INCLUDE_attr_h__
#include "attr_file.h" #include "attr_file.h"
#include "attrcache.h"
#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_IGNORE_CONFIG "core.excludesfile"
typedef int (*git_attr_file_parser)(
git_repository *, void *, const char *, git_attr_file *);
extern int git_attr_cache__insert_macro(
git_repository *repo, git_attr_rule *macro);
extern git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name);
extern int git_attr_cache__push_file(
git_repository *repo,
const char *base,
const char *filename,
git_attr_file_source source,
git_attr_file_parser parse,
void *parsedata, /* passed through to parse function */
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, 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 #endif
#include "common.h" #include "common.h"
#include "repository.h" #include "repository.h"
#include "filebuf.h" #include "filebuf.h"
#include "attr.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 <ctype.h> #include <ctype.h>
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); static void attr_file_free(git_attr_file *file)
static void git_attr_rule__clear(git_attr_rule *rule); {
static bool parse_optimized_patterns( bool unlock = !git_mutex_lock(&file->lock);
git_attr_fnmatch *spec, git_attr_file__clear_rules(file, false);
git_pool *pool, git_pool_clear(&file->pool);
const char *pattern); if (unlock)
git_mutex_unlock(&file->lock);
git_mutex_free(&file->lock);
git__memzero(file, sizeof(*file));
git__free(file);
}
int git_attr_file__new( int git_attr_file__new(
git_attr_file **attrs_ptr, git_attr_file **out,
git_attr_file_source from, git_attr_file_entry *entry,
const char *path, git_attr_file_source source)
git_pool *pool)
{ {
git_attr_file *attrs = NULL; git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
attrs = git__calloc(1, sizeof(git_attr_file));
GITERR_CHECK_ALLOC(attrs); GITERR_CHECK_ALLOC(attrs);
if (pool) if (git_mutex_init(&attrs->lock) < 0) {
attrs->pool = pool; giterr_set(GITERR_OS, "Failed to initialize lock");
else { git__free(attrs);
attrs->pool = git__calloc(1, sizeof(git_pool)); return -1;
if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)
goto fail;
attrs->pool_is_allocated = true;
} }
if (path) { if (git_pool_init(&attrs->pool, 1, 0) < 0) {
size_t len = strlen(path); attr_file_free(attrs);
return -1;
}
GIT_REFCOUNT_INC(attrs);
attrs->entry = entry;
attrs->source = source;
*out = attrs;
return 0;
}
attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3); int git_attr_file__clear_rules(git_attr_file *file, bool need_lock)
GITERR_CHECK_ALLOC(attrs->key); {
unsigned int i;
git_attr_rule *rule;
attrs->key[0] = '0' + (char)from; if (need_lock && git_mutex_lock(&file->lock) < 0) {
attrs->key[1] = '#'; giterr_set(GITERR_OS, "Failed to lock attribute file");
memcpy(&attrs->key[2], path, len); return -1;
attrs->key[len + 2] = '\0';
} }
if (git_vector_init(&attrs->rules, 4, NULL) < 0) git_vector_foreach(&file->rules, i, rule)
goto fail; git_attr_rule__free(rule);
git_vector_free(&file->rules);
if (need_lock)
git_mutex_unlock(&file->lock);
*attrs_ptr = attrs;
return 0; return 0;
}
fail: void git_attr_file__free(git_attr_file *file)
git_attr_file__free(attrs); {
attrs_ptr = NULL; if (!file)
return -1; return;
GIT_REFCOUNT_DEC(file, attr_file_free);
} }
int git_attr_file__parse_buffer( static int attr_file_oid_from_index(
git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs) git_oid *oid, git_repository *repo, const char *path)
{
int error;
git_index *idx;
size_t pos;
const git_index_entry *entry;
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
(error = git_index__find_pos(&pos, idx, path, 0, 0)) < 0)
return error;
if (!(entry = git_index_get_byindex(idx, pos)))
return GIT_ENOTFOUND;
*oid = entry->id;
return 0;
}
int git_attr_file__load(
git_attr_file **out,
git_repository *repo,
git_attr_file_entry *entry,
git_attr_file_source source,
git_attr_file_parser parser)
{ {
int error = 0; int error = 0;
const char *scan = NULL, *context = NULL; git_blob *blob = NULL;
git_attr_rule *rule = NULL; git_buf content = GIT_BUF_INIT;
const char *data = NULL;
git_attr_file *file;
struct stat st;
*out = NULL;
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;
if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
(error = git_blob_lookup(&blob, repo, &id)) < 0)
return error;
data = git_blob_rawcontent(blob);
break;
}
case GIT_ATTR_FILE__FROM_FILE: {
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;
/* 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);
GIT_UNUSED(parsedata); if (error < 0)
return GIT_ENOTFOUND;
assert(buffer && attrs); data = content.ptr;
break;
}
default:
giterr_set(GITERR_INVALID, "Unknown file source %d", source);
return -1;
}
scan = buffer; if ((error = git_attr_file__new(&file, entry, source)) < 0)
goto cleanup;
if (parser && (error = parser(repo, file, data)) < 0) {
git_attr_file__free(file);
goto cleanup;
}
/* 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:
git_blob_free(blob);
git_buf_free(&content);
return error;
}
int git_attr_file__out_of_date(git_repository *repo, git_attr_file *file)
{
if (!file)
return 1;
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;
git_oid id;
if ((error = attr_file_oid_from_index(
&id, repo, file->entry->path)) < 0)
return error;
return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
}
default:
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 void git_attr_rule__clear(git_attr_rule *rule);
static bool parse_optimized_patterns(
git_attr_fnmatch *spec,
git_pool *pool,
const char *pattern);
int git_attr_file__parse_buffer(
git_repository *repo, git_attr_file *attrs, const char *data)
{
int error = 0;
const char *scan = data, *context = NULL;
git_attr_rule *rule = NULL;
/* if subdir file path, convert context for file paths */ /* if subdir file path, convert context for file paths */
if (attrs->key && if (attrs->entry &&
git_path_root(attrs->key + 2) < 0 && git_path_root(attrs->entry->path) < 0 &&
git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) !git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE))
context = attrs->key + 2; context = attrs->entry->path;
if (git_mutex_lock(&attrs->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock attribute file");
return -1;
}
while (!error && *scan) { while (!error && *scan) {
/* allocate rule if needed */ /* allocate rule if needed */
if (!rule) { if (!rule) {
if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) { if (!(rule = git__calloc(1, sizeof(*rule)))) {
error = -1; error = -1;
break; break;
} }
...@@ -89,9 +243,9 @@ int git_attr_file__parse_buffer( ...@@ -89,9 +243,9 @@ int git_attr_file__parse_buffer(
/* parse the next "pattern attr attr attr" line */ /* parse the next "pattern attr attr attr" line */
if (!(error = git_attr_fnmatch__parse( if (!(error = git_attr_fnmatch__parse(
&rule->match, attrs->pool, context, &scan)) && &rule->match, &attrs->pool, context, &scan)) &&
!(error = git_attr_assignment__parse( !(error = git_attr_assignment__parse(
repo, attrs->pool, &rule->assigns, &scan))) repo, &attrs->pool, &rule->assigns, &scan)))
{ {
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
/* should generate error/warning if this is coming from any /* should generate error/warning if this is coming from any
...@@ -112,62 +266,12 @@ int git_attr_file__parse_buffer( ...@@ -112,62 +266,12 @@ int git_attr_file__parse_buffer(
} }
} }
git_mutex_unlock(&attrs->lock);
git_attr_rule__free(rule); git_attr_rule__free(rule);
return error; return error;
} }
int git_attr_file__new_and_load(
git_attr_file **attrs_ptr,
const char *path)
{
int error;
git_buf content = GIT_BUF_INIT;
if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)
return error;
if (!(error = git_futils_readbuffer(&content, path)))
error = git_attr_file__parse_buffer(
NULL, NULL, git_buf_cstr(&content), *attrs_ptr);
git_buf_free(&content);
if (error) {
git_attr_file__free(*attrs_ptr);
*attrs_ptr = NULL;
}
return error;
}
void git_attr_file__clear_rules(git_attr_file *file)
{
unsigned int i;
git_attr_rule *rule;
git_vector_foreach(&file->rules, i, rule)
git_attr_rule__free(rule);
git_vector_free(&file->rules);
}
void git_attr_file__free(git_attr_file *file)
{
if (!file)
return;
git_attr_file__clear_rules(file);
if (file->pool_is_allocated) {
git_pool_clear(file->pool);
git__free(file->pool);
}
file->pool = NULL;
git__free(file);
}
uint32_t git_attr_file__name_hash(const char *name) uint32_t git_attr_file__name_hash(const char *name)
{ {
uint32_t h = 5381; uint32_t h = 5381;
...@@ -178,7 +282,6 @@ uint32_t git_attr_file__name_hash(const char *name) ...@@ -178,7 +282,6 @@ uint32_t git_attr_file__name_hash(const char *name)
return h; return h;
} }
int git_attr_file__lookup_one( int git_attr_file__lookup_one(
git_attr_file *file, git_attr_file *file,
const git_attr_path *path, const git_attr_path *path,
...@@ -207,25 +310,63 @@ int git_attr_file__lookup_one( ...@@ -207,25 +310,63 @@ int git_attr_file__lookup_one(
return 0; return 0;
} }
int git_attr_file__load_standalone(git_attr_file **out, const char *path)
{
int error;
git_attr_file *file;
git_buf content = GIT_BUF_INIT;
error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE);
if (error < 0)
return error;
error = git_attr_cache__alloc_file_entry(
&file->entry, NULL, path, &file->pool);
if (error < 0) {
git_attr_file__free(file);
return error;
}
/* because the cache entry is allocated from the file's own pool, we
* don't have to free it - freeing file+pool will free cache entry, too.
*/
if (!(error = git_futils_readbuffer(&content, path))) {
error = git_attr_file__parse_buffer(NULL, file, content.ptr);
git_buf_free(&content);
}
if (error < 0)
git_attr_file__free(file);
else
*out = file;
return error;
}
bool git_attr_fnmatch__match( bool git_attr_fnmatch__match(
git_attr_fnmatch *match, git_attr_fnmatch *match,
const git_attr_path *path) const git_attr_path *path)
{ {
int fnm; const char *filename;
int icase_flags = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? FNM_CASEFOLD : 0; int flags = 0;
if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir) if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir)
return false; return false;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) if (match->flags & GIT_ATTR_FNMATCH_ICASE)
fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME | icase_flags); flags |= FNM_CASEFOLD;
else if (path->is_dir)
fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR | icase_flags);
else
fnm = p_fnmatch(match->pattern, path->basename, icase_flags);
return (fnm == FNM_NOMATCH) ? false : true; if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
filename = path->path;
flags |= FNM_PATHNAME;
} else {
filename = path->basename;
if (path->is_dir)
flags |= FNM_LEADING_DIR;
}
return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
} }
bool git_attr_rule__match( bool git_attr_rule__match(
...@@ -240,7 +381,6 @@ bool git_attr_rule__match( ...@@ -240,7 +381,6 @@ bool git_attr_rule__match(
return matched; return matched;
} }
git_attr_assignment *git_attr_rule__lookup_assignment( git_attr_assignment *git_attr_rule__lookup_assignment(
git_attr_rule *rule, const char *name) git_attr_rule *rule, const char *name)
{ {
...@@ -339,7 +479,7 @@ void git_attr_path__free(git_attr_path *info) ...@@ -339,7 +479,7 @@ void git_attr_path__free(git_attr_path *info)
int git_attr_fnmatch__parse( int git_attr_fnmatch__parse(
git_attr_fnmatch *spec, git_attr_fnmatch *spec,
git_pool *pool, git_pool *pool,
const char *source, const char *context,
const char **base) const char **base)
{ {
const char *pattern, *scan; const char *pattern, *scan;
...@@ -407,21 +547,21 @@ int git_attr_fnmatch__parse( ...@@ -407,21 +547,21 @@ int git_attr_fnmatch__parse(
} }
if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 && if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
source != NULL && git_path_root(pattern) < 0) context != NULL && git_path_root(pattern) < 0)
{ {
/* use context path minus the trailing filename */ /* use context path minus the trailing filename */
char *slash = strrchr(source, '/'); char *slash = strrchr(context, '/');
size_t sourcelen = slash ? slash - source + 1 : 0; size_t contextlen = slash ? slash - context + 1 : 0;
/* given an unrooted fullpath match from a file inside a repo, /* given an unrooted fullpath match from a file inside a repo,
* prefix the pattern with the relative directory of the source file * prefix the pattern with the relative directory of the source file
*/ */
spec->pattern = git_pool_malloc( spec->pattern = git_pool_malloc(
pool, (uint32_t)(sourcelen + spec->length + 1)); pool, (uint32_t)(contextlen + spec->length + 1));
if (spec->pattern) { if (spec->pattern) {
memcpy(spec->pattern, source, sourcelen); memcpy(spec->pattern, context, contextlen);
memcpy(spec->pattern + sourcelen, pattern, spec->length); memcpy(spec->pattern + contextlen, pattern, spec->length);
spec->length += sourcelen; spec->length += contextlen;
spec->pattern[spec->length] = '\0'; spec->pattern[spec->length] = '\0';
} }
} else { } else {
...@@ -434,6 +574,7 @@ int git_attr_fnmatch__parse( ...@@ -434,6 +574,7 @@ int git_attr_fnmatch__parse(
} else { } else {
/* strip '\' that might have be used for internal whitespace */ /* strip '\' that might have be used for internal whitespace */
spec->length = git__unescape(spec->pattern); spec->length = git__unescape(spec->pattern);
/* TODO: convert remaining '\' into '/' for POSIX ??? */
} }
return 0; return 0;
......
...@@ -35,6 +35,14 @@ ...@@ -35,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;
...@@ -63,17 +71,32 @@ typedef struct { ...@@ -63,17 +71,32 @@ typedef struct {
const char *value; const char *value;
} git_attr_assignment; } git_attr_assignment;
typedef struct git_attr_file_entry git_attr_file_entry;
typedef struct { typedef struct {
char *key; /* cache "source#path" this was loaded from */ git_refcount rc;
git_vector rules; /* vector of <rule*> or <fnmatch*> */ git_mutex lock;
git_pool *pool; git_attr_file_entry *entry;
bool pool_is_allocated; git_attr_file_source source;
git_vector rules; /* vector of <rule*> or <fnmatch*> */
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; } 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;
...@@ -81,29 +104,37 @@ typedef struct { ...@@ -81,29 +104,37 @@ typedef struct {
int is_dir; int is_dir;
} git_attr_path; } 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 * git_attr_file API
*/ */
extern int git_attr_file__new( int git_attr_file__new(
git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool); git_attr_file **out,
git_attr_file_entry *entry,
git_attr_file_source source);
void git_attr_file__free(git_attr_file *file);
int git_attr_file__load(
git_attr_file **out,
git_repository *repo,
git_attr_file_entry *ce,
git_attr_file_source source,
git_attr_file_parser parser);
extern int git_attr_file__new_and_load( int git_attr_file__load_standalone(
git_attr_file **attrs_ptr, const char *path); git_attr_file **out, const char *path);
extern void git_attr_file__free(git_attr_file *file); int git_attr_file__out_of_date(
git_repository *repo, git_attr_file *file);
extern void git_attr_file__clear_rules(git_attr_file *file); int git_attr_file__parse_buffer(
git_repository *repo, git_attr_file *attrs, const char *data);
extern int git_attr_file__parse_buffer( int git_attr_file__clear_rules(
git_repository *repo, void *parsedata, const char *buf, git_attr_file *file); git_attr_file *file, bool need_lock);
extern int git_attr_file__lookup_one( int git_attr_file__lookup_one(
git_attr_file *file, git_attr_file *file,
const git_attr_path *path, const git_attr_path *path,
const char *attr, const char *attr,
...@@ -114,7 +145,7 @@ extern int git_attr_file__lookup_one( ...@@ -114,7 +145,7 @@ extern int git_attr_file__lookup_one(
git_vector_rforeach(&(file)->rules, (iter), (rule)) \ git_vector_rforeach(&(file)->rules, (iter), (rule)) \
if (git_attr_rule__match((rule), (path))) if (git_attr_rule__match((rule), (path)))
extern uint32_t git_attr_file__name_hash(const char *name); uint32_t git_attr_file__name_hash(const char *name);
/* /*
......
#include "common.h"
#include "repository.h"
#include "attr_file.h"
#include "config.h"
#include "sysdir.h"
#include "ignore.h"
GIT__USE_STRMAP;
GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache)
{
GIT_UNUSED(cache); /* avoid warning if threading is off */
if (git_mutex_lock(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to get attr cache lock");
return -1;
}
return 0;
}
GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache)
{
GIT_UNUSED(cache); /* avoid warning if threading is off */
git_mutex_unlock(&cache->lock);
}
GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry(
git_attr_cache *cache, const char *path)
{
khiter_t pos = git_strmap_lookup_index(cache->files, path);
if (git_strmap_valid_index(cache->files, pos))
return git_strmap_value_at(cache->files, pos);
else
return NULL;
}
int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
const char *base,
const char *path,
git_pool *pool)
{
size_t baselen = 0, pathlen = strlen(path);
size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1;
git_attr_file_entry *ce;
if (base != NULL && git_path_root(path) < 0) {
baselen = strlen(base);
cachesize += baselen;
if (baselen && base[baselen - 1] != '/')
cachesize++;
}
ce = git_pool_mallocz(pool, cachesize);
GITERR_CHECK_ALLOC(ce);
if (baselen) {
memcpy(ce->fullpath, base, baselen);
if (base[baselen - 1] != '/')
ce->fullpath[baselen++] = '/';
}
memcpy(&ce->fullpath[baselen], path, pathlen);
ce->path = &ce->fullpath[baselen];
*out = ce;
return 0;
}
/* call with attrcache locked */
static int attr_cache_make_entry(
git_attr_file_entry **out, git_repository *repo, const char *path)
{
int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
error = git_attr_cache__alloc_file_entry(
&entry, git_repository_workdir(repo), path, &cache->pool);
if (!error) {
git_strmap_insert(cache->files, entry->path, entry, error);
if (error > 0)
error = 0;
}
*out = entry;
return error;
}
/* insert entry or replace existing if we raced with another thread */
static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file)
{
git_attr_file_entry *entry;
git_attr_file *old;
if (attr_cache_lock(cache) < 0)
return -1;
entry = attr_cache_lookup_entry(cache, file->entry->path);
GIT_REFCOUNT_OWN(file, entry);
GIT_REFCOUNT_INC(file);
old = git__compare_and_swap(
&entry->file[file->source], entry->file[file->source], file);
if (old) {
GIT_REFCOUNT_OWN(old, NULL);
git_attr_file__free(old);
}
attr_cache_unlock(cache);
return 0;
}
static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
{
int error = 0;
git_attr_file_entry *entry;
if (!file)
return 0;
if ((error = attr_cache_lock(cache)) < 0)
return error;
if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
file = git__compare_and_swap(&entry->file[file->source], file, NULL);
attr_cache_unlock(cache);
if (file) {
GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(file);
}
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(
git_attr_file **out_file,
git_attr_file_entry **out_entry,
git_repository *repo,
git_attr_file_source source,
const char *base,
const char *filename)
{
int error = 0;
git_buf path = GIT_BUF_INIT;
const char *wd = git_repository_workdir(repo), *relfile;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL;
/* 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;
}
relfile = filename;
if (wd && !git__prefixcmp(relfile, wd))
relfile += strlen(wd);
/* check cache for existing entry */
if ((error = attr_cache_lock(cache)) < 0)
goto cleanup;
entry = attr_cache_lookup_entry(cache, relfile);
if (!entry) {
if ((error = attr_cache_make_entry(&entry, repo, relfile)) < 0)
goto cleanup;
} else if (entry->file[source] != NULL) {
file = entry->file[source];
GIT_REFCOUNT_INC(file);
}
attr_cache_unlock(cache);
cleanup:
*out_file = file;
*out_entry = entry;
git_buf_free(&path);
return error;
}
int git_attr_cache__get(
git_attr_file **out,
git_repository *repo,
git_attr_file_source source,
const char *base,
const char *filename,
git_attr_file_parser parser)
{
int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL, *updated = NULL;
if ((error = attr_cache_lookup(
&file, &entry, repo, source, base, filename)) < 0)
return error;
/* load file if we don't have one or if existing one is out of date */
if (!file || (error = git_attr_file__out_of_date(repo, file)) > 0)
error = git_attr_file__load(&updated, repo, entry, source, parser);
/* if we loaded the file, insert into and/or update cache */
if (updated) {
if ((error = attr_cache_upsert(cache, updated)) < 0)
git_attr_file__free(updated);
else {
git_attr_file__free(file); /* offset incref from lookup */
file = updated;
}
}
/* if file could not be loaded */
if (error < 0) {
/* remove existing entry */
if (file) {
git_attr_file__free(file); /* offset incref from lookup */
attr_cache_remove(cache, file);
file = NULL;
}
/* no error if file simply doesn't exist */
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
}
}
*out = file;
return error;
}
bool git_attr_cache__is_cached(
git_repository *repo,
git_attr_file_source source,
const char *filename)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
git_strmap *files;
khiter_t pos;
git_attr_file_entry *entry;
if (!(cache = git_repository_attr_cache(repo)) ||
!(files = cache->files))
return false;
pos = git_strmap_lookup_index(files, filename);
if (!git_strmap_valid_index(files, pos))
return false;
entry = git_strmap_value_at(files, pos);
return entry && (entry->file[source] != NULL);
}
static int attr_cache__lookup_path(
char **out, git_config *cfg, const char *key, const char *fallback)
{
git_buf buf = GIT_BUF_INIT;
int error;
const git_config_entry *entry = NULL;
*out = NULL;
if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0)
return error;
if (entry) {
const char *cfgval = entry->value;
/* expand leading ~/ as needed */
if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
!git_sysdir_find_global_file(&buf, &cfgval[2]))
*out = git_buf_detach(&buf);
else if (cfgval)
*out = git__strdup(cfgval);
}
else if (!git_sysdir_find_xdg_file(&buf, fallback))
*out = git_buf_detach(&buf);
git_buf_free(&buf);
return error;
}
static void attr_cache__free(git_attr_cache *cache)
{
bool unlock;
if (!cache)
return;
unlock = (git_mutex_lock(&cache->lock) == 0);
if (cache->files != NULL) {
git_attr_file_entry *entry;
git_attr_file *file;
int i;
git_strmap_foreach_value(cache->files, entry, {
for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) {
if ((file = git__swap(entry->file[i], NULL)) != NULL) {
GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(file);
}
}
});
git_strmap_free(cache->files);
}
if (cache->macros != NULL) {
git_attr_rule *rule;
git_strmap_foreach_value(cache->macros, rule, {
git_attr_rule__free(rule);
});
git_strmap_free(cache->macros);
}
git_pool_clear(&cache->pool);
git__free(cache->cfg_attr_file);
cache->cfg_attr_file = NULL;
git__free(cache->cfg_excl_file);
cache->cfg_excl_file = NULL;
if (unlock)
git_mutex_unlock(&cache->lock);
git_mutex_free(&cache->lock);
git__free(cache);
}
int git_attr_cache__do_init(git_repository *repo)
{
int ret = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_config *cfg;
if (cache)
return 0;
if ((ret = git_repository_config__weakptr(&cfg, repo)) < 0)
return ret;
cache = git__calloc(1, sizeof(git_attr_cache));
GITERR_CHECK_ALLOC(cache);
/* set up lock */
if (git_mutex_init(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to initialize lock for attr cache");
git__free(cache);
return -1;
}
/* cache config settings for attributes and ignores */
ret = attr_cache__lookup_path(
&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
if (ret < 0)
goto cancel;
ret = attr_cache__lookup_path(
&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
if (ret < 0)
goto cancel;
/* allocate hashtable for attribute and ignore file contents,
* hashtable for attribute macros, and string pool
*/
if ((ret = git_strmap_alloc(&cache->files)) < 0 ||
(ret = git_strmap_alloc(&cache->macros)) < 0 ||
(ret = git_pool_init(&cache->pool, 1, 0)) < 0)
goto cancel;
cache = git__compare_and_swap(&repo->attrcache, NULL, cache);
if (cache)
goto cancel; /* raced with another thread, free this but no error */
/* insert default macros */
return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
cancel:
attr_cache__free(cache);
return ret;
}
void git_attr_cache_flush(git_repository *repo)
{
git_attr_cache *cache;
/* this could be done less expensively, but for now, we'll just free
* the entire attrcache and let the next use reinitialize it...
*/
if (repo && (cache = git__swap(repo->attrcache, NULL)) != NULL)
attr_cache__free(cache);
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
git_strmap *macros = cache->macros;
int error;
/* TODO: generate warning log if (macro->assigns.length == 0) */
if (macro->assigns.length == 0)
return 0;
if (git_mutex_lock(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to get attr cache lock");
error = -1;
} else {
git_strmap_insert(macros, macro->match.pattern, macro, error);
git_mutex_unlock(&cache->lock);
}
return (error < 0) ? -1 : 0;
}
git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name)
{
git_strmap *macros = git_repository_attr_cache(repo)->macros;
khiter_t pos;
pos = git_strmap_lookup_index(macros, name);
if (!git_strmap_valid_index(macros, pos))
return NULL;
return (git_attr_rule *)git_strmap_value_at(macros, pos);
}
...@@ -7,18 +7,50 @@ ...@@ -7,18 +7,50 @@
#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"
#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_IGNORE_CONFIG "core.excludesfile"
typedef struct { typedef struct {
int initialized;
git_pool pool;
git_strmap *files; /* hash path to git_attr_file of rules */
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
char *cfg_attr_file; /* cached value of core.attributesfile */ char *cfg_attr_file; /* cached value of core.attributesfile */
char *cfg_excl_file; /* cached value of core.excludesfile */ char *cfg_excl_file; /* cached value of core.excludesfile */
git_strmap *files; /* hash path to git_attr_cache_entry records */
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
git_mutex lock;
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);
#define git_attr_cache__init(REPO) \
(git_repository_attr_cache(REPO) ? 0 : git_attr_cache__do_init(REPO))
/* get file - loading and reload as needed */
extern int git_attr_cache__get(
git_attr_file **file,
git_repository *repo,
git_attr_file_source source,
const char *base,
const char *filename,
git_attr_file_parser parser);
extern bool git_attr_cache__is_cached(
git_repository *repo,
git_attr_file_source source,
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(
git_repository *repo, git_attr_rule *macro);
extern git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name);
#endif #endif
...@@ -277,19 +277,23 @@ static int checkout_action_wd_only( ...@@ -277,19 +277,23 @@ static int checkout_action_wd_only(
/* check if item is tracked in the index but not in the checkout diff */ /* check if item is tracked in the index but not in the checkout diff */
if (data->index != NULL) { if (data->index != NULL) {
size_t pos;
error = git_index__find_pos(
&pos, data->index, wd->path, 0, GIT_INDEX_STAGE_ANY);
if (wd->mode != GIT_FILEMODE_TREE) { if (wd->mode != GIT_FILEMODE_TREE) {
if (!(error = git_index_find(NULL, data->index, wd->path))) { if (!error) { /* found by git_index__find_pos call */
notify = GIT_CHECKOUT_NOTIFY_DIRTY; notify = GIT_CHECKOUT_NOTIFY_DIRTY;
remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0); remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
} else if (error != GIT_ENOTFOUND) } else if (error != GIT_ENOTFOUND)
return error; return error;
else else
giterr_clear(); error = 0; /* git_index__find_pos does not set error msg */
} else { } else {
/* for tree entries, we have to see if there are any index /* for tree entries, we have to see if there are any index
* entries that are contained inside that tree * entries that are contained inside that tree
*/ */
size_t pos = git_index__prefix_position(data->index, wd->path);
const git_index_entry *e = git_index_get_byindex(data->index, pos); const git_index_entry *e = git_index_get_byindex(data->index, pos);
if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) { if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) {
...@@ -653,10 +657,13 @@ static int checkout_conflictdata_cmp(const void *a, const void *b) ...@@ -653,10 +657,13 @@ static int checkout_conflictdata_cmp(const void *a, const void *b)
return diff; return diff;
} }
int checkout_conflictdata_empty(const git_vector *conflicts, size_t idx) int checkout_conflictdata_empty(
const git_vector *conflicts, size_t idx, void *payload)
{ {
checkout_conflictdata *conflict; checkout_conflictdata *conflict;
GIT_UNUSED(payload);
if ((conflict = git_vector_get(conflicts, idx)) == NULL) if ((conflict = git_vector_get(conflicts, idx)) == NULL)
return -1; return -1;
...@@ -954,7 +961,8 @@ static int checkout_conflicts_coalesce_renames( ...@@ -954,7 +961,8 @@ static int checkout_conflicts_coalesce_renames(
ancestor_conflict->one_to_two = 1; ancestor_conflict->one_to_two = 1;
} }
git_vector_remove_matching(&data->conflicts, checkout_conflictdata_empty); git_vector_remove_matching(
&data->conflicts, checkout_conflictdata_empty, NULL);
done: done:
return error; return error;
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
# include <unistd.h> # include <unistd.h>
# ifdef GIT_THREADS # ifdef GIT_THREADS
# include <pthread.h> # include <pthread.h>
# include <sched.h>
# endif # endif
#define GIT_STDLIB_CALL #define GIT_STDLIB_CALL
......
...@@ -180,11 +180,15 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) ...@@ -180,11 +180,15 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
b->level = level; b->level = level;
b->values = git_strmap_alloc(); if ((res = git_strmap_alloc(&b->values)) < 0)
GITERR_CHECK_ALLOC(b->values); return res;
git_array_init(b->readers); git_array_init(b->readers);
reader = git_array_alloc(b->readers); reader = git_array_alloc(b->readers);
if (!reader) {
git_strmap_free(b->values);
return -1;
}
memset(reader, 0, sizeof(struct reader)); memset(reader, 0, sizeof(struct reader));
reader->file_path = git__strdup(b->file_path); reader->file_path = git__strdup(b->file_path);
...@@ -205,6 +209,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) ...@@ -205,6 +209,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
reader = git_array_get(b->readers, 0); reader = git_array_get(b->readers, 0);
git_buf_free(&reader->buffer); git_buf_free(&reader->buffer);
return res; return res;
} }
...@@ -218,8 +223,10 @@ static int config_refresh(git_config_backend *cfg) ...@@ -218,8 +223,10 @@ static int config_refresh(git_config_backend *cfg)
for (i = 0; i < git_array_size(b->readers); i++) { for (i = 0; i < git_array_size(b->readers); i++) {
reader = git_array_get(b->readers, i); reader = git_array_get(b->readers, i);
res = git_futils_readbuffer_updated( res = git_futils_readbuffer_updated(
&reader->buffer, reader->file_path, &reader->file_mtime, &reader->file_size, &updated); &reader->buffer, reader->file_path,
&reader->file_mtime, &reader->file_size, &updated);
if (res < 0) if (res < 0)
return (res == GIT_ENOTFOUND) ? 0 : res; return (res == GIT_ENOTFOUND) ? 0 : res;
...@@ -233,10 +240,9 @@ static int config_refresh(git_config_backend *cfg) ...@@ -233,10 +240,9 @@ static int config_refresh(git_config_backend *cfg)
/* need to reload - store old values and prep for reload */ /* need to reload - store old values and prep for reload */
old_values = b->values; old_values = b->values;
b->values = git_strmap_alloc(); if ((res = git_strmap_alloc(&b->values)) < 0) {
GITERR_CHECK_ALLOC(b->values); b->values = old_values;
} else if ((res = config_parse(b, reader, b->level, 0)) < 0) {
if ((res = config_parse(b, reader, b->level, 0)) < 0) {
free_vars(b->values); free_vars(b->values);
b->values = old_values; b->values = old_values;
} else { } else {
......
...@@ -318,6 +318,31 @@ static const char *diff_mnemonic_prefix( ...@@ -318,6 +318,31 @@ static const char *diff_mnemonic_prefix(
return pfx; return pfx;
} }
static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
{
if (!ignore_case) {
diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
diff->strcomp = git__strcmp;
diff->strncomp = git__strncmp;
diff->pfxcomp = git__prefixcmp;
diff->entrycomp = git_index_entry_cmp;
git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp);
} else {
diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
diff->strcomp = git__strcasecmp;
diff->strncomp = git__strncasecmp;
diff->pfxcomp = git__prefixcmp_icase;
diff->entrycomp = git_index_entry_icmp;
git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
}
git_vector_sort(&diff->deltas);
}
static git_diff *diff_list_alloc( static git_diff *diff_list_alloc(
git_repository *repo, git_repository *repo,
git_iterator *old_iter, git_iterator *old_iter,
...@@ -344,24 +369,10 @@ static git_diff *diff_list_alloc( ...@@ -344,24 +369,10 @@ static git_diff *diff_list_alloc(
/* Use case-insensitive compare if either iterator has /* Use case-insensitive compare if either iterator has
* the ignore_case bit set */ * the ignore_case bit set */
if (!git_iterator_ignore_case(old_iter) && diff_set_ignore_case(
!git_iterator_ignore_case(new_iter)) { diff,
diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE; git_iterator_ignore_case(old_iter) ||
git_iterator_ignore_case(new_iter));
diff->strcomp = git__strcmp;
diff->strncomp = git__strncmp;
diff->pfxcomp = git__prefixcmp;
diff->entrycomp = git_index_entry__cmp;
} else {
diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
diff->strcomp = git__strcasecmp;
diff->strncomp = git__strncasecmp;
diff->pfxcomp = git__prefixcmp_icase;
diff->entrycomp = git_index_entry__cmp_icase;
git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
}
return diff; return diff;
} }
...@@ -1183,39 +1194,25 @@ int git_diff_tree_to_index( ...@@ -1183,39 +1194,25 @@ int git_diff_tree_to_index(
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; int error = 0;
bool reset_index_ignore_case = false; bool index_ignore_case = false;
assert(diff && repo); assert(diff && repo);
if (!index && (error = diff_load_index(&index, repo)) < 0) if (!index && (error = diff_load_index(&index, repo)) < 0)
return error; return error;
if (index->ignore_case) { index_ignore_case = index->ignore_case;
git_index__set_ignore_case(index, false);
reset_index_ignore_case = true;
}
DIFF_FROM_ITERATORS( DIFF_FROM_ITERATORS(
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), git_iterator_for_tree(
git_iterator_for_index(&b, index, 0, pfx, pfx) &a, old_tree, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx),
git_iterator_for_index(
&b, index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx)
); );
if (reset_index_ignore_case) { /* if index is in case-insensitive order, re-sort deltas to match */
git_index__set_ignore_case(index, true); if (!error && index_ignore_case)
diff_set_ignore_case(*diff, true);
if (!error) {
git_diff *d = *diff;
d->opts.flags |= GIT_DIFF_IGNORE_CASE;
d->strcomp = git__strcasecmp;
d->strncomp = git__strncasecmp;
d->pfxcomp = git__prefixcmp_icase;
d->entrycomp = git_index_entry__cmp_icase;
git_vector_set_cmp(&d->deltas, git_diff_delta__casecmp);
git_vector_sort(&d->deltas);
}
}
return error; return error;
} }
......
...@@ -66,7 +66,7 @@ git_diff_driver_registry *git_diff_driver_registry_new() ...@@ -66,7 +66,7 @@ git_diff_driver_registry *git_diff_driver_registry_new()
if (!reg) if (!reg)
return NULL; return NULL;
if ((reg->drivers = git_strmap_alloc()) == NULL) { if (git_strmap_alloc(&reg->drivers) < 0) {
git_diff_driver_registry_free(reg); git_diff_driver_registry_free(reg);
return NULL; return NULL;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "diff.h" #include "diff.h"
#include "diff_patch.h" #include "diff_patch.h"
#include "fileops.h" #include "fileops.h"
#include "git2/sys/diff.h"
typedef struct { typedef struct {
git_diff *diff; git_diff *diff;
...@@ -435,7 +436,7 @@ int git_patch_print( ...@@ -435,7 +436,7 @@ int git_patch_print(
return error; return error;
} }
static int diff_print_to_buffer_cb( int git_diff_print_callback__to_buf(
const git_diff_delta *delta, const git_diff_delta *delta,
const git_diff_hunk *hunk, const git_diff_hunk *hunk,
const git_diff_line *line, const git_diff_line *line,
...@@ -444,6 +445,11 @@ static int diff_print_to_buffer_cb( ...@@ -444,6 +445,11 @@ static int diff_print_to_buffer_cb(
git_buf *output = payload; git_buf *output = payload;
GIT_UNUSED(delta); GIT_UNUSED(hunk); GIT_UNUSED(delta); GIT_UNUSED(hunk);
if (!output) {
giterr_set(GITERR_INVALID, "Buffer pointer must be provided");
return -1;
}
if (line->origin == GIT_DIFF_LINE_ADDITION || if (line->origin == GIT_DIFF_LINE_ADDITION ||
line->origin == GIT_DIFF_LINE_DELETION || line->origin == GIT_DIFF_LINE_DELETION ||
line->origin == GIT_DIFF_LINE_CONTEXT) line->origin == GIT_DIFF_LINE_CONTEXT)
...@@ -452,10 +458,28 @@ static int diff_print_to_buffer_cb( ...@@ -452,10 +458,28 @@ static int diff_print_to_buffer_cb(
return git_buf_put(output, line->content, line->content_len); return git_buf_put(output, line->content, line->content_len);
} }
int git_diff_print_callback__to_file_handle(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload)
{
FILE *fp = payload ? payload : stdout;
GIT_UNUSED(delta); GIT_UNUSED(hunk);
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
line->origin == GIT_DIFF_LINE_ADDITION ||
line->origin == GIT_DIFF_LINE_DELETION)
fputc(line->origin, fp);
fwrite(line->content, 1, line->content_len, fp);
return 0;
}
/* print a git_patch to a git_buf */ /* print a git_patch to a git_buf */
int git_patch_to_buf( int git_patch_to_buf(
git_buf *out, git_buf *out,
git_patch *patch) git_patch *patch)
{ {
return git_patch_print(patch, diff_print_to_buffer_cb, out); return git_patch_print(patch, git_diff_print_callback__to_buf, out);
} }
...@@ -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;
} }
...@@ -804,10 +805,8 @@ int git_futils_filestamp_check( ...@@ -804,10 +805,8 @@ int git_futils_filestamp_check(
if (stamp == NULL) if (stamp == NULL)
return 1; return 1;
if (p_stat(path, &st) < 0) { if (p_stat(path, &st) < 0)
giterr_set(GITERR_OS, "Could not stat '%s'", path);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
}
if (stamp->mtime == (git_time_t)st.st_mtime && if (stamp->mtime == (git_time_t)st.st_mtime &&
stamp->size == (git_off_t)st.st_size && stamp->size == (git_off_t)st.st_size &&
...@@ -831,3 +830,16 @@ void git_futils_filestamp_set( ...@@ -831,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));
}
}
...@@ -292,13 +292,14 @@ typedef struct { ...@@ -292,13 +292,14 @@ typedef struct {
* Compare stat information for file with reference info. * Compare stat information for file with reference info.
* *
* This function updates the file stamp to current data for the given path * This function updates the file stamp to current data for the given path
* and returns 0 if the file is up-to-date relative to the prior setting or * and returns 0 if the file is up-to-date relative to the prior setting,
* 1 if the file has been changed. (This also may return GIT_ENOTFOUND if * 1 if the file has been changed, or GIT_ENOTFOUND if the file doesn't
* the file doesn't exist.) * exist. This will not call giterr_set, so you must set the error if you
* plan to return an error.
* *
* @param stamp File stamp to be checked * @param stamp File stamp to be checked
* @param path Path to stat and check if changed * @param path Path to stat and check if changed
* @return 0 if up-to-date, 1 if out-of-date, <0 on error * @return 0 if up-to-date, 1 if out-of-date, GIT_ENOTFOUND if cannot stat
*/ */
extern int git_futils_filestamp_check( extern int git_futils_filestamp_check(
git_futils_filestamp *stamp, const char *path); git_futils_filestamp *stamp, const char *path);
...@@ -316,4 +317,10 @@ extern int git_futils_filestamp_check( ...@@ -316,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__ */
...@@ -16,8 +16,9 @@ git_mutex git__mwindow_mutex; ...@@ -16,8 +16,9 @@ git_mutex git__mwindow_mutex;
#define MAX_SHUTDOWN_CB 8 #define MAX_SHUTDOWN_CB 8
git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
git_atomic git__n_shutdown_callbacks; static git_atomic git__n_shutdown_callbacks;
static git_atomic git__n_inits;
void git__on_shutdown(git_global_shutdown_fn callback) void git__on_shutdown(git_global_shutdown_fn callback)
{ {
...@@ -74,7 +75,6 @@ static void git__shutdown(void) ...@@ -74,7 +75,6 @@ static void git__shutdown(void)
static DWORD _tls_index; static DWORD _tls_index;
static DWORD _mutex = 0; static DWORD _mutex = 0;
static DWORD _n_inits = 0;
static int synchronized_threads_init() static int synchronized_threads_init()
{ {
...@@ -101,7 +101,7 @@ int git_threads_init(void) ...@@ -101,7 +101,7 @@ int git_threads_init(void)
while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
/* Only do work on a 0 -> 1 transition of the refcount */ /* Only do work on a 0 -> 1 transition of the refcount */
if (1 == ++_n_inits) if (1 == git_atomic_inc(&git__n_inits))
error = synchronized_threads_init(); error = synchronized_threads_init();
/* Exit the lock */ /* Exit the lock */
...@@ -124,7 +124,7 @@ void git_threads_shutdown(void) ...@@ -124,7 +124,7 @@ void git_threads_shutdown(void)
while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
/* Only do work on a 1 -> 0 transition of the refcount */ /* Only do work on a 1 -> 0 transition of the refcount */
if (0 == --_n_inits) if (0 == git_atomic_dec(&git__n_inits))
synchronized_threads_shutdown(); synchronized_threads_shutdown();
/* Exit the lock */ /* Exit the lock */
...@@ -135,7 +135,7 @@ git_global_st *git__global_state(void) ...@@ -135,7 +135,7 @@ git_global_st *git__global_state(void)
{ {
void *ptr; void *ptr;
assert(_n_inits); assert(git_atomic_get(&git__n_inits) > 0);
if ((ptr = TlsGetValue(_tls_index)) != NULL) if ((ptr = TlsGetValue(_tls_index)) != NULL)
return ptr; return ptr;
...@@ -153,7 +153,6 @@ git_global_st *git__global_state(void) ...@@ -153,7 +153,6 @@ git_global_st *git__global_state(void)
static pthread_key_t _tls_key; static pthread_key_t _tls_key;
static pthread_once_t _once_init = PTHREAD_ONCE_INIT; static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
static git_atomic git__n_inits;
int init_error = 0; int init_error = 0;
static void cb__free_status(void *st) static void cb__free_status(void *st)
...@@ -204,7 +203,7 @@ git_global_st *git__global_state(void) ...@@ -204,7 +203,7 @@ git_global_st *git__global_state(void)
{ {
void *ptr; void *ptr;
assert(git__n_inits.val); assert(git_atomic_get(&git__n_inits) > 0);
if ((ptr = pthread_getspecific(_tls_key)) != NULL) if ((ptr = pthread_getspecific(_tls_key)) != NULL)
return ptr; return ptr;
...@@ -224,14 +223,15 @@ static git_global_st __state; ...@@ -224,14 +223,15 @@ static git_global_st __state;
int git_threads_init(void) int git_threads_init(void)
{ {
/* noop */ git_atomic_inc(&git__n_inits);
return 0; return 0;
} }
void git_threads_shutdown(void) void git_threads_shutdown(void)
{ {
/* Shut down any subsystems that have global state */ /* Shut down any subsystems that have global state */
git__shutdown(); if (0 == git_atomic_dec(&git__n_inits))
git__shutdown();
} }
git_global_st *git__global_state(void) git_global_st *git__global_state(void)
......
#include "git2/ignore.h" #include "git2/ignore.h"
#include "common.h" #include "common.h"
#include "ignore.h" #include "ignore.h"
#include "attr.h" #include "attrcache.h"
#include "path.h" #include "path.h"
#include "config.h" #include "config.h"
...@@ -10,26 +10,26 @@ ...@@ -10,26 +10,26 @@
#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, void *parsedata, const char *buffer, git_attr_file *ignores) git_repository *repo, git_attr_file *attrs, const char *data)
{ {
int error = 0; int error = 0;
git_attr_fnmatch *match = NULL;
const char *scan = NULL, *context = NULL;
int ignore_case = false; int ignore_case = false;
const char *scan = data, *context = NULL;
git_attr_fnmatch *match = NULL;
/* Prefer to have the caller pass in a git_ignores as the parsedata if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0)
* object. If they did not, then look up the value of ignore_case */ giterr_clear();
if (parsedata != NULL)
ignore_case = ((git_ignores *)parsedata)->ignore_case;
else if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0)
return error;
if (ignores->key && /* if subdir file path, convert context for file paths */
git_path_root(ignores->key + 2) < 0 && if (attrs->entry &&
git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) git_path_root(attrs->entry->path) < 0 &&
context = ignores->key + 2; !git__suffixcmp(attrs->entry->path, "/" GIT_IGNORE_FILE))
context = attrs->entry->path;
scan = buffer; if (git_mutex_lock(&attrs->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock ignore file");
return -1;
}
while (!error && *scan) { while (!error && *scan) {
if (!match) { if (!match) {
...@@ -40,7 +40,7 @@ static int parse_ignore_file( ...@@ -40,7 +40,7 @@ static int parse_ignore_file(
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG; match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
if (!(error = git_attr_fnmatch__parse( if (!(error = git_attr_fnmatch__parse(
match, ignores->pool, context, &scan))) match, &attrs->pool, context, &scan)))
{ {
match->flags |= GIT_ATTR_FNMATCH_IGNORE; match->flags |= GIT_ATTR_FNMATCH_IGNORE;
...@@ -48,7 +48,7 @@ static int parse_ignore_file( ...@@ -48,7 +48,7 @@ static int parse_ignore_file(
match->flags |= GIT_ATTR_FNMATCH_ICASE; match->flags |= GIT_ATTR_FNMATCH_ICASE;
scan = git__next_line(scan); scan = git__next_line(scan);
error = git_vector_insert(&ignores->rules, match); error = git_vector_insert(&attrs->rules, match);
} }
if (error != 0) { if (error != 0) {
...@@ -62,33 +62,55 @@ static int parse_ignore_file( ...@@ -62,33 +62,55 @@ static int parse_ignore_file(
} }
} }
git_mutex_unlock(&attrs->lock);
git__free(match); git__free(match);
return error; return error;
} }
#define push_ignore_file(R,IGN,S,B,F) \ static int push_ignore_file(
git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S)) git_ignores *ignores,
git_vector *which_list,
const char *base,
const char *filename)
{
int error = 0;
git_attr_file *file = NULL;
error = git_attr_cache__get(
&file, ignores->repo, GIT_ATTR_FILE__FROM_FILE,
base, filename, parse_ignore_file);
if (error < 0)
return error;
if (file != NULL) {
if ((error = git_vector_insert(which_list, file)) < 0)
git_attr_file__free(file);
}
return error;
}
static int push_one_ignore(void *payload, git_buf *path) static int push_one_ignore(void *payload, git_buf *path)
{ {
git_ignores *ign = payload; git_ignores *ign = payload;
ign->depth++; ign->depth++;
return push_ignore_file(ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
return push_ignore_file(
ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
} }
static int get_internal_ignores(git_attr_file **ign, git_repository *repo) static int get_internal_ignores(git_attr_file **out, git_repository *repo)
{ {
int error; int error;
if (!(error = git_attr_cache__init(repo))) if ((error = git_attr_cache__init(repo)) < 0)
error = git_attr_cache__internal_file(repo, GIT_IGNORE_INTERNAL, ign); return error;
error = git_attr_cache__get(
out, repo, GIT_ATTR_FILE__IN_MEMORY, NULL, GIT_IGNORE_INTERNAL, NULL);
if (!error && !(*ign)->rules.length) /* if internal rules list is empty, insert default rules */
error = parse_ignore_file(repo, NULL, GIT_IGNORE_DEFAULT_RULES, *ign); if (!error && !(*out)->rules.length)
error = parse_ignore_file(repo, *out, GIT_IGNORE_DEFAULT_RULES);
return error; return error;
} }
...@@ -103,19 +125,15 @@ int git_ignore__for_path( ...@@ -103,19 +125,15 @@ int git_ignore__for_path(
assert(ignores); assert(ignores);
memset(ignores, 0, sizeof(*ignores));
ignores->repo = repo; ignores->repo = repo;
git_buf_init(&ignores->dir, 0);
ignores->ign_internal = NULL;
ignores->depth = 0;
/* Read the ignore_case flag */ /* Read the ignore_case flag */
if ((error = git_repository__cvar( if ((error = git_repository__cvar(
&ignores->ignore_case, repo, GIT_CVAR_IGNORECASE)) < 0) &ignores->ignore_case, repo, GIT_CVAR_IGNORECASE)) < 0)
goto cleanup; goto cleanup;
if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 || if ((error = git_attr_cache__init(repo)) < 0)
(error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 ||
(error = git_attr_cache__init(repo)) < 0)
goto cleanup; goto cleanup;
/* given a unrooted path in a non-bare repo, resolve it */ /* given a unrooted path in a non-bare repo, resolve it */
...@@ -127,8 +145,7 @@ int git_ignore__for_path( ...@@ -127,8 +145,7 @@ int git_ignore__for_path(
goto cleanup; goto cleanup;
/* set up internals */ /* set up internals */
error = get_internal_ignores(&ignores->ign_internal, repo); if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0)
if (error < 0)
goto cleanup; goto cleanup;
/* load .gitignore up the path */ /* load .gitignore up the path */
...@@ -140,14 +157,16 @@ int git_ignore__for_path( ...@@ -140,14 +157,16 @@ int git_ignore__for_path(
} }
/* load .git/info/exclude */ /* load .git/info/exclude */
error = push_ignore_file(repo, ignores, &ignores->ign_global, error = push_ignore_file(
ignores, &ignores->ign_global,
git_repository_path(repo), GIT_IGNORE_FILE_INREPO); git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
/* load core.excludesfile */ /* load core.excludesfile */
if (git_repository_attr_cache(repo)->cfg_excl_file != NULL) if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
error = push_ignore_file(repo, ignores, &ignores->ign_global, NULL, error = push_ignore_file(
ignores, &ignores->ign_global, NULL,
git_repository_attr_cache(repo)->cfg_excl_file); git_repository_attr_cache(repo)->cfg_excl_file);
cleanup: cleanup:
...@@ -165,33 +184,34 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir) ...@@ -165,33 +184,34 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir)
ign->depth++; ign->depth++;
return push_ignore_file( return push_ignore_file(
ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE); ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
} }
int git_ignore__pop_dir(git_ignores *ign) 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, *end, *scan; const char *start = file->entry->path, *end;
size_t keylen;
/* - ign->dir looks something like "a/b/" (or "a/b/c/d/") /* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/")
* - file->key looks something like "0#a/b/.gitignore * - file->path looks something like "a/b/.gitignore
* *
* We are popping the last directory off ign->dir. We also want to * We are popping the last directory off ign->dir. We also want
* remove the file from the vector if the directory part of the key * to remove the file from the vector if the popped directory
* matches the ign->dir path. We need to test if the "a/b" part of * matches the ignore path. We need to test if the "a/b" part of
* the file key matches the path we are about to pop. * the file key matches the path we are about to pop.
*/ */
for (start = end = scan = &file->key[2]; *scan; ++scan) if ((end = strrchr(start, '/')) != NULL) {
if (*scan == '/') size_t dirlen = (end - start) + 1;
end = scan; /* point 'end' to last '/' in key */
keylen = (end - start) + 1;
if (ign->dir.size >= keylen && if (ign->dir.size >= dirlen &&
!memcmp(ign->dir.ptr + ign->dir.size - keylen, start, keylen)) !memcmp(ign->dir.ptr + ign->dir.size - dirlen, start, dirlen))
git_vector_pop(&ign->ign_path); {
git_vector_pop(&ign->ign_path);
git_attr_file__free(file);
}
}
} }
if (--ign->depth > 0) { if (--ign->depth > 0) {
...@@ -204,19 +224,33 @@ int git_ignore__pop_dir(git_ignores *ign) ...@@ -204,19 +224,33 @@ int git_ignore__pop_dir(git_ignores *ign)
void git_ignore__free(git_ignores *ignores) void git_ignore__free(git_ignores *ignores)
{ {
/* don't need to free ignores->ign_internal since it is in cache */ unsigned int i;
git_attr_file *file;
git_attr_file__free(ignores->ign_internal);
git_vector_foreach(&ignores->ign_path, i, file) {
git_attr_file__free(file);
ignores->ign_path.contents[i] = NULL;
}
git_vector_free(&ignores->ign_path); git_vector_free(&ignores->ign_path);
git_vector_foreach(&ignores->ign_global, i, file) {
git_attr_file__free(file);
ignores->ign_global.contents[i] = NULL;
}
git_vector_free(&ignores->ign_global); git_vector_free(&ignores->ign_global);
git_buf_free(&ignores->dir); git_buf_free(&ignores->dir);
} }
static bool ignore_lookup_in_rules( static bool ignore_lookup_in_rules(
git_vector *rules, git_attr_path *path, int *ignored) git_attr_file *file, git_attr_path *path, int *ignored)
{ {
size_t j; size_t j;
git_attr_fnmatch *match; git_attr_fnmatch *match;
git_vector_rforeach(rules, j, match) { git_vector_rforeach(&file->rules, j, match) {
if (git_attr_fnmatch__match(match, path)) { if (git_attr_fnmatch__match(match, path)) {
*ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0); *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);
return true; return true;
...@@ -238,19 +272,18 @@ int git_ignore__lookup( ...@@ -238,19 +272,18 @@ int git_ignore__lookup(
return -1; return -1;
/* first process builtins - success means path was found */ /* first process builtins - success means path was found */
if (ignore_lookup_in_rules( if (ignore_lookup_in_rules(ignores->ign_internal, &path, ignored))
&ignores->ign_internal->rules, &path, ignored))
goto cleanup; goto cleanup;
/* next process files in the path */ /* next process files in the path */
git_vector_foreach(&ignores->ign_path, i, file) { git_vector_foreach(&ignores->ign_path, i, file) {
if (ignore_lookup_in_rules(&file->rules, &path, ignored)) if (ignore_lookup_in_rules(file, &path, ignored))
goto cleanup; goto cleanup;
} }
/* last process global ignores */ /* last process global ignores */
git_vector_foreach(&ignores->ign_global, i, file) { git_vector_foreach(&ignores->ign_global, i, file) {
if (ignore_lookup_in_rules(&file->rules, &path, ignored)) if (ignore_lookup_in_rules(file, &path, ignored))
goto cleanup; goto cleanup;
} }
...@@ -261,32 +294,33 @@ cleanup: ...@@ -261,32 +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; 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, NULL, rules, ign_internal); return error;
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)
git_attr_file__clear_rules(ign_internal); return error;
return parse_ignore_file( if (!(error = git_attr_file__clear_rules(ign_internal, true)))
repo, NULL, GIT_IGNORE_DEFAULT_RULES, ign_internal); error = parse_ignore_file(
} repo, ign_internal, GIT_IGNORE_DEFAULT_RULES);
git_attr_file__free(ign_internal);
return error; return error;
} }
...@@ -331,19 +365,18 @@ int git_ignore_path_is_ignored( ...@@ -331,19 +365,18 @@ int git_ignore_path_is_ignored(
break; break;
/* first process builtins - success means path was found */ /* first process builtins - success means path was found */
if (ignore_lookup_in_rules( if (ignore_lookup_in_rules(ignores.ign_internal, &path, ignored))
&ignores.ign_internal->rules, &path, ignored))
goto cleanup; goto cleanup;
/* next process files in the path */ /* next process files in the path */
git_vector_foreach(&ignores.ign_path, i, file) { git_vector_foreach(&ignores.ign_path, i, file) {
if (ignore_lookup_in_rules(&file->rules, &path, ignored)) if (ignore_lookup_in_rules(file, &path, ignored))
goto cleanup; goto cleanup;
} }
/* last process global ignores */ /* last process global ignores */
git_vector_foreach(&ignores.ign_global, i, file) { git_vector_foreach(&ignores.ign_global, i, file) {
if (ignore_lookup_in_rules(&file->rules, &path, ignored)) if (ignore_lookup_in_rules(file, &path, ignored))
goto cleanup; goto cleanup;
} }
......
...@@ -90,13 +90,24 @@ struct entry_long { ...@@ -90,13 +90,24 @@ struct entry_long {
struct entry_srch_key { struct entry_srch_key {
const char *path; const char *path;
size_t path_len; size_t pathlen;
int stage; int stage;
}; };
struct entry_internal {
git_index_entry entry;
size_t pathlen;
char path[GIT_FLEX_ARRAY];
};
struct reuc_entry_internal {
git_index_reuc_entry entry;
size_t pathlen;
char path[GIT_FLEX_ARRAY];
};
/* local declarations */ /* local declarations */
static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size); static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);
static int read_header(struct index_header *dest, const void *buffer); static int read_header(struct index_header *dest, const void *buffer);
static int parse_index(git_index *index, const char *buffer, size_t buffer_size); static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
...@@ -106,15 +117,15 @@ static int write_index(git_index *index, git_filebuf *file); ...@@ -106,15 +117,15 @@ static int write_index(git_index *index, git_filebuf *file);
static void index_entry_free(git_index_entry *entry); static void index_entry_free(git_index_entry *entry);
static void index_entry_reuc_free(git_index_reuc_entry *reuc); static void index_entry_reuc_free(git_index_reuc_entry *reuc);
static int index_srch(const void *key, const void *array_member) int git_index_entry_srch(const void *key, const void *array_member)
{ {
const struct entry_srch_key *srch_key = key; const struct entry_srch_key *srch_key = key;
const git_index_entry *entry = array_member; const struct entry_internal *entry = array_member;
int cmp; int cmp;
size_t len1, len2, len; size_t len1, len2, len;
len1 = srch_key->path_len; len1 = srch_key->pathlen;
len2 = strlen(entry->path); len2 = entry->pathlen;
len = len1 < len2 ? len1 : len2; len = len1 < len2 ? len1 : len2;
cmp = memcmp(srch_key->path, entry->path, len); cmp = memcmp(srch_key->path, entry->path, len);
...@@ -126,20 +137,20 @@ static int index_srch(const void *key, const void *array_member) ...@@ -126,20 +137,20 @@ static int index_srch(const void *key, const void *array_member)
return 1; return 1;
if (srch_key->stage != GIT_INDEX_STAGE_ANY) if (srch_key->stage != GIT_INDEX_STAGE_ANY)
return srch_key->stage - GIT_IDXENTRY_STAGE(entry); return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry);
return 0; return 0;
} }
static int index_isrch(const void *key, const void *array_member) int git_index_entry_isrch(const void *key, const void *array_member)
{ {
const struct entry_srch_key *srch_key = key; const struct entry_srch_key *srch_key = key;
const git_index_entry *entry = array_member; const struct entry_internal *entry = array_member;
int cmp; int cmp;
size_t len1, len2, len; size_t len1, len2, len;
len1 = srch_key->path_len; len1 = srch_key->pathlen;
len2 = strlen(entry->path); len2 = entry->pathlen;
len = len1 < len2 ? len1 : len2; len = len1 < len2 ? len1 : len2;
cmp = strncasecmp(srch_key->path, entry->path, len); cmp = strncasecmp(srch_key->path, entry->path, len);
...@@ -152,36 +163,26 @@ static int index_isrch(const void *key, const void *array_member) ...@@ -152,36 +163,26 @@ static int index_isrch(const void *key, const void *array_member)
return 1; return 1;
if (srch_key->stage != GIT_INDEX_STAGE_ANY) if (srch_key->stage != GIT_INDEX_STAGE_ANY)
return srch_key->stage - GIT_IDXENTRY_STAGE(entry); return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry);
return 0; return 0;
} }
static int index_cmp_path(const void *a, const void *b) static int index_entry_srch_path(const void *path, const void *array_member)
{
return strcmp((const char *)a, (const char *)b);
}
static int index_icmp_path(const void *a, const void *b)
{
return strcasecmp((const char *)a, (const char *)b);
}
static int index_srch_path(const void *path, const void *array_member)
{ {
const git_index_entry *entry = array_member; const git_index_entry *entry = array_member;
return strcmp((const char *)path, entry->path); return strcmp((const char *)path, entry->path);
} }
static int index_isrch_path(const void *path, const void *array_member) static int index_entry_isrch_path(const void *path, const void *array_member)
{ {
const git_index_entry *entry = array_member; const git_index_entry *entry = array_member;
return strcasecmp((const char *)path, entry->path); return strcasecmp((const char *)path, entry->path);
} }
static int index_cmp(const void *a, const void *b) int git_index_entry_cmp(const void *a, const void *b)
{ {
int diff; int diff;
const git_index_entry *entry_a = a; const git_index_entry *entry_a = a;
...@@ -195,7 +196,7 @@ static int index_cmp(const void *a, const void *b) ...@@ -195,7 +196,7 @@ static int index_cmp(const void *a, const void *b)
return diff; return diff;
} }
static int index_icmp(const void *a, const void *b) int git_index_entry_icmp(const void *a, const void *b)
{ {
int diff; int diff;
const git_index_entry *entry_a = a; const git_index_entry *entry_a = a;
...@@ -286,17 +287,12 @@ static int reuc_icmp(const void *a, const void *b) ...@@ -286,17 +287,12 @@ static int reuc_icmp(const void *a, const void *b)
static void index_entry_reuc_free(git_index_reuc_entry *reuc) static void index_entry_reuc_free(git_index_reuc_entry *reuc)
{ {
if (!reuc)
return;
git__free(reuc->path);
git__free(reuc); git__free(reuc);
} }
static void index_entry_free(git_index_entry *entry) static void index_entry_free(git_index_entry *entry)
{ {
if (!entry) memset(&entry->id, 0, sizeof(entry->id));
return;
git__free(entry->path);
git__free(entry); git__free(entry);
} }
...@@ -325,18 +321,69 @@ static unsigned int index_merge_mode( ...@@ -325,18 +321,69 @@ static unsigned int index_merge_mode(
return git_index__create_mode(mode); return git_index__create_mode(mode);
} }
void git_index__set_ignore_case(git_index *index, bool ignore_case) static int index_sort_if_needed(git_index *index, bool need_lock)
{ {
index->ignore_case = ignore_case; /* not truly threadsafe because between when this checks and/or
* sorts the array another thread could come in and unsort it
*/
index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path; if (git_vector_is_sorted(&index->entries))
index->entries_search = ignore_case ? index_isrch : index_srch; return 0;
index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path;
if (need_lock && git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Unable to lock index");
return -1;
}
git_vector_set_cmp(&index->entries, ignore_case ? index_icmp : index_cmp);
git_vector_sort(&index->entries); git_vector_sort(&index->entries);
index->reuc_search = ignore_case ? reuc_isrch : reuc_srch; if (need_lock)
git_mutex_unlock(&index->lock);
return 0;
}
GIT_INLINE(int) index_find_in_entries(
size_t *out, git_vector *entries, git_vector_cmp entry_srch,
const char *path, size_t path_len, int stage)
{
struct entry_srch_key srch_key;
srch_key.path = path;
srch_key.pathlen = !path_len ? strlen(path) : path_len;
srch_key.stage = stage;
return git_vector_bsearch2(out, entries, entry_srch, &srch_key);
}
GIT_INLINE(int) index_find(
size_t *out, git_index *index,
const char *path, size_t path_len, int stage, bool need_lock)
{
if (index_sort_if_needed(index, need_lock) < 0)
return -1;
return index_find_in_entries(
out, &index->entries, index->entries_search, path, path_len, stage);
}
void git_index__set_ignore_case(git_index *index, bool ignore_case)
{
index->ignore_case = ignore_case;
if (ignore_case) {
index->entries_cmp_path = git__strcasecmp_cb;
index->entries_search = git_index_entry_isrch;
index->entries_search_path = index_entry_isrch_path;
index->reuc_search = reuc_isrch;
} else {
index->entries_cmp_path = git__strcmp_cb;
index->entries_search = git_index_entry_srch;
index->entries_search_path = index_entry_srch_path;
index->reuc_search = reuc_srch;
}
git_vector_set_cmp(&index->entries,
ignore_case ? git_index_entry_icmp : git_index_entry_cmp);
index_sort_if_needed(index, true);
git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp); git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp);
git_vector_sort(&index->reuc); git_vector_sort(&index->reuc);
...@@ -345,41 +392,51 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case) ...@@ -345,41 +392,51 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case)
int git_index_open(git_index **index_out, const char *index_path) int git_index_open(git_index **index_out, const char *index_path)
{ {
git_index *index; git_index *index;
int error; int error = -1;
assert(index_out); assert(index_out);
index = git__calloc(1, sizeof(git_index)); index = git__calloc(1, sizeof(git_index));
GITERR_CHECK_ALLOC(index); GITERR_CHECK_ALLOC(index);
if (git_mutex_init(&index->lock)) {
giterr_set(GITERR_OS, "Failed to initialize lock");
git__free(index);
return -1;
}
if (index_path != NULL) { if (index_path != NULL) {
index->index_file_path = git__strdup(index_path); index->index_file_path = git__strdup(index_path);
GITERR_CHECK_ALLOC(index->index_file_path); if (!index->index_file_path)
goto fail;
/* Check if index file is stored on disk already */ /* Check if index file is stored on disk already */
if (git_path_exists(index->index_file_path) == true) if (git_path_exists(index->index_file_path) == true)
index->on_disk = 1; index->on_disk = 1;
} }
if (git_vector_init(&index->entries, 32, index_cmp) < 0 || if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 ||
git_vector_init(&index->names, 32, conflict_name_cmp) < 0 || git_vector_init(&index->names, 8, conflict_name_cmp) < 0 ||
git_vector_init(&index->reuc, 32, reuc_cmp) < 0) git_vector_init(&index->reuc, 8, reuc_cmp) < 0 ||
return -1; git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0)
goto fail;
index->entries_cmp_path = index_cmp_path; index->entries_cmp_path = git__strcmp_cb;
index->entries_search = index_srch; index->entries_search = git_index_entry_srch;
index->entries_search_path = index_srch_path; index->entries_search_path = index_entry_srch_path;
index->reuc_search = reuc_srch; index->reuc_search = reuc_srch;
if ((index_path != NULL) && ((error = git_index_read(index, true)) < 0)) { if (index_path != NULL && (error = git_index_read(index, true)) < 0)
git_index_free(index); goto fail;
return error;
}
*index_out = index; *index_out = index;
GIT_REFCOUNT_INC(index); GIT_REFCOUNT_INC(index);
return 0; return 0;
fail:
git_index_free(index);
return error;
} }
int git_index_new(git_index **out) int git_index_new(git_index **out)
...@@ -389,12 +446,19 @@ int git_index_new(git_index **out) ...@@ -389,12 +446,19 @@ int git_index_new(git_index **out)
static void index_free(git_index *index) static void index_free(git_index *index)
{ {
/* index iterators increment the refcount of the index, so if we
* get here then there should be no outstanding iterators.
*/
assert(!git_atomic_get(&index->readers));
git_index_clear(index); git_index_clear(index);
git_vector_free(&index->entries); git_vector_free(&index->entries);
git_vector_free(&index->names); git_vector_free(&index->names);
git_vector_free(&index->reuc); git_vector_free(&index->reuc);
git_vector_free(&index->deleted);
git__free(index->index_file_path); git__free(index->index_file_path);
git_mutex_free(&index->lock);
git__memzero(index, sizeof(*index)); git__memzero(index, sizeof(*index));
git__free(index); git__free(index);
...@@ -408,28 +472,71 @@ void git_index_free(git_index *index) ...@@ -408,28 +472,71 @@ void git_index_free(git_index *index)
GIT_REFCOUNT_DEC(index, index_free); GIT_REFCOUNT_DEC(index, index_free);
} }
static void index_entries_free(git_vector *entries) /* call with locked index */
static void index_free_deleted(git_index *index)
{ {
int readers = (int)git_atomic_get(&index->readers);
size_t i; size_t i;
for (i = 0; i < entries->length; ++i) if (readers > 0 || !index->deleted.length)
index_entry_free(git__swap(entries->contents[i], NULL)); return;
for (i = 0; i < index->deleted.length; ++i) {
git_index_entry *ie = git__swap(index->deleted.contents[i], NULL);
index_entry_free(ie);
}
git_vector_clear(entries); git_vector_clear(&index->deleted);
} }
void git_index_clear(git_index *index) /* call with locked index */
static int index_remove_entry(git_index *index, size_t pos)
{ {
int error = 0;
git_index_entry *entry = git_vector_get(&index->entries, pos);
if (entry != NULL)
git_tree_cache_invalidate_path(index->tree, entry->path);
error = git_vector_remove(&index->entries, pos);
if (!error) {
if (git_atomic_get(&index->readers) > 0) {
error = git_vector_insert(&index->deleted, entry);
} else {
index_entry_free(entry);
}
}
return error;
}
int git_index_clear(git_index *index)
{
int error = 0;
assert(index); assert(index);
index_entries_free(&index->entries); git_tree_cache_free(index->tree);
index->tree = NULL;
if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock index");
return -1;
}
while (!error && index->entries.length > 0)
error = index_remove_entry(index, index->entries.length - 1);
index_free_deleted(index);
git_index_reuc_clear(index); git_index_reuc_clear(index);
git_index_name_clear(index); git_index_name_clear(index);
git_futils_filestamp_set(&index->stamp, NULL); git_futils_filestamp_set(&index->stamp, NULL);
git_tree_cache_free(index->tree); git_mutex_unlock(&index->lock);
index->tree = NULL;
return error;
} }
static int create_index_error(int error, const char *msg) static int create_index_error(int error, const char *msg)
...@@ -495,20 +602,29 @@ int git_index_read(git_index *index, int force) ...@@ -495,20 +602,29 @@ int git_index_read(git_index *index, int force)
if (!index->on_disk) { if (!index->on_disk) {
if (force) if (force)
git_index_clear(index); return git_index_clear(index);
return 0; return 0;
} }
updated = git_futils_filestamp_check(&stamp, index->index_file_path); updated = git_futils_filestamp_check(&stamp, index->index_file_path);
if (updated < 0 || (!updated && !force)) if (updated < 0) {
giterr_set(
GITERR_INDEX,
"Failed to read index: '%s' no longer exists",
index->index_file_path);
return updated; return updated;
}
if (!updated && !force)
return 0;
error = git_futils_readbuffer(&buffer, index->index_file_path); error = git_futils_readbuffer(&buffer, index->index_file_path);
if (error < 0) if (error < 0)
return error; return error;
git_index_clear(index); error = git_index_clear(index);
error = parse_index(index, buffer.ptr, buffer.size);
if (!error)
error = parse_index(index, buffer.ptr, buffer.size);
if (!error) if (!error)
git_futils_filestamp_set(&index->stamp, &stamp); git_futils_filestamp_set(&index->stamp, &stamp);
...@@ -538,7 +654,8 @@ int git_index_write(git_index *index) ...@@ -538,7 +654,8 @@ int git_index_write(git_index *index)
return create_index_error(-1, return create_index_error(-1,
"Failed to read index: The index is in-memory only"); "Failed to read index: The index is in-memory only");
git_vector_sort(&index->entries); if (index_sort_if_needed(index, true) < 0)
return -1;
git_vector_sort(&index->reuc); git_vector_sort(&index->reuc);
if ((error = git_filebuf_open( if ((error = git_filebuf_open(
...@@ -557,11 +674,11 @@ int git_index_write(git_index *index) ...@@ -557,11 +674,11 @@ int git_index_write(git_index *index)
if ((error = git_filebuf_commit(&file)) < 0) if ((error = git_filebuf_commit(&file)) < 0)
return error; return error;
error = git_futils_filestamp_check(&index->stamp, index->index_file_path); if (git_futils_filestamp_check(&index->stamp, index->index_file_path) < 0)
if (error < 0) /* index could not be read from disk! */;
return error; else
index->on_disk = 1;
index->on_disk = 1;
return 0; return 0;
} }
...@@ -586,7 +703,8 @@ int git_index_write_tree(git_oid *oid, git_index *index) ...@@ -586,7 +703,8 @@ int git_index_write_tree(git_oid *oid, git_index *index)
return git_tree__write_index(oid, index, repo); return git_tree__write_index(oid, index, repo);
} }
int git_index_write_tree_to(git_oid *oid, git_index *index, git_repository *repo) int git_index_write_tree_to(
git_oid *oid, git_index *index, git_repository *repo)
{ {
assert(oid && index && repo); assert(oid && index && repo);
return git_tree__write_index(oid, index, repo); return git_tree__write_index(oid, index, repo);
...@@ -602,7 +720,8 @@ const git_index_entry *git_index_get_byindex( ...@@ -602,7 +720,8 @@ const git_index_entry *git_index_get_byindex(
git_index *index, size_t n) git_index *index, size_t n)
{ {
assert(index); assert(index);
git_vector_sort(&index->entries); if (index_sort_if_needed(index, true) < 0)
return NULL;
return git_vector_get(&index->entries, n); return git_vector_get(&index->entries, n);
} }
...@@ -613,7 +732,7 @@ const git_index_entry *git_index_get_bypath( ...@@ -613,7 +732,7 @@ const git_index_entry *git_index_get_bypath(
assert(index); assert(index);
if (git_index__find(&pos, index, path, 0, stage) < 0) { if (index_find(&pos, index, path, 0, stage, true) < 0) {
giterr_set(GITERR_INDEX, "Index does not contain %s", path); giterr_set(GITERR_INDEX, "Index does not contain %s", path);
return NULL; return NULL;
} }
...@@ -637,20 +756,19 @@ void git_index_entry__init_from_stat( ...@@ -637,20 +756,19 @@ void git_index_entry__init_from_stat(
entry->file_size = st->st_size; entry->file_size = st->st_size;
} }
int git_index_entry__cmp(const void *a, const void *b) static git_index_entry *index_entry_alloc(const char *path)
{ {
const git_index_entry *entry_a = a; size_t pathlen = strlen(path);
const git_index_entry *entry_b = b; struct entry_internal *entry =
git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1);
return strcmp(entry_a->path, entry_b->path); if (!entry)
} return NULL;
int git_index_entry__cmp_icase(const void *a, const void *b) entry->pathlen = pathlen;
{ memcpy(entry->path, path, pathlen);
const git_index_entry *entry_a = a; entry->entry.path = entry->path;
const git_index_entry *entry_b = b;
return strcasecmp(entry_a->path, entry_b->path); return (git_index_entry *)entry;
} }
static int index_entry_init( static int index_entry_init(
...@@ -672,19 +790,31 @@ static int index_entry_init( ...@@ -672,19 +790,31 @@ static int index_entry_init(
if (error < 0) if (error < 0)
return error; return error;
entry = git__calloc(1, sizeof(git_index_entry)); entry = index_entry_alloc(rel_path);
GITERR_CHECK_ALLOC(entry); GITERR_CHECK_ALLOC(entry);
git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
entry->id = oid; entry->id = oid;
entry->path = git__strdup(rel_path); git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
GITERR_CHECK_ALLOC(entry->path);
*entry_out = entry; *entry_out = (git_index_entry *)entry;
return 0; return 0;
} }
static git_index_reuc_entry *reuc_entry_alloc(const char *path)
{
size_t pathlen = strlen(path);
struct reuc_entry_internal *entry =
git__calloc(sizeof(struct reuc_entry_internal) + pathlen + 1, 1);
if (!entry)
return NULL;
entry->pathlen = pathlen;
memcpy(entry->path, path, pathlen);
entry->entry.path = entry->path;
return (git_index_reuc_entry *)entry;
}
static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
const char *path, const char *path,
int ancestor_mode, const git_oid *ancestor_oid, int ancestor_mode, const git_oid *ancestor_oid,
...@@ -695,15 +825,9 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, ...@@ -695,15 +825,9 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
assert(reuc_out && path); assert(reuc_out && path);
*reuc_out = NULL; *reuc_out = reuc = reuc_entry_alloc(path);
reuc = git__calloc(1, sizeof(git_index_reuc_entry));
GITERR_CHECK_ALLOC(reuc); GITERR_CHECK_ALLOC(reuc);
reuc->path = git__strdup(path);
if (reuc->path == NULL)
return -1;
if ((reuc->mode[0] = ancestor_mode) > 0) if ((reuc->mode[0] = ancestor_mode) > 0)
git_oid_cpy(&reuc->oid[0], ancestor_oid); git_oid_cpy(&reuc->oid[0], ancestor_oid);
...@@ -713,26 +837,31 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, ...@@ -713,26 +837,31 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
if ((reuc->mode[2] = their_mode) > 0) if ((reuc->mode[2] = their_mode) > 0)
git_oid_cpy(&reuc->oid[2], their_oid); git_oid_cpy(&reuc->oid[2], their_oid);
*reuc_out = reuc;
return 0; return 0;
} }
static git_index_entry *index_entry_dup(const git_index_entry *source_entry) static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src)
{
char *tgt_path = tgt->path;
memcpy(tgt, src, sizeof(*tgt));
tgt->path = tgt_path; /* reset to existing path data */
}
static int index_entry_dup(git_index_entry **out, const git_index_entry *src)
{ {
git_index_entry *entry; git_index_entry *entry;
entry = git__malloc(sizeof(git_index_entry)); if (!src) {
if (!entry) *out = NULL;
return NULL; return 0;
}
memcpy(entry, source_entry, sizeof(git_index_entry)); *out = entry = index_entry_alloc(src->path);
GITERR_CHECK_ALLOC(entry);
/* duplicate the path string so we own it */ index_entry_cpy(entry, src);
entry->path = git__strdup(entry->path);
if (!entry->path)
return NULL;
return entry; return 0;
} }
static int has_file_name(git_index *index, static int has_file_name(git_index *index,
...@@ -744,20 +873,22 @@ static int has_file_name(git_index *index, ...@@ -744,20 +873,22 @@ static int has_file_name(git_index *index,
const char *name = entry->path; const char *name = entry->path;
while (pos < index->entries.length) { while (pos < index->entries.length) {
git_index_entry *p = index->entries.contents[pos++]; struct entry_internal *p = index->entries.contents[pos++];
if (len >= strlen(p->path)) if (len >= p->pathlen)
break; break;
if (memcmp(name, p->path, len)) if (memcmp(name, p->path, len))
break; break;
if (GIT_IDXENTRY_STAGE(p) != stage) if (GIT_IDXENTRY_STAGE(&p->entry) != stage)
continue; continue;
if (p->path[len] != '/') if (p->path[len] != '/')
continue; continue;
retval = -1; retval = -1;
if (!ok_to_replace) if (!ok_to_replace)
break; break;
git_vector_remove(&index->entries, --pos);
if (index_remove_entry(index, --pos) < 0)
break;
} }
return retval; return retval;
} }
...@@ -775,7 +906,7 @@ static int has_dir_name(git_index *index, ...@@ -775,7 +906,7 @@ static int has_dir_name(git_index *index,
const char *slash = name + strlen(name); const char *slash = name + strlen(name);
for (;;) { for (;;) {
size_t len, position; size_t len, pos;
for (;;) { for (;;) {
if (*--slash == '/') if (*--slash == '/')
...@@ -785,12 +916,13 @@ static int has_dir_name(git_index *index, ...@@ -785,12 +916,13 @@ static int has_dir_name(git_index *index,
} }
len = slash - name; len = slash - name;
if (git_index__find(&position, index, name, len, stage) == 0) { if (!index_find(&pos, index, name, len, stage, false)) {
retval = -1; retval = -1;
if (!ok_to_replace) if (!ok_to_replace)
break; break;
git_vector_remove(&index->entries, position); if (index_remove_entry(index, pos) < 0)
break;
continue; continue;
} }
...@@ -799,20 +931,19 @@ static int has_dir_name(git_index *index, ...@@ -799,20 +931,19 @@ static int has_dir_name(git_index *index,
* already matches the sub-directory, then we know * already matches the sub-directory, then we know
* we're ok, and we can exit. * we're ok, and we can exit.
*/ */
while (position < index->entries.length) { for (; pos < index->entries.length; ++pos) {
git_index_entry *p = index->entries.contents[position]; struct entry_internal *p = index->entries.contents[pos];
if ((strlen(p->path) <= len) || if (p->pathlen <= len ||
(p->path[len] != '/') || p->path[len] != '/' ||
memcmp(p->path, name, len)) memcmp(p->path, name, len))
break; /* not our subdirectory */ break; /* not our subdirectory */
if (GIT_IDXENTRY_STAGE(p) == stage) if (GIT_IDXENTRY_STAGE(&p->entry) == stage)
return retval; return retval;
position++;
} }
} }
return retval; return retval;
} }
...@@ -823,22 +954,41 @@ static int check_file_directory_collision(git_index *index, ...@@ -823,22 +954,41 @@ static int check_file_directory_collision(git_index *index,
retval = retval + has_dir_name(index, entry, ok_to_replace); retval = retval + has_dir_name(index, entry, ok_to_replace);
if (retval) { if (retval) {
giterr_set(GITERR_INDEX, "'%s' appears as both a file an a directory", entry->path); giterr_set(GITERR_INDEX,
"'%s' appears as both a file and a directory", entry->path);
return -1; return -1;
} }
return 0; return 0;
} }
static int index_insert(git_index *index, git_index_entry *entry, int replace) static int index_no_dups(void **old, void *new)
{
const git_index_entry *entry = new;
GIT_UNUSED(old);
giterr_set(GITERR_INDEX, "'%s' appears multiple times at stage %d",
entry->path, GIT_IDXENTRY_STAGE(entry));
return GIT_EEXISTS;
}
/* index_insert takes ownership of the new entry - if it can't insert
* it, then it will return an error **and also free the entry**. When
* it replaces an existing entry, it will update the entry_ptr with the
* actual entry in the index (and free the passed in one).
*/
static int index_insert(
git_index *index, git_index_entry **entry_ptr, int replace)
{ {
int error = 0;
size_t path_length, position; size_t path_length, position;
git_index_entry **existing = NULL; git_index_entry *existing = NULL, *entry;
assert(index && entry_ptr);
assert(index && entry && entry->path != NULL); entry = *entry_ptr;
/* make sure that the path length flag is correct */ /* make sure that the path length flag is correct */
path_length = strlen(entry->path); path_length = ((struct entry_internal *)entry)->pathlen;
entry->flags &= ~GIT_IDXENTRY_NAMEMASK; entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
...@@ -847,30 +997,51 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace) ...@@ -847,30 +997,51 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
else else
entry->flags |= GIT_IDXENTRY_NAMEMASK; entry->flags |= GIT_IDXENTRY_NAMEMASK;
if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire index lock");
return -1;
}
git_vector_sort(&index->entries);
/* look if an entry with this path already exists */ /* look if an entry with this path already exists */
if (!git_index__find( if (!index_find(
&position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry))) { &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) {
existing = (git_index_entry **)&index->entries.contents[position]; existing = index->entries.contents[position];
/* update filemode to existing values if stat is not trusted */ /* update filemode to existing values if stat is not trusted */
entry->mode = index_merge_mode(index, *existing, entry->mode); entry->mode = index_merge_mode(index, existing, entry->mode);
} }
if (check_file_directory_collision(index, entry, position, replace) < 0) /* look for tree / blob name collisions, removing conflicts if requested */
return -1; error = check_file_directory_collision(index, entry, position, replace);
if (error < 0)
/* skip changes */;
/* if replacing is not requested or no existing entry exists, just /* if we are replacing an existing item, overwrite the existing entry
* insert entry at the end; the index is no longer sorted * and return it in place of the passed in one.
*/ */
if (!replace || !existing) else if (existing) {
return git_vector_insert(&index->entries, entry); if (replace)
index_entry_cpy(existing, entry);
index_entry_free(entry);
*entry_ptr = entry = existing;
}
else {
/* if replace is not requested or no existing entry exists, insert
* at the sorted position. (Since we re-sort after each insert to
* check for dups, this is actually cheaper in the long run.)
*/
error = git_vector_insert_sorted(&index->entries, entry, index_no_dups);
}
/* exists, replace it (preserving name from existing entry) */ if (error < 0) {
git__free(entry->path); index_entry_free(*entry_ptr);
entry->path = (*existing)->path; *entry_ptr = NULL;
git__free(*existing); }
*existing = entry;
return 0; git_mutex_unlock(&index->lock);
return error;
} }
static int index_conflict_to_reuc(git_index *index, const char *path) static int index_conflict_to_reuc(git_index *index, const char *path)
...@@ -907,19 +1078,15 @@ int git_index_add_bypath(git_index *index, const char *path) ...@@ -907,19 +1078,15 @@ int git_index_add_bypath(git_index *index, const char *path)
assert(index && path); assert(index && path);
if ((ret = index_entry_init(&entry, index, path)) < 0 || if ((ret = index_entry_init(&entry, index, path)) < 0 ||
(ret = index_insert(index, entry, 1)) < 0) (ret = index_insert(index, &entry, 1)) < 0)
goto on_error; return ret;
/* Adding implies conflict was resolved, move conflict entries to REUC */ /* Adding implies conflict was resolved, move conflict entries to REUC */
if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND) if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)
goto on_error; return ret;
git_tree_cache_invalidate_path(index->tree, entry->path); git_tree_cache_invalidate_path(index->tree, entry->path);
return 0; return 0;
on_error:
index_entry_free(entry);
return ret;
} }
int git_index_remove_bypath(git_index *index, const char *path) int git_index_remove_bypath(git_index *index, const char *path)
...@@ -942,14 +1109,11 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) ...@@ -942,14 +1109,11 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
git_index_entry *entry = NULL; git_index_entry *entry = NULL;
int ret; int ret;
entry = index_entry_dup(source_entry); assert(index && source_entry && source_entry->path);
if (entry == NULL)
return -1;
if ((ret = index_insert(index, entry, 1)) < 0) { if ((ret = index_entry_dup(&entry, source_entry)) < 0 ||
index_entry_free(entry); (ret = index_insert(index, &entry, 1)) < 0)
return ret; return ret;
}
git_tree_cache_invalidate_path(index->tree, entry->path); git_tree_cache_invalidate_path(index->tree, entry->path);
return 0; return 0;
...@@ -957,25 +1121,23 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) ...@@ -957,25 +1121,23 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
int git_index_remove(git_index *index, const char *path, int stage) int git_index_remove(git_index *index, const char *path, int stage)
{ {
size_t position;
int error; int error;
git_index_entry *entry; size_t position;
if (git_index__find(&position, index, path, 0, stage) < 0) { if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d", giterr_set(GITERR_OS, "Failed to lock index");
path, stage); return -1;
return GIT_ENOTFOUND;
} }
entry = git_vector_get(&index->entries, position); if (index_find(&position, index, path, 0, stage, false) < 0) {
if (entry != NULL) giterr_set(
git_tree_cache_invalidate_path(index->tree, entry->path); GITERR_INDEX, "Index does not contain %s at stage %d", path, stage);
error = GIT_ENOTFOUND;
error = git_vector_remove(&index->entries, position); } else {
error = index_remove_entry(index, position);
if (!error) }
index_entry_free(entry);
git_mutex_unlock(&index->lock);
return error; return error;
} }
...@@ -986,14 +1148,16 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) ...@@ -986,14 +1148,16 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage)
size_t pos; size_t pos;
git_index_entry *entry; git_index_entry *entry;
if (git_buf_sets(&pfx, dir) < 0 || git_path_to_dir(&pfx) < 0) if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock index");
return -1; return -1;
}
git_vector_sort(&index->entries); if (!(error = git_buf_sets(&pfx, dir)) &&
!(error = git_path_to_dir(&pfx)))
pos = git_index__prefix_position(index, pfx.ptr); index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY, false);
while (1) { while (!error) {
entry = git_vector_get(&index->entries, pos); entry = git_vector_get(&index->entries, pos);
if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0) if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0)
break; break;
...@@ -1003,35 +1167,22 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) ...@@ -1003,35 +1167,22 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage)
continue; continue;
} }
git_tree_cache_invalidate_path(index->tree, entry->path); error = index_remove_entry(index, pos);
if ((error = git_vector_remove(&index->entries, pos)) < 0)
break;
index_entry_free(entry);
/* removed entry at 'pos' so we don't need to increment it */ /* removed entry at 'pos' so we don't need to increment */
} }
git_mutex_unlock(&index->lock);
git_buf_free(&pfx); git_buf_free(&pfx);
return error; return error;
} }
int git_index__find( int git_index__find_pos(
size_t *out, git_index *index, const char *path, size_t path_len, int stage) size_t *out, git_index *index, const char *path, size_t path_len, int stage)
{ {
struct entry_srch_key srch_key; assert(index && path);
return index_find(out, index, path, path_len, stage, true);
assert(path);
git_vector_sort(&index->entries);
srch_key.path = path;
srch_key.path_len = !path_len ? strlen(path) : path_len;
srch_key.stage = stage;
return git_vector_bsearch2(
out, &index->entries, index->entries_search, &srch_key);
} }
int git_index_find(size_t *at_pos, git_index *index, const char *path) int git_index_find(size_t *at_pos, git_index *index, const char *path)
...@@ -1040,7 +1191,14 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path) ...@@ -1040,7 +1191,14 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path)
assert(index && path); assert(index && path);
if (git_vector_bsearch2(&pos, &index->entries, index->entries_search_path, path) < 0) { if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock index");
return -1;
}
if (git_vector_bsearch2(
&pos, &index->entries, index->entries_search_path, path) < 0) {
git_mutex_unlock(&index->lock);
giterr_set(GITERR_INDEX, "Index does not contain %s", path); giterr_set(GITERR_INDEX, "Index does not contain %s", path);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
} }
...@@ -1048,37 +1206,20 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path) ...@@ -1048,37 +1206,20 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path)
/* Since our binary search only looked at path, we may be in the /* Since our binary search only looked at path, we may be in the
* middle of a list of stages. * middle of a list of stages.
*/ */
while (pos > 0) { for (; pos > 0; --pos) {
const git_index_entry *prev = git_vector_get(&index->entries, pos-1); const git_index_entry *prev = git_vector_get(&index->entries, pos - 1);
if (index->entries_cmp_path(prev->path, path) != 0) if (index->entries_cmp_path(prev->path, path) != 0)
break; break;
--pos;
} }
if (at_pos) if (at_pos)
*at_pos = pos; *at_pos = pos;
git_mutex_unlock(&index->lock);
return 0; return 0;
} }
size_t git_index__prefix_position(git_index *index, const char *path)
{
struct entry_srch_key srch_key;
size_t pos;
srch_key.path = path;
srch_key.path_len = strlen(path);
srch_key.stage = 0;
git_vector_sort(&index->entries);
git_vector_bsearch2(
&pos, &index->entries, index->entries_search, &srch_key);
return pos;
}
int git_index_conflict_add(git_index *index, int git_index_conflict_add(git_index *index,
const git_index_entry *ancestor_entry, const git_index_entry *ancestor_entry,
const git_index_entry *our_entry, const git_index_entry *our_entry,
...@@ -1090,21 +1231,22 @@ int git_index_conflict_add(git_index *index, ...@@ -1090,21 +1231,22 @@ int git_index_conflict_add(git_index *index,
assert (index); assert (index);
if ((ancestor_entry != NULL && (entries[0] = index_entry_dup(ancestor_entry)) == NULL) || if ((ret = index_entry_dup(&entries[0], ancestor_entry)) < 0 ||
(our_entry != NULL && (entries[1] = index_entry_dup(our_entry)) == NULL) || (ret = index_entry_dup(&entries[1], our_entry)) < 0 ||
(their_entry != NULL && (entries[2] = index_entry_dup(their_entry)) == NULL)) (ret = index_entry_dup(&entries[2], their_entry)) < 0)
return -1; goto on_error;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
if (entries[i] == NULL) if (entries[i] == NULL)
continue; continue;
/* Make sure stage is correct */ /* Make sure stage is correct */
entries[i]->flags = (entries[i]->flags & ~GIT_IDXENTRY_STAGEMASK) | GIT_IDXENTRY_STAGE_SET(entries[i], i + 1);
((i+1) << GIT_IDXENTRY_STAGESHIFT);
if ((ret = index_insert(index, entries[i], 1)) < 0) if ((ret = index_insert(index, &entries[i], 1)) < 0)
goto on_error; goto on_error;
entries[i] = NULL; /* don't free if later entry fails */
} }
return 0; return 0;
...@@ -1194,23 +1336,24 @@ int git_index_conflict_get( ...@@ -1194,23 +1336,24 @@ int git_index_conflict_get(
return 0; return 0;
} }
int git_index_conflict_remove(git_index *index, const char *path) static int index_conflict_remove(git_index *index, const char *path)
{ {
size_t pos, posmax; size_t pos = 0;
git_index_entry *conflict_entry; git_index_entry *conflict_entry;
int error = 0; int error = 0;
assert(index && path); if (path != NULL && git_index_find(&pos, index, path) < 0)
if (git_index_find(&pos, index, path) < 0)
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
posmax = git_index_entrycount(index); if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Unable to lock index");
return -1;
}
while (pos < posmax) { while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) {
conflict_entry = git_vector_get(&index->entries, pos);
if (index->entries_cmp_path(conflict_entry->path, path) != 0) if (path != NULL &&
index->entries_cmp_path(conflict_entry->path, path) != 0)
break; break;
if (GIT_IDXENTRY_STAGE(conflict_entry) == 0) { if (GIT_IDXENTRY_STAGE(conflict_entry) == 0) {
...@@ -1218,32 +1361,25 @@ int git_index_conflict_remove(git_index *index, const char *path) ...@@ -1218,32 +1361,25 @@ int git_index_conflict_remove(git_index *index, const char *path)
continue; continue;
} }
if ((error = git_vector_remove(&index->entries, pos)) < 0) if ((error = index_remove_entry(index, pos)) < 0)
return error; break;
index_entry_free(conflict_entry);
posmax--;
} }
return 0; git_mutex_unlock(&index->lock);
return error;
} }
static int index_conflicts_match(const git_vector *v, size_t idx) int git_index_conflict_remove(git_index *index, const char *path)
{ {
git_index_entry *entry = git_vector_get(v, idx); assert(index && path);
return index_conflict_remove(index, path);
if (GIT_IDXENTRY_STAGE(entry) > 0) {
index_entry_free(entry);
return 1;
}
return 0;
} }
void git_index_conflict_cleanup(git_index *index) int git_index_conflict_cleanup(git_index *index)
{ {
assert(index); assert(index);
git_vector_remove_matching(&index->entries, index_conflicts_match); return index_conflict_remove(index, NULL);
} }
int git_index_has_conflicts(const git_index *index) int git_index_has_conflicts(const git_index *index)
...@@ -1338,32 +1474,36 @@ const git_index_name_entry *git_index_name_get_byindex( ...@@ -1338,32 +1474,36 @@ const git_index_name_entry *git_index_name_get_byindex(
return git_vector_get(&index->names, n); return git_vector_get(&index->names, n);
} }
static void index_name_entry_free(git_index_name_entry *ne)
{
if (!ne)
return;
git__free(ne->ancestor);
git__free(ne->ours);
git__free(ne->theirs);
git__free(ne);
}
int git_index_name_add(git_index *index, int git_index_name_add(git_index *index,
const char *ancestor, const char *ours, const char *theirs) const char *ancestor, const char *ours, const char *theirs)
{ {
git_index_name_entry *conflict_name; git_index_name_entry *conflict_name;
assert ((ancestor && ours) || (ancestor && theirs) || (ours && theirs)); assert((ancestor && ours) || (ancestor && theirs) || (ours && theirs));
conflict_name = git__calloc(1, sizeof(git_index_name_entry)); conflict_name = git__calloc(1, sizeof(git_index_name_entry));
GITERR_CHECK_ALLOC(conflict_name); GITERR_CHECK_ALLOC(conflict_name);
if (ancestor) { if ((ancestor && !(conflict_name->ancestor = git__strdup(ancestor))) ||
conflict_name->ancestor = git__strdup(ancestor); (ours && !(conflict_name->ours = git__strdup(ours))) ||
GITERR_CHECK_ALLOC(conflict_name->ancestor); (theirs && !(conflict_name->theirs = git__strdup(theirs))) ||
} git_vector_insert(&index->names, conflict_name) < 0)
{
if (ours) { index_name_entry_free(conflict_name);
conflict_name->ours = git__strdup(ours); return -1;
GITERR_CHECK_ALLOC(conflict_name->ours);
}
if (theirs) {
conflict_name->theirs = git__strdup(theirs);
GITERR_CHECK_ALLOC(conflict_name->theirs);
} }
return git_vector_insert(&index->names, conflict_name); return 0;
} }
void git_index_name_clear(git_index *index) void git_index_name_clear(git_index *index)
...@@ -1373,18 +1513,8 @@ void git_index_name_clear(git_index *index) ...@@ -1373,18 +1513,8 @@ void git_index_name_clear(git_index *index)
assert(index); assert(index);
git_vector_foreach(&index->names, i, conflict_name) { git_vector_foreach(&index->names, i, conflict_name)
if (conflict_name->ancestor) index_name_entry_free(conflict_name);
git__free(conflict_name->ancestor);
if (conflict_name->ours)
git__free(conflict_name->ours);
if (conflict_name->theirs)
git__free(conflict_name->theirs);
git__free(conflict_name);
}
git_vector_clear(&index->names); git_vector_clear(&index->names);
} }
...@@ -1412,7 +1542,6 @@ static int index_reuc_insert( ...@@ -1412,7 +1542,6 @@ static int index_reuc_insert(
return git_vector_insert(&index->reuc, reuc); return git_vector_insert(&index->reuc, reuc);
/* exists, replace it */ /* exists, replace it */
git__free((*existing)->path);
git__free(*existing); git__free(*existing);
*existing = reuc; *existing = reuc;
...@@ -1429,15 +1558,13 @@ int git_index_reuc_add(git_index *index, const char *path, ...@@ -1429,15 +1558,13 @@ int git_index_reuc_add(git_index *index, const char *path,
assert(index && path); assert(index && path);
if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode,
ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 ||
(error = index_reuc_insert(index, reuc, 1)) < 0) (error = index_reuc_insert(index, reuc, 1)) < 0)
{
index_entry_reuc_free(reuc); index_entry_reuc_free(reuc);
return error;
}
return error; return error;
} }
int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path) int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path)
{ {
...@@ -1522,13 +1649,9 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) ...@@ -1522,13 +1649,9 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
if (size <= len) if (size <= len)
return index_error_invalid("reading reuc entries"); return index_error_invalid("reading reuc entries");
lost = git__calloc(1, sizeof(git_index_reuc_entry)); lost = reuc_entry_alloc(buffer);
GITERR_CHECK_ALLOC(lost); GITERR_CHECK_ALLOC(lost);
/* read NUL-terminated pathname for entry */
lost->path = git__strdup(buffer);
GITERR_CHECK_ALLOC(lost->path);
size -= len; size -= len;
buffer += len; buffer += len;
...@@ -1626,41 +1749,41 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size ...@@ -1626,41 +1749,41 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
return 0; return 0;
} }
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size) static size_t read_entry(
git_index_entry **out, const void *buffer, size_t buffer_size)
{ {
size_t path_length, entry_size; size_t path_length, entry_size;
uint16_t flags_raw; uint16_t flags_raw;
const char *path_ptr; const char *path_ptr;
const struct entry_short *source = buffer; const struct entry_short *source = buffer;
git_index_entry entry = {{0}};
if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size) if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
return 0; return 0;
memset(dest, 0x0, sizeof(git_index_entry)); entry.ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
entry.ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds); entry.mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds); entry.mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds); entry.dev = ntohl(source->dev);
dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds); entry.ino = ntohl(source->ino);
dest->dev = ntohl(source->dev); entry.mode = ntohl(source->mode);
dest->ino = ntohl(source->ino); entry.uid = ntohl(source->uid);
dest->mode = ntohl(source->mode); entry.gid = ntohl(source->gid);
dest->uid = ntohl(source->uid); entry.file_size = ntohl(source->file_size);
dest->gid = ntohl(source->gid); git_oid_cpy(&entry.id, &source->oid);
dest->file_size = ntohl(source->file_size); entry.flags = ntohs(source->flags);
git_oid_cpy(&dest->id, &source->oid);
dest->flags = ntohs(source->flags); if (entry.flags & GIT_IDXENTRY_EXTENDED) {
if (dest->flags & GIT_IDXENTRY_EXTENDED) {
const struct entry_long *source_l = (const struct entry_long *)source; const struct entry_long *source_l = (const struct entry_long *)source;
path_ptr = source_l->path; path_ptr = source_l->path;
flags_raw = ntohs(source_l->flags_extended); flags_raw = ntohs(source_l->flags_extended);
memcpy(&dest->flags_extended, &flags_raw, 2); memcpy(&entry.flags_extended, &flags_raw, 2);
} else } else
path_ptr = source->path; path_ptr = source->path;
path_length = dest->flags & GIT_IDXENTRY_NAMEMASK; path_length = entry.flags & GIT_IDXENTRY_NAMEMASK;
/* if this is a very long string, we must find its /* if this is a very long string, we must find its
* real length without overflowing */ * real length without overflowing */
...@@ -1674,7 +1797,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe ...@@ -1674,7 +1797,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
path_length = path_end - path_ptr; path_length = path_end - path_ptr;
} }
if (dest->flags & GIT_IDXENTRY_EXTENDED) if (entry.flags & GIT_IDXENTRY_EXTENDED)
entry_size = long_entry_size(path_length); entry_size = long_entry_size(path_length);
else else
entry_size = short_entry_size(path_length); entry_size = short_entry_size(path_length);
...@@ -1682,8 +1805,10 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe ...@@ -1682,8 +1805,10 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
if (INDEX_FOOTER_SIZE + entry_size > buffer_size) if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
return 0; return 0;
dest->path = git__strdup(path_ptr); entry.path = (char *)path_ptr;
assert(dest->path);
if (index_entry_dup(out, &entry) < 0)
return 0;
return entry_size; return entry_size;
} }
...@@ -1749,6 +1874,7 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer ...@@ -1749,6 +1874,7 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
static int parse_index(git_index *index, const char *buffer, size_t buffer_size) static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
{ {
int error = 0;
unsigned int i; unsigned int i;
struct index_header header = { 0 }; struct index_header header = { 0 };
git_oid checksum_calculated, checksum_expected; git_oid checksum_calculated, checksum_expected;
...@@ -1768,35 +1894,41 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) ...@@ -1768,35 +1894,41 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE); git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE);
/* Parse header */ /* Parse header */
if (read_header(&header, buffer) < 0) if ((error = read_header(&header, buffer)) < 0)
return -1; return error;
seek_forward(INDEX_HEADER_SIZE); seek_forward(INDEX_HEADER_SIZE);
git_vector_clear(&index->entries); if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire index lock");
return -1;
}
assert(!index->entries.length);
/* Parse all the entries */ /* Parse all the entries */
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
size_t entry_size;
git_index_entry *entry; git_index_entry *entry;
size_t entry_size = read_entry(&entry, buffer, buffer_size);
entry = git__malloc(sizeof(git_index_entry));
GITERR_CHECK_ALLOC(entry);
entry_size = read_entry(entry, buffer, buffer_size);
/* 0 bytes read means an object corruption */ /* 0 bytes read means an object corruption */
if (entry_size == 0) if (entry_size == 0) {
return index_error_invalid("invalid entry"); error = index_error_invalid("invalid entry");
goto done;
}
if (git_vector_insert(&index->entries, entry) < 0) if ((error = git_vector_insert(&index->entries, entry)) < 0) {
return -1; index_entry_free(entry);
goto done;
}
seek_forward(entry_size); seek_forward(entry_size);
} }
if (i != header.entry_count) if (i != header.entry_count) {
return index_error_invalid("header entries changed while parsing"); error = index_error_invalid("header entries changed while parsing");
goto done;
}
/* There's still space for some extensions! */ /* There's still space for some extensions! */
while (buffer_size > INDEX_FOOTER_SIZE) { while (buffer_size > INDEX_FOOTER_SIZE) {
...@@ -1805,20 +1937,28 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) ...@@ -1805,20 +1937,28 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
extension_size = read_extension(index, buffer, buffer_size); extension_size = read_extension(index, buffer, buffer_size);
/* see if we have read any bytes from the extension */ /* see if we have read any bytes from the extension */
if (extension_size == 0) if (extension_size == 0) {
return index_error_invalid("extension is truncated"); error = index_error_invalid("extension is truncated");
goto done;
}
seek_forward(extension_size); seek_forward(extension_size);
} }
if (buffer_size != INDEX_FOOTER_SIZE) if (buffer_size != INDEX_FOOTER_SIZE) {
return index_error_invalid("buffer size does not match index footer size"); error = index_error_invalid(
"buffer size does not match index footer size");
goto done;
}
/* 160-bit SHA-1 over the content of the index file before this checksum. */ /* 160-bit SHA-1 over the content of the index file before this checksum. */
git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer); git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);
if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) {
return index_error_invalid("calculated checksum does not match expected"); error = index_error_invalid(
"calculated checksum does not match expected");
goto done;
}
#undef seek_forward #undef seek_forward
...@@ -1826,9 +1966,11 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) ...@@ -1826,9 +1966,11 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
* in-memory index is supposed to be case-insensitive * in-memory index is supposed to be case-insensitive
*/ */
git_vector_set_sorted(&index->entries, !index->ignore_case); git_vector_set_sorted(&index->entries, !index->ignore_case);
git_vector_sort(&index->entries); error = index_sort_if_needed(index, false);
return 0; done:
git_mutex_unlock(&index->lock);
return error;
} }
static bool is_index_extended(git_index *index) static bool is_index_extended(git_index *index)
...@@ -1856,7 +1998,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry) ...@@ -1856,7 +1998,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
size_t path_len, disk_size; size_t path_len, disk_size;
char *path; char *path;
path_len = strlen(entry->path); path_len = ((struct entry_internal *)entry)->pathlen;
if (entry->flags & GIT_IDXENTRY_EXTENDED) if (entry->flags & GIT_IDXENTRY_EXTENDED)
disk_size = long_entry_size(path_len); disk_size = long_entry_size(path_len);
...@@ -1913,22 +2055,30 @@ static int write_entries(git_index *index, git_filebuf *file) ...@@ -1913,22 +2055,30 @@ static int write_entries(git_index *index, git_filebuf *file)
{ {
int error = 0; int error = 0;
size_t i; size_t i;
git_vector case_sorted; git_vector case_sorted, *entries;
git_index_entry *entry; git_index_entry *entry;
git_vector *out = &index->entries;
if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock index");
return -1;
}
/* If index->entries is sorted case-insensitively, then we need /* If index->entries is sorted case-insensitively, then we need
* to re-sort it case-sensitively before writing */ * to re-sort it case-sensitively before writing */
if (index->ignore_case) { if (index->ignore_case) {
git_vector_dup(&case_sorted, &index->entries, index_cmp); git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp);
git_vector_sort(&case_sorted); git_vector_sort(&case_sorted);
out = &case_sorted; entries = &case_sorted;
} else {
entries = &index->entries;
} }
git_vector_foreach(out, i, entry) git_vector_foreach(entries, i, entry)
if ((error = write_disk_entry(file, entry)) < 0) if ((error = write_disk_entry(file, entry)) < 0)
break; break;
git_mutex_unlock(&index->lock);
if (index->ignore_case) if (index->ignore_case)
git_vector_free(&case_sorted); git_vector_free(&case_sorted);
...@@ -2100,7 +2250,7 @@ int git_index_entry_stage(const git_index_entry *entry) ...@@ -2100,7 +2250,7 @@ int git_index_entry_stage(const git_index_entry *entry)
typedef struct read_tree_data { typedef struct read_tree_data {
git_vector *old_entries; git_vector *old_entries;
git_vector *new_entries; git_vector *new_entries;
git_vector_cmp entries_search; git_vector_cmp entry_cmp;
} read_tree_data; } read_tree_data;
static int read_tree_cb( static int read_tree_cb(
...@@ -2109,6 +2259,7 @@ static int read_tree_cb( ...@@ -2109,6 +2259,7 @@ static int read_tree_cb(
read_tree_data *data = payload; read_tree_data *data = payload;
git_index_entry *entry = NULL, *old_entry; git_index_entry *entry = NULL, *old_entry;
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
size_t pos;
if (git_tree_entry__is_tree(tentry)) if (git_tree_entry__is_tree(tentry))
return 0; return 0;
...@@ -2116,30 +2267,24 @@ static int read_tree_cb( ...@@ -2116,30 +2267,24 @@ static int read_tree_cb(
if (git_buf_joinpath(&path, root, tentry->filename) < 0) if (git_buf_joinpath(&path, root, tentry->filename) < 0)
return -1; return -1;
entry = git__calloc(1, sizeof(git_index_entry)); entry = index_entry_alloc(path.ptr);
GITERR_CHECK_ALLOC(entry); GITERR_CHECK_ALLOC(entry);
entry->mode = tentry->attr; entry->mode = tentry->attr;
entry->id = tentry->oid; entry->id = tentry->oid;
/* look for corresponding old entry and copy data to new entry */ /* look for corresponding old entry and copy data to new entry */
if (data->old_entries) { if (data->old_entries != NULL &&
size_t pos; !index_find_in_entries(
struct entry_srch_key skey; &pos, data->old_entries, data->entry_cmp, path.ptr, 0, 0) &&
(old_entry = git_vector_get(data->old_entries, pos)) != NULL &&
skey.path = path.ptr; entry->mode == old_entry->mode &&
skey.path_len = strlen(path.ptr); git_oid_equal(&entry->id, &old_entry->id))
skey.stage = 0; {
char *oldpath = entry->path;
if (!git_vector_bsearch2( memcpy(entry, old_entry, sizeof(*entry));
&pos, data->old_entries, data->entries_search, &skey) && entry->path = oldpath;
(old_entry = git_vector_get(data->old_entries, pos)) != NULL && entry->flags_extended = 0;
entry->mode == old_entry->mode &&
git_oid_equal(&entry->id, &old_entry->id))
{
memcpy(entry, old_entry, sizeof(*entry));
entry->flags_extended = 0;
}
} }
if (path.size < GIT_IDXENTRY_NAMEMASK) if (path.size < GIT_IDXENTRY_NAMEMASK)
...@@ -2147,7 +2292,6 @@ static int read_tree_cb( ...@@ -2147,7 +2292,6 @@ static int read_tree_cb(
else else
entry->flags = GIT_IDXENTRY_NAMEMASK; entry->flags = GIT_IDXENTRY_NAMEMASK;
entry->path = git_buf_detach(&path);
git_buf_free(&path); git_buf_free(&path);
if (git_vector_insert(data->new_entries, entry) < 0) { if (git_vector_insert(data->new_entries, entry) < 0) {
...@@ -2168,16 +2312,25 @@ int git_index_read_tree(git_index *index, const git_tree *tree) ...@@ -2168,16 +2312,25 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
data.old_entries = &index->entries; data.old_entries = &index->entries;
data.new_entries = &entries; data.new_entries = &entries;
data.entries_search = index->entries_search; data.entry_cmp = index->entries_search;
git_vector_sort(&index->entries); if (index_sort_if_needed(index, true) < 0)
return -1;
error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data);
if (!error) { if (!error) {
git_vector_sort(&entries); git_vector_sort(&entries);
git_index_clear(index);
git_vector_swap(&entries, &index->entries); if ((error = git_index_clear(index)) < 0)
/* well, this isn't good */;
else if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire index lock");
error = -1;
} else {
git_vector_swap(&entries, &index->entries);
git_mutex_unlock(&index->lock);
}
} }
git_vector_free(&entries); git_vector_free(&entries);
...@@ -2247,7 +2400,7 @@ int git_index_add_all( ...@@ -2247,7 +2400,7 @@ int git_index_add_all(
/* skip ignored items that are not already in the index */ /* skip ignored items that are not already in the index */
if ((flags & GIT_INDEX_ADD_FORCE) == 0 && if ((flags & GIT_INDEX_ADD_FORCE) == 0 &&
git_iterator_current_is_ignored(wditer) && git_iterator_current_is_ignored(wditer) &&
git_index__find(&existing, index, wd->path, 0, 0) < 0) index_find(&existing, index, wd->path, 0, 0, true) < 0)
continue; continue;
/* issue notification callback if requested */ /* issue notification callback if requested */
...@@ -2269,17 +2422,14 @@ int git_index_add_all( ...@@ -2269,17 +2422,14 @@ int git_index_add_all(
break; break;
/* make the new entry to insert */ /* make the new entry to insert */
if ((entry = index_entry_dup(wd)) == NULL) { if ((error = index_entry_dup(&entry, wd)) < 0)
error = -1;
break; break;
}
entry->id = blobid; entry->id = blobid;
/* add working directory item to index */ /* add working directory item to index */
if ((error = index_insert(index, entry, 1)) < 0) { if ((error = index_insert(index, &entry, 1)) < 0)
index_entry_free(entry);
break; break;
}
git_tree_cache_invalidate_path(index->tree, wd->path); git_tree_cache_invalidate_path(index->tree, wd->path);
...@@ -2411,3 +2561,48 @@ int git_index_update_all( ...@@ -2411,3 +2561,48 @@ int git_index_update_all(
return error; return error;
} }
int git_index_snapshot_new(git_vector *snap, git_index *index)
{
int error;
GIT_REFCOUNT_INC(index);
if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock index");
return -1;
}
git_atomic_inc(&index->readers);
git_vector_sort(&index->entries);
error = git_vector_dup(snap, &index->entries, index->entries._cmp);
git_mutex_unlock(&index->lock);
if (error < 0)
git_index_free(index);
return error;
}
void git_index_snapshot_release(git_vector *snap, git_index *index)
{
git_vector_free(snap);
git_atomic_dec(&index->readers);
if (!git_mutex_lock(&index->lock)) {
index_free_deleted(index); /* try to free pending deleted items */
git_mutex_unlock(&index->lock);
}
git_index_free(index);
}
int git_index_snapshot_find(
size_t *out, git_vector *entries, git_vector_cmp entry_srch,
const char *path, size_t path_len, int stage)
{
return index_find_in_entries(out, entries, entry_srch, path, path_len, stage);
}
...@@ -21,12 +21,15 @@ struct git_index { ...@@ -21,12 +21,15 @@ struct git_index {
git_refcount rc; git_refcount rc;
char *index_file_path; char *index_file_path;
git_futils_filestamp stamp; git_futils_filestamp stamp;
git_vector entries; git_vector entries;
unsigned int on_disk:1; git_mutex lock; /* lock held while entries is being changed */
git_vector deleted; /* deleted entries if readers > 0 */
git_atomic readers; /* number of active iterators */
unsigned int on_disk:1;
unsigned int ignore_case:1; unsigned int ignore_case:1;
unsigned int distrust_filemode:1; unsigned int distrust_filemode:1;
unsigned int no_symlinks:1; unsigned int no_symlinks:1;
...@@ -50,12 +53,21 @@ struct git_index_conflict_iterator { ...@@ -50,12 +53,21 @@ struct git_index_conflict_iterator {
extern void git_index_entry__init_from_stat( extern void git_index_entry__init_from_stat(
git_index_entry *entry, struct stat *st, bool trust_mode); git_index_entry *entry, struct stat *st, bool trust_mode);
extern size_t git_index__prefix_position(git_index *index, const char *path); /* Index entry comparison functions for array sorting */
extern int git_index_entry_cmp(const void *a, const void *b);
extern int git_index_entry_icmp(const void *a, const void *b);
extern int git_index_entry__cmp(const void *a, const void *b); /* Index entry search functions for search using a search spec */
extern int git_index_entry__cmp_icase(const void *a, const void *b); extern int git_index_entry_srch(const void *a, const void *b);
extern int git_index_entry_isrch(const void *a, const void *b);
extern int git_index__find( /* Search index for `path`, returning GIT_ENOTFOUND if it does not exist
* (but not setting an error message).
*
* `at_pos` is set to the position where it is or would be inserted.
* Pass `path_len` as strlen of path or 0 to call strlen internally.
*/
extern int git_index__find_pos(
size_t *at_pos, git_index *index, const char *path, size_t path_len, int stage); size_t *at_pos, git_index *index, const char *path, size_t path_len, int stage);
extern void git_index__set_ignore_case(git_index *index, bool ignore_case); extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
...@@ -69,4 +81,16 @@ GIT_INLINE(const git_futils_filestamp *) git_index__filestamp(git_index *index) ...@@ -69,4 +81,16 @@ GIT_INLINE(const git_futils_filestamp *) git_index__filestamp(git_index *index)
extern int git_index__changed_relative_to(git_index *index, const git_futils_filestamp *fs); extern int git_index__changed_relative_to(git_index *index, const git_futils_filestamp *fs);
/* Copy the current entries vector *and* increment the index refcount.
* Call `git_index__release_snapshot` when done.
*/
extern int git_index_snapshot_new(git_vector *snap, git_index *index);
extern void git_index_snapshot_release(git_vector *snap, git_index *index);
/* Allow searching in a snapshot; entries must already be sorted! */
extern int git_index_snapshot_find(
size_t *at_pos, git_vector *snap, git_vector_cmp entry_srch,
const char *path, size_t path_len, int stage);
#endif #endif
...@@ -644,6 +644,8 @@ typedef struct { ...@@ -644,6 +644,8 @@ typedef struct {
git_iterator base; git_iterator base;
git_iterator_callbacks cb; git_iterator_callbacks cb;
git_index *index; git_index *index;
git_vector entries;
git_vector_cmp entry_srch;
size_t current; size_t current;
/* when not in autoexpand mode, use these to represent "tree" state */ /* when not in autoexpand mode, use these to represent "tree" state */
git_buf partial; git_buf partial;
...@@ -654,10 +656,10 @@ typedef struct { ...@@ -654,10 +656,10 @@ typedef struct {
static const git_index_entry *index_iterator__index_entry(index_iterator *ii) static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
{ {
const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
if (ie != NULL && iterator__past_end(ii, ie->path)) { if (ie != NULL && iterator__past_end(ii, ie->path)) {
ii->current = git_index_entrycount(ii->index); ii->current = git_vector_length(&ii->entries);
ie = NULL; ie = NULL;
} }
...@@ -726,7 +728,7 @@ static int index_iterator__current( ...@@ -726,7 +728,7 @@ static int index_iterator__current(
const git_index_entry **entry, git_iterator *self) const git_index_entry **entry, git_iterator *self)
{ {
index_iterator *ii = (index_iterator *)self; index_iterator *ii = (index_iterator *)self;
const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
if (ie != NULL && index_iterator__at_tree(ii)) { if (ie != NULL && index_iterator__at_tree(ii)) {
ii->tree_entry.path = ii->partial.ptr; ii->tree_entry.path = ii->partial.ptr;
...@@ -744,14 +746,14 @@ static int index_iterator__current( ...@@ -744,14 +746,14 @@ static int index_iterator__current(
static int index_iterator__at_end(git_iterator *self) static int index_iterator__at_end(git_iterator *self)
{ {
index_iterator *ii = (index_iterator *)self; index_iterator *ii = (index_iterator *)self;
return (ii->current >= git_index_entrycount(ii->index)); return (ii->current >= git_vector_length(&ii->entries));
} }
static int index_iterator__advance( static int index_iterator__advance(
const git_index_entry **entry, git_iterator *self) const git_index_entry **entry, git_iterator *self)
{ {
index_iterator *ii = (index_iterator *)self; index_iterator *ii = (index_iterator *)self;
size_t entrycount = git_index_entrycount(ii->index); size_t entrycount = git_vector_length(&ii->entries);
const git_index_entry *ie; const git_index_entry *ie;
if (!iterator__has_been_accessed(ii)) if (!iterator__has_been_accessed(ii))
...@@ -766,7 +768,7 @@ static int index_iterator__advance( ...@@ -766,7 +768,7 @@ static int index_iterator__advance(
while (ii->current < entrycount) { while (ii->current < entrycount) {
ii->current++; ii->current++;
if (!(ie = git_index_get_byindex(ii->index, ii->current)) || if (!(ie = git_vector_get(&ii->entries, ii->current)) ||
ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0) ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
break; break;
} }
...@@ -789,7 +791,7 @@ static int index_iterator__advance_into( ...@@ -789,7 +791,7 @@ static int index_iterator__advance_into(
const git_index_entry **entry, git_iterator *self) const git_index_entry **entry, git_iterator *self)
{ {
index_iterator *ii = (index_iterator *)self; index_iterator *ii = (index_iterator *)self;
const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
if (ie != NULL && index_iterator__at_tree(ii)) { if (ie != NULL && index_iterator__at_tree(ii)) {
if (ii->restore_terminator) if (ii->restore_terminator)
...@@ -815,8 +817,11 @@ static int index_iterator__reset( ...@@ -815,8 +817,11 @@ static int index_iterator__reset(
if (iterator__reset_range(self, start, end) < 0) if (iterator__reset_range(self, start, end) < 0)
return -1; return -1;
ii->current = ii->base.start ? ii->current = 0;
git_index__prefix_position(ii->index, ii->base.start) : 0;
if (ii->base.start)
git_index_snapshot_find(
&ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0);
if ((ie = index_iterator__skip_conflicts(ii)) == NULL) if ((ie = index_iterator__skip_conflicts(ii)) == NULL)
return 0; return 0;
...@@ -841,9 +846,8 @@ static int index_iterator__reset( ...@@ -841,9 +846,8 @@ static int index_iterator__reset(
static void index_iterator__free(git_iterator *self) static void index_iterator__free(git_iterator *self)
{ {
index_iterator *ii = (index_iterator *)self; index_iterator *ii = (index_iterator *)self;
git_index_free(ii->index); git_index_snapshot_release(&ii->entries, ii->index);
ii->index = NULL; ii->index = NULL;
git_buf_free(&ii->partial); git_buf_free(&ii->partial);
} }
...@@ -854,18 +858,29 @@ int git_iterator_for_index( ...@@ -854,18 +858,29 @@ int git_iterator_for_index(
const char *start, const char *start,
const char *end) const char *end)
{ {
int error = 0;
index_iterator *ii = git__calloc(1, sizeof(index_iterator)); index_iterator *ii = git__calloc(1, sizeof(index_iterator));
GITERR_CHECK_ALLOC(ii); GITERR_CHECK_ALLOC(ii);
if ((error = git_index_snapshot_new(&ii->entries, index)) < 0) {
git__free(ii);
return error;
}
ii->index = index;
ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index)); ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
if (index->ignore_case) { if ((error = iterator__update_ignore_case((git_iterator *)ii, flags)) < 0) {
ii->base.flags |= GIT_ITERATOR_IGNORE_CASE; git_iterator_free((git_iterator *)ii);
ii->base.prefixcomp = git__prefixcmp_icase; return error;
} }
ii->index = index; ii->entry_srch = iterator__ignore_case(ii) ?
GIT_REFCOUNT_INC(index); git_index_entry_isrch : git_index_entry_srch;
git_vector_set_cmp(&ii->entries, iterator__ignore_case(ii) ?
git_index_entry_icmp : git_index_entry_cmp);
git_vector_sort(&ii->entries);
git_buf_init(&ii->partial, 0); git_buf_init(&ii->partial, 0);
ii->tree_entry.mode = GIT_FILEMODE_TREE; ii->tree_entry.mode = GIT_FILEMODE_TREE;
...@@ -873,7 +888,6 @@ int git_iterator_for_index( ...@@ -873,7 +888,6 @@ int git_iterator_for_index(
index_iterator__reset((git_iterator *)ii, NULL, NULL); index_iterator__reset((git_iterator *)ii, NULL, NULL);
*iter = (git_iterator *)ii; *iter = (git_iterator *)ii;
return 0; return 0;
} }
......
...@@ -995,10 +995,12 @@ static void merge_diff_list_coalesce_renames( ...@@ -995,10 +995,12 @@ static void merge_diff_list_coalesce_renames(
} }
} }
static int merge_diff_empty(const git_vector *conflicts, size_t idx) static int merge_diff_empty(const git_vector *conflicts, size_t idx, void *p)
{ {
git_merge_diff *conflict = conflicts->contents[idx]; git_merge_diff *conflict = conflicts->contents[idx];
GIT_UNUSED(p);
return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) && return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) &&
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) &&
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)); !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry));
...@@ -1079,7 +1081,7 @@ int git_merge_diff_list__find_renames( ...@@ -1079,7 +1081,7 @@ int git_merge_diff_list__find_renames(
merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts); merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts);
/* And remove any entries that were merged and are now empty. */ /* And remove any entries that were merged and are now empty. */
git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty); git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty, NULL);
done: done:
if (cache != NULL) { if (cache != NULL) {
...@@ -1228,7 +1230,7 @@ done: ...@@ -1228,7 +1230,7 @@ done:
return error; return error;
} }
GIT_INLINE(int) index_entry_dup( GIT_INLINE(int) index_entry_dup_pool(
git_index_entry *out, git_index_entry *out,
git_pool *pool, git_pool *pool,
const git_index_entry *src) const git_index_entry *src)
...@@ -1274,9 +1276,9 @@ static git_merge_diff *merge_diff_from_index_entries( ...@@ -1274,9 +1276,9 @@ static git_merge_diff *merge_diff_from_index_entries(
if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL) if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL)
return NULL; return NULL;
if (index_entry_dup(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 ||
index_entry_dup(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 || index_entry_dup_pool(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 ||
index_entry_dup(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0) index_entry_dup_pool(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0)
return NULL; return NULL;
conflict->our_status = merge_delta_type_from_index_entries( conflict->our_status = merge_delta_type_from_index_entries(
...@@ -1316,7 +1318,7 @@ static int merge_diff_list_insert_unmodified( ...@@ -1316,7 +1318,7 @@ static int merge_diff_list_insert_unmodified(
entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry)); entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry));
GITERR_CHECK_ALLOC(entry); GITERR_CHECK_ALLOC(entry);
if ((error = index_entry_dup(entry, &diff_list->pool, tree_items[0])) >= 0) if ((error = index_entry_dup_pool(entry, &diff_list->pool, tree_items[0])) >= 0)
error = git_vector_insert(&diff_list->staged, entry); error = git_vector_insert(&diff_list->staged, entry);
return error; return error;
...@@ -1330,7 +1332,7 @@ int git_merge_diff_list__find_differences( ...@@ -1330,7 +1332,7 @@ int git_merge_diff_list__find_differences(
{ {
git_iterator *iterators[3] = {0}; git_iterator *iterators[3] = {0};
const git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3]; const git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3];
git_vector_cmp entry_compare = git_index_entry__cmp; git_vector_cmp entry_compare = git_index_entry_cmp;
struct merge_diff_df_data df_data = {0}; struct merge_diff_df_data df_data = {0};
int cur_item_modified; int cur_item_modified;
size_t i, j; size_t i, j;
......
...@@ -445,7 +445,7 @@ static int pathspec_match_from_iterator( ...@@ -445,7 +445,7 @@ static int pathspec_match_from_iterator(
/* check if path is ignored and untracked */ /* check if path is ignored and untracked */
if (index != NULL && if (index != NULL &&
git_iterator_current_is_ignored(iter) && git_iterator_current_is_ignored(iter) &&
git_index__find(NULL, index, entry->path, 0, GIT_INDEX_STAGE_ANY) < 0) git_index__find_pos(NULL, index, entry->path, 0, GIT_INDEX_STAGE_ANY) < 0)
continue; continue;
/* mark the matched pattern as used */ /* mark the matched pattern as used */
......
...@@ -108,7 +108,7 @@ struct git_repository { ...@@ -108,7 +108,7 @@ struct git_repository {
git_submodule_cache *_submodules; git_submodule_cache *_submodules;
git_cache objects; git_cache objects;
git_attr_cache attrcache; git_attr_cache *attrcache;
git_diff_driver_registry *diff_drivers; git_diff_driver_registry *diff_drivers;
char *path_repository; char *path_repository;
...@@ -123,7 +123,7 @@ struct git_repository { ...@@ -123,7 +123,7 @@ struct git_repository {
GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo) GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
{ {
return &repo->attrcache; return repo->attrcache;
} }
int git_repository_head_tree(git_tree **tree, git_repository *repo); int git_repository_head_tree(git_tree **tree, git_repository *repo);
......
...@@ -20,7 +20,7 @@ int git_sortedcache_new( ...@@ -20,7 +20,7 @@ int git_sortedcache_new(
if (git_pool_init(&sc->pool, 1, 0) < 0 || if (git_pool_init(&sc->pool, 1, 0) < 0 ||
git_vector_init(&sc->items, 4, item_cmp) < 0 || git_vector_init(&sc->items, 4, item_cmp) < 0 ||
(sc->map = git_strmap_alloc()) == NULL) git_strmap_alloc(&sc->map) < 0)
goto fail; goto fail;
if (git_rwlock_init(&sc->lock)) { if (git_rwlock_init(&sc->lock)) {
...@@ -39,8 +39,7 @@ int git_sortedcache_new( ...@@ -39,8 +39,7 @@ int git_sortedcache_new(
return 0; return 0;
fail: fail:
if (sc->map) git_strmap_free(sc->map);
git_strmap_free(sc->map);
git_vector_free(&sc->items); git_vector_free(&sc->items);
git_pool_clear(&sc->pool); git_pool_clear(&sc->pool);
git__free(sc); git__free(sc);
...@@ -233,9 +232,8 @@ unlock: ...@@ -233,9 +232,8 @@ unlock:
void git_sortedcache_updated(git_sortedcache *sc) void git_sortedcache_updated(git_sortedcache *sc)
{ {
/* update filestamp to latest value */ /* update filestamp to latest value */
if (git_futils_filestamp_check(&sc->stamp, sc->path) < 0) git_futils_filestamp_check(&sc->stamp, sc->path);
giterr_clear();
} }
/* release all items in sorted cache */ /* release all items in sorted cache */
......
...@@ -22,7 +22,9 @@ typedef khiter_t git_strmap_iter; ...@@ -22,7 +22,9 @@ typedef khiter_t git_strmap_iter;
#define GIT__USE_STRMAP \ #define GIT__USE_STRMAP \
__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) __KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
#define git_strmap_alloc() kh_init(str) #define git_strmap_alloc(hp) \
((*(hp) = kh_init(str)) == NULL) ? giterr_set_oom(), -1 : 0
#define git_strmap_free(h) kh_destroy(str, h), h = NULL #define git_strmap_free(h) kh_destroy(str, h), h = NULL
#define git_strmap_clear(h) kh_clear(str, h) #define git_strmap_clear(h) kh_clear(str, h)
......
...@@ -1638,8 +1638,7 @@ static int submodule_cache_alloc( ...@@ -1638,8 +1638,7 @@ static int submodule_cache_alloc(
return -1; return -1;
} }
cache->submodules = git_strmap_alloc(); if (git_strmap_alloc(&cache->submodules) < 0) {
if (!cache->submodules) {
submodule_cache_free(cache); submodule_cache_free(cache);
return -1; return -1;
} }
...@@ -1694,8 +1693,6 @@ static int submodule_cache_refresh(git_submodule_cache *cache, int refresh) ...@@ -1694,8 +1693,6 @@ static int submodule_cache_refresh(git_submodule_cache *cache, int refresh)
update_gitmod = (wd != NULL) ? update_gitmod = (wd != NULL) ?
git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr) : git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr) :
(cache->gitmodules_stamp.mtime != 0); (cache->gitmodules_stamp.mtime != 0);
if (update_gitmod < 0)
giterr_clear();
} }
/* clear submodule flags that are to be refreshed */ /* clear submodule flags that are to be refreshed */
......
...@@ -47,6 +47,12 @@ typedef git_atomic git_atomic_ssize; ...@@ -47,6 +47,12 @@ typedef git_atomic git_atomic_ssize;
#define git_thread_exit(status) pthread_exit(status) #define git_thread_exit(status) pthread_exit(status)
#define git_thread_join(id, status) pthread_join(id, status) #define git_thread_join(id, status) pthread_join(id, status)
#if defined(GIT_WIN32)
#define git_thread_yield() Sleep(0)
#else
#define git_thread_yield() sched_yield()
#endif
/* Pthreads Mutex */ /* Pthreads Mutex */
#define git_mutex pthread_mutex_t #define git_mutex pthread_mutex_t
#define git_mutex_init(a) pthread_mutex_init(a, NULL) #define git_mutex_init(a) pthread_mutex_init(a, NULL)
...@@ -176,6 +182,7 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) ...@@ -176,6 +182,7 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#define git_thread_kill(thread) (void)0 #define git_thread_kill(thread) (void)0
#define git_thread_exit(status) (void)0 #define git_thread_exit(status) (void)0
#define git_thread_join(id, status) (void)0 #define git_thread_join(id, status) (void)0
#define git_thread_yield() (void)0
/* Pthreads Mutex */ /* Pthreads Mutex */
#define git_mutex unsigned int #define git_mutex unsigned int
......
...@@ -7,23 +7,16 @@ ...@@ -7,23 +7,16 @@
#include "tree-cache.h" #include "tree-cache.h"
static git_tree_cache *find_child(const git_tree_cache *tree, const char *path) static git_tree_cache *find_child(
const git_tree_cache *tree, const char *path, const char *end)
{ {
size_t i, dirlen; size_t i, dirlen = end ? (size_t)(end - path) : strlen(path);
const char *end;
end = strchr(path, '/');
if (end == NULL) {
end = strrchr(path, '\0');
}
dirlen = end - path;
for (i = 0; i < tree->children_count; ++i) { for (i = 0; i < tree->children_count; ++i) {
const char *childname = tree->children[i]->name; git_tree_cache *child = tree->children[i];
if (strlen(childname) == dirlen && !memcmp(path, childname, dirlen)) if (child->namelen == dirlen && !memcmp(path, child->name, dirlen))
return tree->children[i]; return child;
} }
return NULL; return NULL;
...@@ -44,7 +37,7 @@ void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path) ...@@ -44,7 +37,7 @@ void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path)
if (end == NULL) /* End of path */ if (end == NULL) /* End of path */
break; break;
tree = find_child(tree, ptr); tree = find_child(tree, ptr, end);
if (tree == NULL) /* We don't have that tree */ if (tree == NULL) /* We don't have that tree */
return; return;
...@@ -64,10 +57,9 @@ const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char ...@@ -64,10 +57,9 @@ const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char
while (1) { while (1) {
end = strchr(ptr, '/'); end = strchr(ptr, '/');
tree = find_child(tree, ptr); tree = find_child(tree, ptr, end);
if (tree == NULL) { /* Can't find it */ if (tree == NULL) /* Can't find it */
return NULL; return NULL;
}
if (end == NULL || *end + 1 == '\0') if (end == NULL || *end + 1 == '\0')
return tree; return tree;
...@@ -100,6 +92,7 @@ static int read_tree_internal(git_tree_cache **out, ...@@ -100,6 +92,7 @@ static int read_tree_internal(git_tree_cache **out,
tree->parent = parent; tree->parent = parent;
/* NUL-terminated tree name */ /* NUL-terminated tree name */
tree->namelen = name_len;
memcpy(tree->name, name_start, name_len); memcpy(tree->name, name_start, name_len);
tree->name[name_len] = '\0'; tree->name[name_len] = '\0';
......
...@@ -18,6 +18,7 @@ struct git_tree_cache { ...@@ -18,6 +18,7 @@ struct git_tree_cache {
ssize_t entries; ssize_t entries;
git_oid oid; git_oid oid;
size_t namelen;
char name[GIT_FLEX_ARRAY]; char name[GIT_FLEX_ARRAY];
}; };
......
...@@ -542,10 +542,12 @@ int git__bsearch_r( ...@@ -542,10 +542,12 @@ int git__bsearch_r(
*/ */
int git__strcmp_cb(const void *a, const void *b) int git__strcmp_cb(const void *a, const void *b)
{ {
const char *stra = (const char *)a; return strcmp((const char *)a, (const char *)b);
const char *strb = (const char *)b; }
return strcmp(stra, strb); int git__strcasecmp_cb(const void *a, const void *b)
{
return strcasecmp((const char *)a, (const char *)b);
} }
int git__parse_bool(int *out, const char *value) int git__parse_bool(int *out, const char *value)
......
...@@ -196,6 +196,7 @@ extern int git__bsearch_r( ...@@ -196,6 +196,7 @@ extern int git__bsearch_r(
size_t *position); size_t *position);
extern int git__strcmp_cb(const void *a, const void *b); extern int git__strcmp_cb(const void *a, const void *b);
extern int git__strcasecmp_cb(const void *a, const void *b);
extern int git__strcmp(const char *a, const char *b); extern int git__strcmp(const char *a, const char *b);
extern int git__strcasecmp(const char *a, const char *b); extern int git__strcasecmp(const char *a, const char *b);
......
...@@ -177,7 +177,8 @@ void git_vector_sort(git_vector *v) ...@@ -177,7 +177,8 @@ void git_vector_sort(git_vector *v)
if (git_vector_is_sorted(v) || !v->_cmp) if (git_vector_is_sorted(v) || !v->_cmp)
return; return;
git__tsort(v->contents, v->length, v->_cmp); if (v->length > 1)
git__tsort(v->contents, v->length, v->_cmp);
git_vector_set_sorted(v, 1); git_vector_set_sorted(v, 1);
} }
...@@ -276,14 +277,16 @@ void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *)) ...@@ -276,14 +277,16 @@ void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *))
} }
void git_vector_remove_matching( void git_vector_remove_matching(
git_vector *v, int (*match)(const git_vector *v, size_t idx)) git_vector *v,
int (*match)(const git_vector *v, size_t idx, void *payload),
void *payload)
{ {
size_t i, j; size_t i, j;
for (i = 0, j = 0; j < v->length; ++j) { for (i = 0, j = 0; j < v->length; ++j) {
v->contents[i] = v->contents[j]; v->contents[i] = v->contents[j];
if (!match(v, i)) if (!match(v, i, payload))
i++; i++;
} }
...@@ -339,3 +342,18 @@ int git_vector_set(void **old, git_vector *v, size_t position, void *value) ...@@ -339,3 +342,18 @@ int git_vector_set(void **old, git_vector *v, size_t position, void *value)
return 0; return 0;
} }
int git_vector_verify_sorted(const git_vector *v)
{
size_t i;
if (!git_vector_is_sorted(v))
return -1;
for (i = 1; i < v->length; ++i) {
if (v->_cmp(v->contents[i - 1], v->contents[i]) > 0)
return -1;
}
return 0;
}
...@@ -85,8 +85,11 @@ int git_vector_insert_sorted(git_vector *v, void *element, ...@@ -85,8 +85,11 @@ int git_vector_insert_sorted(git_vector *v, void *element,
int git_vector_remove(git_vector *v, size_t idx); int git_vector_remove(git_vector *v, size_t idx);
void git_vector_pop(git_vector *v); void git_vector_pop(git_vector *v);
void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *)); void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *));
void git_vector_remove_matching( void git_vector_remove_matching(
git_vector *v, int (*match)(const git_vector *v, size_t idx)); git_vector *v,
int (*match)(const git_vector *v, size_t idx, void *payload),
void *payload);
int git_vector_resize_to(git_vector *v, size_t new_length); int git_vector_resize_to(git_vector *v, size_t new_length);
int git_vector_set(void **old, git_vector *v, size_t position, void *value); int git_vector_set(void **old, git_vector *v, size_t position, void *value);
...@@ -108,4 +111,7 @@ GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp) ...@@ -108,4 +111,7 @@ GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp)
} }
} }
/* Just use this in tests, not for realz. returns -1 if not sorted */
int git_vector_verify_sorted(const git_vector *v);
#endif #endif
...@@ -11,9 +11,9 @@ void test_attr_file__simple_read(void) ...@@ -11,9 +11,9 @@ void test_attr_file__simple_read(void)
git_attr_assignment *assign; git_attr_assignment *assign;
git_attr_rule *rule; git_attr_rule *rule;
cl_git_pass(git_attr_file__new_and_load(&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->key + 2); 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);
...@@ -37,9 +37,9 @@ void test_attr_file__match_variants(void) ...@@ -37,9 +37,9 @@ void test_attr_file__match_variants(void)
git_attr_rule *rule; git_attr_rule *rule;
git_attr_assignment *assign; git_attr_assignment *assign;
cl_git_pass(git_attr_file__new_and_load(&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->key + 2); 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
...@@ -121,9 +121,9 @@ void test_attr_file__assign_variants(void) ...@@ -121,9 +121,9 @@ void test_attr_file__assign_variants(void)
git_attr_rule *rule; git_attr_rule *rule;
git_attr_assignment *assign; git_attr_assignment *assign;
cl_git_pass(git_attr_file__new_and_load(&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->key + 2); 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);
...@@ -187,8 +187,8 @@ void test_attr_file__check_attr_examples(void) ...@@ -187,8 +187,8 @@ void test_attr_file__check_attr_examples(void)
git_attr_rule *rule; git_attr_rule *rule;
git_attr_assignment *assign; git_attr_assignment *assign;
cl_git_pass(git_attr_file__new_and_load(&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->key + 2); 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);
......
...@@ -9,8 +9,8 @@ void test_attr_lookup__simple(void) ...@@ -9,8 +9,8 @@ void test_attr_lookup__simple(void)
git_attr_path path; git_attr_path path;
const char *value = NULL; const char *value = NULL;
cl_git_pass(git_attr_file__new_and_load(&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->key + 2); 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));
...@@ -129,8 +129,8 @@ void test_attr_lookup__match_variants(void) ...@@ -129,8 +129,8 @@ void test_attr_lookup__match_variants(void)
{ NULL, NULL, 0, NULL } { NULL, NULL, 0, NULL }
}; };
cl_git_pass(git_attr_file__new_and_load(&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->key + 2); 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));
...@@ -190,7 +190,7 @@ void test_attr_lookup__assign_variants(void) ...@@ -190,7 +190,7 @@ void test_attr_lookup__assign_variants(void)
{ NULL, NULL, 0, NULL } { NULL, NULL, 0, NULL }
}; };
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr2"))); cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2")));
cl_assert(file->rules.length == 11); cl_assert(file->rules.length == 11);
run_test_cases(file, cases, 0); run_test_cases(file, cases, 0);
...@@ -225,7 +225,7 @@ void test_attr_lookup__check_attr_examples(void) ...@@ -225,7 +225,7 @@ void test_attr_lookup__check_attr_examples(void)
{ NULL, NULL, 0, NULL } { NULL, NULL, 0, NULL }
}; };
cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3"))); cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3")));
cl_assert(file->rules.length == 3); cl_assert(file->rules.length == 3);
run_test_cases(file, cases, 0); run_test_cases(file, cases, 0);
...@@ -250,9 +250,9 @@ void test_attr_lookup__from_buffer(void) ...@@ -250,9 +250,9 @@ void test_attr_lookup__from_buffer(void)
{ NULL, NULL, 0, NULL } { NULL, NULL, 0, NULL }
}; };
cl_git_pass(git_attr_file__new(&file, 0, NULL, NULL)); cl_git_pass(git_attr_file__new(&file, NULL, 0));
cl_git_pass(git_attr_file__parse_buffer(NULL, NULL, "a* foo\nabc bar\n* baz", file)); 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)
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* Use this wrapper around all `git_` library calls that return error codes! * Use this wrapper around all `git_` library calls that return error codes!
*/ */
#define cl_git_pass(expr) cl_git_pass_(expr, __FILE__, __LINE__) #define cl_git_pass(expr) cl_git_pass_((expr), __FILE__, __LINE__)
#define cl_git_pass_(expr, file, line) do { \ #define cl_git_pass_(expr, file, line) do { \
int _lg2_error; \ int _lg2_error; \
......
...@@ -3,12 +3,22 @@ ...@@ -3,12 +3,22 @@
GIT__USE_STRMAP; GIT__USE_STRMAP;
git_strmap *g_table;
void test_core_strmap__initialize(void)
{
cl_git_pass(git_strmap_alloc(&g_table));
cl_assert(g_table != NULL);
}
void test_core_strmap__cleanup(void)
{
git_strmap_free(g_table);
}
void test_core_strmap__0(void) void test_core_strmap__0(void)
{ {
git_strmap *table = git_strmap_alloc(); cl_assert(git_strmap_num_entries(g_table) == 0);
cl_assert(table != NULL);
cl_assert(git_strmap_num_entries(table) == 0);
git_strmap_free(table);
} }
static void insert_strings(git_strmap *table, int count) static void insert_strings(git_strmap *table, int count)
...@@ -37,21 +47,17 @@ void test_core_strmap__1(void) ...@@ -37,21 +47,17 @@ void test_core_strmap__1(void)
{ {
int i; int i;
char *str; char *str;
git_strmap *table = git_strmap_alloc();
cl_assert(table != NULL);
insert_strings(table, 20); insert_strings(g_table, 20);
cl_assert(git_strmap_exists(table, "aaaaaaaaa")); cl_assert(git_strmap_exists(g_table, "aaaaaaaaa"));
cl_assert(git_strmap_exists(table, "ggggggggg")); cl_assert(git_strmap_exists(g_table, "ggggggggg"));
cl_assert(!git_strmap_exists(table, "aaaaaaaab")); cl_assert(!git_strmap_exists(g_table, "aaaaaaaab"));
cl_assert(!git_strmap_exists(table, "abcdefghi")); cl_assert(!git_strmap_exists(g_table, "abcdefghi"));
i = 0; i = 0;
git_strmap_foreach_value(table, str, { i++; free(str); }); git_strmap_foreach_value(g_table, str, { i++; free(str); });
cl_assert(i == 20); cl_assert(i == 20);
git_strmap_free(table);
} }
void test_core_strmap__2(void) void test_core_strmap__2(void)
...@@ -59,44 +65,36 @@ void test_core_strmap__2(void) ...@@ -59,44 +65,36 @@ void test_core_strmap__2(void)
khiter_t pos; khiter_t pos;
int i; int i;
char *str; char *str;
git_strmap *table = git_strmap_alloc();
cl_assert(table != NULL);
insert_strings(table, 20); insert_strings(g_table, 20);
cl_assert(git_strmap_exists(table, "aaaaaaaaa")); cl_assert(git_strmap_exists(g_table, "aaaaaaaaa"));
cl_assert(git_strmap_exists(table, "ggggggggg")); cl_assert(git_strmap_exists(g_table, "ggggggggg"));
cl_assert(!git_strmap_exists(table, "aaaaaaaab")); cl_assert(!git_strmap_exists(g_table, "aaaaaaaab"));
cl_assert(!git_strmap_exists(table, "abcdefghi")); cl_assert(!git_strmap_exists(g_table, "abcdefghi"));
cl_assert(git_strmap_exists(table, "bbbbbbbbb")); cl_assert(git_strmap_exists(g_table, "bbbbbbbbb"));
pos = git_strmap_lookup_index(table, "bbbbbbbbb"); pos = git_strmap_lookup_index(g_table, "bbbbbbbbb");
cl_assert(git_strmap_valid_index(table, pos)); cl_assert(git_strmap_valid_index(g_table, pos));
cl_assert_equal_s(git_strmap_value_at(table, pos), "bbbbbbbbb"); cl_assert_equal_s(git_strmap_value_at(g_table, pos), "bbbbbbbbb");
free(git_strmap_value_at(table, pos)); free(git_strmap_value_at(g_table, pos));
git_strmap_delete_at(table, pos); git_strmap_delete_at(g_table, pos);
cl_assert(!git_strmap_exists(table, "bbbbbbbbb")); cl_assert(!git_strmap_exists(g_table, "bbbbbbbbb"));
i = 0; i = 0;
git_strmap_foreach_value(table, str, { i++; free(str); }); git_strmap_foreach_value(g_table, str, { i++; free(str); });
cl_assert(i == 19); cl_assert(i == 19);
git_strmap_free(table);
} }
void test_core_strmap__3(void) void test_core_strmap__3(void)
{ {
int i; int i;
char *str; char *str;
git_strmap *table = git_strmap_alloc();
cl_assert(table != NULL);
insert_strings(table, 10000); insert_strings(g_table, 10000);
i = 0; i = 0;
git_strmap_foreach_value(table, str, { i++; free(str); }); git_strmap_foreach_value(g_table, str, { i++; free(str); });
cl_assert(i == 10000); cl_assert(i == 10000);
git_strmap_free(table);
} }
...@@ -190,8 +190,9 @@ void test_core_vector__5(void) ...@@ -190,8 +190,9 @@ void test_core_vector__5(void)
git_vector_free(&x); git_vector_free(&x);
} }
static int remove_ones(const git_vector *v, size_t idx) static int remove_ones(const git_vector *v, size_t idx, void *p)
{ {
GIT_UNUSED(p);
return (git_vector_get(v, idx) == (void *)0x001); return (git_vector_get(v, idx) == (void *)0x001);
} }
...@@ -206,7 +207,7 @@ void test_core_vector__remove_matching(void) ...@@ -206,7 +207,7 @@ void test_core_vector__remove_matching(void)
git_vector_insert(&x, (void*) 0x001); git_vector_insert(&x, (void*) 0x001);
cl_assert(x.length == 1); cl_assert(x.length == 1);
git_vector_remove_matching(&x, remove_ones); git_vector_remove_matching(&x, remove_ones, NULL);
cl_assert(x.length == 0); cl_assert(x.length == 0);
git_vector_insert(&x, (void*) 0x001); git_vector_insert(&x, (void*) 0x001);
...@@ -214,7 +215,7 @@ void test_core_vector__remove_matching(void) ...@@ -214,7 +215,7 @@ void test_core_vector__remove_matching(void)
git_vector_insert(&x, (void*) 0x001); git_vector_insert(&x, (void*) 0x001);
cl_assert(x.length == 3); cl_assert(x.length == 3);
git_vector_remove_matching(&x, remove_ones); git_vector_remove_matching(&x, remove_ones, NULL);
cl_assert(x.length == 0); cl_assert(x.length == 0);
git_vector_insert(&x, (void*) 0x002); git_vector_insert(&x, (void*) 0x002);
...@@ -223,7 +224,7 @@ void test_core_vector__remove_matching(void) ...@@ -223,7 +224,7 @@ void test_core_vector__remove_matching(void)
git_vector_insert(&x, (void*) 0x001); git_vector_insert(&x, (void*) 0x001);
cl_assert(x.length == 4); cl_assert(x.length == 4);
git_vector_remove_matching(&x, remove_ones); git_vector_remove_matching(&x, remove_ones, NULL);
cl_assert(x.length == 2); cl_assert(x.length == 2);
git_vector_foreach(&x, i, compare) { git_vector_foreach(&x, i, compare) {
...@@ -238,7 +239,7 @@ void test_core_vector__remove_matching(void) ...@@ -238,7 +239,7 @@ void test_core_vector__remove_matching(void)
git_vector_insert(&x, (void*) 0x001); git_vector_insert(&x, (void*) 0x001);
cl_assert(x.length == 4); cl_assert(x.length == 4);
git_vector_remove_matching(&x, remove_ones); git_vector_remove_matching(&x, remove_ones, NULL);
cl_assert(x.length == 2); cl_assert(x.length == 2);
git_vector_foreach(&x, i, compare) { git_vector_foreach(&x, i, compare) {
...@@ -253,7 +254,7 @@ void test_core_vector__remove_matching(void) ...@@ -253,7 +254,7 @@ void test_core_vector__remove_matching(void)
git_vector_insert(&x, (void*) 0x001); git_vector_insert(&x, (void*) 0x001);
cl_assert(x.length == 4); cl_assert(x.length == 4);
git_vector_remove_matching(&x, remove_ones); git_vector_remove_matching(&x, remove_ones, NULL);
cl_assert(x.length == 2); cl_assert(x.length == 2);
git_vector_foreach(&x, i, compare) { git_vector_foreach(&x, i, compare) {
...@@ -268,7 +269,7 @@ void test_core_vector__remove_matching(void) ...@@ -268,7 +269,7 @@ void test_core_vector__remove_matching(void)
git_vector_insert(&x, (void*) 0x003); git_vector_insert(&x, (void*) 0x003);
cl_assert(x.length == 4); cl_assert(x.length == 4);
git_vector_remove_matching(&x, remove_ones); git_vector_remove_matching(&x, remove_ones, NULL);
cl_assert(x.length == 4); cl_assert(x.length == 4);
git_vector_free(&x); git_vector_free(&x);
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "diff_helpers.h" #include "diff_helpers.h"
#include "git2/sys/diff.h"
git_tree *resolve_commit_oid_to_tree( git_tree *resolve_commit_oid_to_tree(
git_repository *repo, git_repository *repo,
...@@ -215,32 +216,16 @@ abort: ...@@ -215,32 +216,16 @@ abort:
return GIT_EUSER; return GIT_EUSER;
} }
static int diff_print_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload)
{
FILE *fp = payload;
GIT_UNUSED(delta); GIT_UNUSED(hunk);
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
line->origin == GIT_DIFF_LINE_ADDITION ||
line->origin == GIT_DIFF_LINE_DELETION)
fputc(line->origin, fp);
fwrite(line->content, 1, line->content_len, fp);
return 0;
}
void diff_print(FILE *fp, git_diff *diff) void diff_print(FILE *fp, git_diff *diff)
{ {
cl_git_pass(git_diff_print( cl_git_pass(
diff, GIT_DIFF_FORMAT_PATCH, diff_print_cb, fp ? fp : stderr)); git_diff_print(diff, GIT_DIFF_FORMAT_PATCH,
git_diff_print_callback__to_file_handle, fp ? fp : stderr));
} }
void diff_print_raw(FILE *fp, git_diff *diff) void diff_print_raw(FILE *fp, git_diff *diff)
{ {
cl_git_pass(git_diff_print( cl_git_pass(
diff, GIT_DIFF_FORMAT_RAW, diff_print_cb, fp ? fp : stderr)); git_diff_print(diff, GIT_DIFF_FORMAT_RAW,
git_diff_print_callback__to_file_handle, fp ? fp : stderr));
} }
...@@ -355,6 +355,7 @@ static void index_iterator_test( ...@@ -355,6 +355,7 @@ static void index_iterator_test(
const char *sandbox, const char *sandbox,
const char *start, const char *start,
const char *end, const char *end,
git_iterator_flag_t flags,
int expected_count, int expected_count,
const char **expected_names, const char **expected_names,
const char **expected_oids) const char **expected_oids)
...@@ -362,11 +363,13 @@ static void index_iterator_test( ...@@ -362,11 +363,13 @@ static void index_iterator_test(
git_index *index; git_index *index;
git_iterator *i; git_iterator *i;
const git_index_entry *entry; const git_index_entry *entry;
int error, count = 0; int error, count = 0, caps;
git_repository *repo = cl_git_sandbox_init(sandbox); git_repository *repo = cl_git_sandbox_init(sandbox);
cl_git_pass(git_repository_index(&index, repo)); cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_iterator_for_index(&i, index, 0, start, end)); caps = git_index_caps(index);
cl_git_pass(git_iterator_for_index(&i, index, flags, start, end));
while (!(error = git_iterator_advance(&entry, i))) { while (!(error = git_iterator_advance(&entry, i))) {
cl_assert(entry); cl_assert(entry);
...@@ -388,6 +391,8 @@ static void index_iterator_test( ...@@ -388,6 +391,8 @@ static void index_iterator_test(
cl_assert_equal_i(expected_count, count); cl_assert_equal_i(expected_count, count);
git_iterator_free(i); git_iterator_free(i);
cl_assert(caps == git_index_caps(index));
git_index_free(index); git_index_free(index);
} }
...@@ -446,7 +451,8 @@ static const char *expected_index_oids_0[] = { ...@@ -446,7 +451,8 @@ static const char *expected_index_oids_0[] = {
void test_diff_iterator__index_0(void) void test_diff_iterator__index_0(void)
{ {
index_iterator_test( index_iterator_test(
"attr", NULL, NULL, 23, expected_index_0, expected_index_oids_0); "attr", NULL, NULL, 0, ARRAY_SIZE(expected_index_0),
expected_index_0, expected_index_oids_0);
} }
static const char *expected_index_range[] = { static const char *expected_index_range[] = {
...@@ -466,25 +472,26 @@ static const char *expected_index_oids_range[] = { ...@@ -466,25 +472,26 @@ static const char *expected_index_oids_range[] = {
void test_diff_iterator__index_range(void) void test_diff_iterator__index_range(void)
{ {
index_iterator_test( index_iterator_test(
"attr", "root", "root", 4, expected_index_range, expected_index_oids_range); "attr", "root", "root", 0, ARRAY_SIZE(expected_index_range),
expected_index_range, expected_index_oids_range);
} }
void test_diff_iterator__index_range_empty_0(void) void test_diff_iterator__index_range_empty_0(void)
{ {
index_iterator_test( index_iterator_test(
"attr", "empty", "empty", 0, NULL, NULL); "attr", "empty", "empty", 0, 0, NULL, NULL);
} }
void test_diff_iterator__index_range_empty_1(void) void test_diff_iterator__index_range_empty_1(void)
{ {
index_iterator_test( index_iterator_test(
"attr", "z_empty_after", NULL, 0, NULL, NULL); "attr", "z_empty_after", NULL, 0, 0, NULL, NULL);
} }
void test_diff_iterator__index_range_empty_2(void) void test_diff_iterator__index_range_empty_2(void)
{ {
index_iterator_test( index_iterator_test(
"attr", NULL, ".aaa_empty_before", 0, NULL, NULL); "attr", NULL, ".aaa_empty_before", 0, 0, NULL, NULL);
} }
static const char *expected_index_1[] = { static const char *expected_index_1[] = {
...@@ -522,9 +529,45 @@ static const char* expected_index_oids_1[] = { ...@@ -522,9 +529,45 @@ static const char* expected_index_oids_1[] = {
void test_diff_iterator__index_1(void) void test_diff_iterator__index_1(void)
{ {
index_iterator_test( index_iterator_test(
"status", NULL, NULL, 13, expected_index_1, expected_index_oids_1); "status", NULL, NULL, 0, ARRAY_SIZE(expected_index_1),
expected_index_1, expected_index_oids_1);
} }
static const char *expected_index_cs[] = {
"B", "D", "F", "H", "J", "L/1", "L/B", "L/D", "L/a", "L/c",
"a", "c", "e", "g", "i", "k/1", "k/B", "k/D", "k/a", "k/c",
};
static const char *expected_index_ci[] = {
"a", "B", "c", "D", "e", "F", "g", "H", "i", "J",
"k/1", "k/a", "k/B", "k/c", "k/D", "L/1", "L/a", "L/B", "L/c", "L/D",
};
void test_diff_iterator__index_case_folding(void)
{
git_buf path = GIT_BUF_INIT;
int fs_is_ci = 0;
cl_git_pass(git_buf_joinpath(&path, cl_fixture("icase"), ".gitted/CoNfIg"));
fs_is_ci = git_path_exists(path.ptr);
git_buf_free(&path);
index_iterator_test(
"icase", NULL, NULL, 0, ARRAY_SIZE(expected_index_cs),
fs_is_ci ? expected_index_ci : expected_index_cs, NULL);
cl_git_sandbox_cleanup();
index_iterator_test(
"icase", NULL, NULL, GIT_ITERATOR_IGNORE_CASE,
ARRAY_SIZE(expected_index_ci), expected_index_ci, NULL);
cl_git_sandbox_cleanup();
index_iterator_test(
"icase", NULL, NULL, GIT_ITERATOR_DONT_IGNORE_CASE,
ARRAY_SIZE(expected_index_cs), expected_index_cs, NULL);
}
/* -- WORKDIR ITERATOR TESTS -- */ /* -- WORKDIR ITERATOR TESTS -- */
......
...@@ -544,36 +544,22 @@ void test_index_tests__corrupted_extension(void) ...@@ -544,36 +544,22 @@ void test_index_tests__corrupted_extension(void)
cl_git_fail_with(git_index_open(&index, TEST_INDEXBAD_PATH), GIT_ERROR); cl_git_fail_with(git_index_open(&index, TEST_INDEXBAD_PATH), GIT_ERROR);
} }
static void assert_index_is_sorted(git_index *index)
{
git_vector *entries = &index->entries;
size_t i;
cl_assert(git_vector_is_sorted(entries));
for (i = 1; i < git_vector_length(entries); ++i) {
git_index_entry *prev = git_vector_get(entries, i - 1);
git_index_entry *curr = git_vector_get(entries, i);
cl_assert(index->entries._cmp(prev, curr) <= 0);
}
}
void test_index_tests__reload_while_ignoring_case(void) void test_index_tests__reload_while_ignoring_case(void)
{ {
git_index *index; git_index *index;
unsigned int caps; unsigned int caps;
cl_git_pass(git_index_open(&index, TEST_INDEX_PATH)); cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
assert_index_is_sorted(index); cl_git_pass(git_vector_verify_sorted(&index->entries));
caps = git_index_caps(index); caps = git_index_caps(index);
cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEXCAP_IGNORE_CASE)); cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEXCAP_IGNORE_CASE));
cl_git_pass(git_index_read(index, true)); cl_git_pass(git_index_read(index, true));
assert_index_is_sorted(index); cl_git_pass(git_vector_verify_sorted(&index->entries));
cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE)); cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE));
cl_git_pass(git_index_read(index, true)); cl_git_pass(git_index_read(index, true));
assert_index_is_sorted(index); cl_git_pass(git_vector_verify_sorted(&index->entries));
git_index_free(index); git_index_free(index);
} }
...@@ -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"));
} }
......
#include "clar_libgit2.h"
#include "thread_helpers.h"
static git_repository *_repo;
static git_tree *_a, *_b;
static git_atomic _counts[4];
static int _check_counts;
#define THREADS 20
void test_threads_diff__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void setup_trees(void)
{
git_index *idx;
_repo = cl_git_sandbox_reopen(); /* reopen sandbox to flush caches */
/* avoid competing to load initial index */
cl_git_pass(git_repository_index(&idx, _repo));
git_index_free(idx);
cl_git_pass(git_revparse_single(
(git_object **)&_a, _repo, "0017bd4ab1^{tree}"));
cl_git_pass(git_revparse_single(
(git_object **)&_b, _repo, "26a125ee1b^{tree}"));
memset(_counts, 0, sizeof(_counts));
}
static void free_trees(void)
{
git_tree_free(_a); _a = NULL;
git_tree_free(_b); _b = NULL;
if (_check_counts) {
cl_assert_equal_i(288, git_atomic_get(&_counts[0]));
cl_assert_equal_i(112, git_atomic_get(&_counts[1]));
cl_assert_equal_i( 80, git_atomic_get(&_counts[2]));
cl_assert_equal_i( 96, git_atomic_get(&_counts[3]));
}
}
static void *run_index_diffs(void *arg)
{
int thread = *(int *)arg;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
size_t i;
int exp[4] = { 0, 0, 0, 0 };
switch (thread & 0x03) {
case 0: /* diff index to workdir */;
cl_git_pass(git_diff_index_to_workdir(&diff, _repo, NULL, &opts));
break;
case 1: /* diff tree 'a' to index */;
cl_git_pass(git_diff_tree_to_index(&diff, _repo, _a, NULL, &opts));
break;
case 2: /* diff tree 'b' to index */;
cl_git_pass(git_diff_tree_to_index(&diff, _repo, _b, NULL, &opts));
break;
case 3: /* diff index to workdir (explicit index) */;
{
git_index *idx;
cl_git_pass(git_repository_index(&idx, _repo));
cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts));
git_index_free(idx);
break;
}
}
/* keep some diff stats to make sure results are as expected */
i = git_diff_num_deltas(diff);
git_atomic_add(&_counts[0], (int32_t)i);
exp[0] = (int)i;
while (i > 0) {
switch (git_diff_get_delta(diff, --i)->status) {
case GIT_DELTA_MODIFIED: exp[1]++; git_atomic_inc(&_counts[1]); break;
case GIT_DELTA_ADDED: exp[2]++; git_atomic_inc(&_counts[2]); break;
case GIT_DELTA_DELETED: exp[3]++; git_atomic_inc(&_counts[3]); break;
default: break;
}
}
switch (thread & 0x03) {
case 0: case 3:
cl_assert_equal_i(8, exp[0]); cl_assert_equal_i(4, exp[1]);
cl_assert_equal_i(0, exp[2]); cl_assert_equal_i(4, exp[3]);
break;
case 1:
cl_assert_equal_i(12, exp[0]); cl_assert_equal_i(3, exp[1]);
cl_assert_equal_i(7, exp[2]); cl_assert_equal_i(2, exp[3]);
break;
case 2:
cl_assert_equal_i(8, exp[0]); cl_assert_equal_i(3, exp[1]);
cl_assert_equal_i(3, exp[2]); cl_assert_equal_i(2, exp[3]);
break;
}
git_diff_free(diff);
giterr_clear();
return arg;
}
void test_threads_diff__concurrent_diffs(void)
{
_repo = cl_git_sandbox_init("status");
_check_counts = 1;
run_in_parallel(
5, 32, run_index_diffs, setup_trees, free_trees);
}
static void *run_index_diffs_with_modifier(void *arg)
{
int thread = *(int *)arg;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
git_index *idx = NULL;
cl_git_pass(git_repository_index(&idx, _repo));
/* have first thread altering the index as we go */
if (thread == 0) {
int i;
for (i = 0; i < 300; ++i) {
switch (i & 0x03) {
case 0: (void)git_index_add_bypath(idx, "new_file"); break;
case 1: (void)git_index_remove_bypath(idx, "modified_file"); break;
case 2: (void)git_index_remove_bypath(idx, "new_file"); break;
case 3: (void)git_index_add_bypath(idx, "modified_file"); break;
}
git_thread_yield();
}
goto done;
}
/* only use explicit index in this test to prevent reloading */
switch (thread & 0x03) {
case 0: /* diff index to workdir */;
cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts));
break;
case 1: /* diff tree 'a' to index */;
cl_git_pass(git_diff_tree_to_index(&diff, _repo, _a, idx, &opts));
break;
case 2: /* diff tree 'b' to index */;
cl_git_pass(git_diff_tree_to_index(&diff, _repo, _b, idx, &opts));
break;
case 3: /* diff index to workdir reversed */;
opts.flags |= GIT_DIFF_REVERSE;
cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts));
break;
}
/* results will be unpredictable with index modifier thread running */
git_diff_free(diff);
done:
git_index_free(idx);
giterr_clear();
return arg;
}
void test_threads_diff__with_concurrent_index_modified(void)
{
_repo = cl_git_sandbox_init("status");
_check_counts = 0;
run_in_parallel(
5, 16, run_index_diffs_with_modifier, setup_trees, free_trees);
}
#include "clar_libgit2.h"
#include "thread_helpers.h"
#include "iterator.h"
static git_repository *_repo;
void test_threads_iterator__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void *run_workdir_iterator(void *arg)
{
int error = 0;
git_iterator *iter;
const git_index_entry *entry = NULL;
cl_git_pass(git_iterator_for_workdir(
&iter, _repo, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
while (!error) {
if (entry && entry->mode == GIT_FILEMODE_TREE) {
error = git_iterator_advance_into(&entry, iter);
if (error == GIT_ENOTFOUND)
error = git_iterator_advance(&entry, iter);
} else {
error = git_iterator_advance(&entry, iter);
}
if (!error)
(void)git_iterator_current_is_ignored(iter);
}
cl_assert_equal_i(GIT_ITEROVER, error);
git_iterator_free(iter);
giterr_clear();
return arg;
}
void test_threads_iterator__workdir(void)
{
_repo = cl_git_sandbox_init("status");
run_in_parallel(
1, 20, run_workdir_iterator, NULL, NULL);
}
...@@ -37,6 +37,7 @@ static void *iterate_refs(void *arg) ...@@ -37,6 +37,7 @@ static void *iterate_refs(void *arg)
git_reference_iterator_free(i); git_reference_iterator_free(i);
giterr_clear();
return arg; return arg;
} }
...@@ -115,6 +116,7 @@ static void *create_refs(void *arg) ...@@ -115,6 +116,7 @@ static void *create_refs(void *arg)
for (i = 0; i < 10; ++i) for (i = 0; i < 10; ++i)
git_reference_free(ref[i]); git_reference_free(ref[i]);
giterr_clear();
return arg; return arg;
} }
...@@ -141,6 +143,7 @@ static void *delete_refs(void *arg) ...@@ -141,6 +143,7 @@ static void *delete_refs(void *arg)
} }
} }
giterr_clear();
return arg; return arg;
} }
......
#include "clar_libgit2.h"
#include "thread_helpers.h"
void run_in_parallel(
int repeats,
int threads,
void *(*func)(void *),
void (*before_test)(void),
void (*after_test)(void))
{
int r, t, *id = git__calloc(threads, sizeof(int));
#ifdef GIT_THREADS
git_thread *th = git__calloc(threads, sizeof(git_thread));
cl_assert(th != NULL);
#else
void *th = NULL;
#endif
cl_assert(id != NULL);
for (r = 0; r < repeats; ++r) {
if (before_test) before_test();
for (t = 0; t < threads; ++t) {
id[t] = t;
#ifdef GIT_THREADS
cl_git_pass(git_thread_create(&th[t], NULL, func, &id[t]));
#else
cl_assert(func(&id[t]) == &id[t]);
#endif
}
#ifdef GIT_THREADS
for (t = 0; t < threads; ++t)
cl_git_pass(git_thread_join(th[t], NULL));
memset(th, 0, threads * sizeof(git_thread));
#endif
if (after_test) after_test();
}
git__free(id);
git__free(th);
}
#include "thread-utils.h"
void run_in_parallel(
int repeats,
int threads,
void *(*func)(void *),
void (*before_test)(void),
void (*after_test)(void));
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