mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-06 11:19:47 +00:00
Merge pull request #2108 from libgit2/rb/threadsafe-index-iterator
Make index iterator thread safe
This commit is contained in:
commit
28fd7206b1
@ -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 {
|
|||||||
#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);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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(
|
|||||||
* @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(
|
|||||||
* @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(
|
|||||||
* 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);
|
|||||||
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(
|
||||||
|
63
include/git2/sys/diff.h
Normal file
63
include/git2/sys/diff.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
463
src/attr.c
463
src/attr.c
@ -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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
|
|
||||||
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(
|
|||||||
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 {
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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(
|
|||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
34
src/attr.h
34
src/attr.h
@ -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
|
||||||
|
403
src/attr_file.c
403
src/attr_file.c
@ -1,11 +1,210 @@
|
|||||||
#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 void attr_file_free(git_attr_file *file)
|
||||||
|
{
|
||||||
|
bool unlock = !git_mutex_lock(&file->lock);
|
||||||
|
git_attr_file__clear_rules(file, false);
|
||||||
|
git_pool_clear(&file->pool);
|
||||||
|
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(
|
||||||
|
git_attr_file **out,
|
||||||
|
git_attr_file_entry *entry,
|
||||||
|
git_attr_file_source source)
|
||||||
|
{
|
||||||
|
git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
|
||||||
|
GITERR_CHECK_ALLOC(attrs);
|
||||||
|
|
||||||
|
if (git_mutex_init(&attrs->lock) < 0) {
|
||||||
|
giterr_set(GITERR_OS, "Failed to initialize lock");
|
||||||
|
git__free(attrs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (git_pool_init(&attrs->pool, 1, 0) < 0) {
|
||||||
|
attr_file_free(attrs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
GIT_REFCOUNT_INC(attrs);
|
||||||
|
attrs->entry = entry;
|
||||||
|
attrs->source = source;
|
||||||
|
*out = attrs;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_attr_file__clear_rules(git_attr_file *file, bool need_lock)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
git_attr_rule *rule;
|
||||||
|
|
||||||
|
if (need_lock && git_mutex_lock(&file->lock) < 0) {
|
||||||
|
giterr_set(GITERR_OS, "Failed to lock attribute file");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
git_vector_foreach(&file->rules, i, rule)
|
||||||
|
git_attr_rule__free(rule);
|
||||||
|
git_vector_free(&file->rules);
|
||||||
|
|
||||||
|
if (need_lock)
|
||||||
|
git_mutex_unlock(&file->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_attr_file__free(git_attr_file *file)
|
||||||
|
{
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
GIT_REFCOUNT_DEC(file, attr_file_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int attr_file_oid_from_index(
|
||||||
|
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;
|
||||||
|
git_blob *blob = 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);
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
|
data = content.ptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
giterr_set(GITERR_INVALID, "Unknown file source %d", source);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 void git_attr_rule__clear(git_attr_rule *rule);
|
||||||
static bool parse_optimized_patterns(
|
static bool parse_optimized_patterns(
|
||||||
@ -13,73 +212,28 @@ static bool parse_optimized_patterns(
|
|||||||
git_pool *pool,
|
git_pool *pool,
|
||||||
const char *pattern);
|
const char *pattern);
|
||||||
|
|
||||||
int git_attr_file__new(
|
|
||||||
git_attr_file **attrs_ptr,
|
|
||||||
git_attr_file_source from,
|
|
||||||
const char *path,
|
|
||||||
git_pool *pool)
|
|
||||||
{
|
|
||||||
git_attr_file *attrs = NULL;
|
|
||||||
|
|
||||||
attrs = git__calloc(1, sizeof(git_attr_file));
|
|
||||||
GITERR_CHECK_ALLOC(attrs);
|
|
||||||
|
|
||||||
if (pool)
|
|
||||||
attrs->pool = pool;
|
|
||||||
else {
|
|
||||||
attrs->pool = git__calloc(1, sizeof(git_pool));
|
|
||||||
if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)
|
|
||||||
goto fail;
|
|
||||||
attrs->pool_is_allocated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
size_t len = strlen(path);
|
|
||||||
|
|
||||||
attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
|
|
||||||
GITERR_CHECK_ALLOC(attrs->key);
|
|
||||||
|
|
||||||
attrs->key[0] = '0' + (char)from;
|
|
||||||
attrs->key[1] = '#';
|
|
||||||
memcpy(&attrs->key[2], path, len);
|
|
||||||
attrs->key[len + 2] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (git_vector_init(&attrs->rules, 4, NULL) < 0)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
*attrs_ptr = attrs;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
git_attr_file__free(attrs);
|
|
||||||
attrs_ptr = NULL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int git_attr_file__parse_buffer(
|
int git_attr_file__parse_buffer(
|
||||||
git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs)
|
git_repository *repo, git_attr_file *attrs, const char *data)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
const char *scan = NULL, *context = NULL;
|
const char *scan = data, *context = NULL;
|
||||||
git_attr_rule *rule = NULL;
|
git_attr_rule *rule = NULL;
|
||||||
|
|
||||||
GIT_UNUSED(parsedata);
|
|
||||||
|
|
||||||
assert(buffer && attrs);
|
|
||||||
|
|
||||||
scan = buffer;
|
|
||||||
|
|
||||||
/* 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(
|
|||||||
|
|
||||||
/* 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(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
|||||||
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(
|
|||||||
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(
|
|||||||
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)
|
|||||||
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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(
|
|||||||
} 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 @@
|
|||||||
(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 {
|
|||||||
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_mutex lock;
|
||||||
|
git_attr_file_entry *entry;
|
||||||
|
git_attr_file_source source;
|
||||||
git_vector rules; /* vector of <rule*> or <fnmatch*> */
|
git_vector rules; /* vector of <rule*> or <fnmatch*> */
|
||||||
git_pool *pool;
|
git_pool pool;
|
||||||
bool pool_is_allocated;
|
|
||||||
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 {
|
|||||||
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);
|
||||||
|
|
||||||
extern int git_attr_file__new_and_load(
|
void git_attr_file__free(git_attr_file *file);
|
||||||
git_attr_file **attrs_ptr, const char *path);
|
|
||||||
|
|
||||||
extern 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 void git_attr_file__clear_rules(git_attr_file *file);
|
int git_attr_file__load_standalone(
|
||||||
|
git_attr_file **out, const char *path);
|
||||||
|
|
||||||
extern int git_attr_file__parse_buffer(
|
int git_attr_file__out_of_date(
|
||||||
git_repository *repo, void *parsedata, const char *buf, git_attr_file *file);
|
git_repository *repo, git_attr_file *file);
|
||||||
|
|
||||||
extern int git_attr_file__lookup_one(
|
int git_attr_file__parse_buffer(
|
||||||
|
git_repository *repo, git_attr_file *attrs, const char *data);
|
||||||
|
|
||||||
|
int git_attr_file__clear_rules(
|
||||||
|
git_attr_file *file, bool need_lock);
|
||||||
|
|
||||||
|
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(
|
|||||||
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);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
448
src/attrcache.c
Normal file
448
src/attrcache.c
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
#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 @@
|
|||||||
#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(
|
|||||||
|
|
||||||
/* 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)
|
|||||||
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(
|
|||||||
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 @@
|
|||||||
# 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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
/* 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 {
|
||||||
|
79
src/diff.c
79
src/diff.c
@ -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(
|
|||||||
|
|
||||||
/* 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(
|
|||||||
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()
|
|||||||
if (!reg)
|
if (!reg)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if ((reg->drivers = git_strmap_alloc()) == NULL) {
|
if (git_strmap_alloc(®->drivers) < 0) {
|
||||||
git_diff_driver_registry_free(reg);
|
git_diff_driver_registry_free(reg);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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)
|
|||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
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 {
|
|||||||
* 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(
|
|||||||
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__ */
|
||||||
|
18
src/global.c
18
src/global.c
@ -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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
{
|
{
|
||||||
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,13 +223,14 @@ 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 */
|
||||||
|
if (0 == git_atomic_dec(&git__n_inits))
|
||||||
git__shutdown();
|
git__shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
185
src/ignore.c
185
src/ignore.c
@ -1,7 +1,7 @@
|
|||||||
#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 @@
|
|||||||
#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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
if (!error && !(*ign)->rules.length)
|
error = git_attr_cache__get(
|
||||||
error = parse_ignore_file(repo, NULL, GIT_IGNORE_DEFAULT_RULES, *ign);
|
out, repo, GIT_ATTR_FILE__IN_MEMORY, NULL, GIT_IGNORE_INTERNAL, NULL);
|
||||||
|
|
||||||
|
/* if internal rules list is empty, insert default rules */
|
||||||
|
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(
|
|||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
|||||||
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)
|
|||||||
|
|
||||||
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(
|
|||||||
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:
|
|||||||
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(
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
919
src/index.c
919
src/index.c
File diff suppressed because it is too large
Load Diff
36
src/index.h
36
src/index.h
@ -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 {
|
|||||||
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)
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
src/merge.c
18
src/merge.c
@ -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(
|
|||||||
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:
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
{
|
{
|
||||||
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(
|
|||||||
/* 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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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(
|
|||||||
|
|
||||||
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,7 +39,6 @@ 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);
|
||||||
@ -234,8 +233,7 @@ 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;
|
|||||||
#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(
|
|||||||
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)
|
|||||||
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;
|
|||||||
#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)
|
|||||||
#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 @@
|
|||||||
|
|
||||||
#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)
|
|||||||
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
|
|||||||
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,
|
|||||||
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 {
|
|||||||
|
|
||||||
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(
|
|||||||
*/
|
*/
|
||||||
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(
|
|||||||
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);
|
||||||
|
22
src/vector.c
22
src/vector.c
@ -177,6 +177,7 @@ 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;
|
||||||
|
|
||||||
|
if (v->length > 1)
|
||||||
git__tsort(v->contents, v->length, v->_cmp);
|
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 *))
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
{ 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)
|
|||||||
{ 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)
|
|||||||
{ 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)
|
|||||||
{ 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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
* 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 @@
|
|||||||
|
|
||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#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:
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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[] = {
|
|||||||
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[] = {
|
|||||||
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[] = {
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
182
tests/threads/diff.c
Normal file
182
tests/threads/diff.c
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#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);
|
||||||
|
}
|
49
tests/threads/iterator.c
Normal file
49
tests/threads/iterator.c
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
giterr_clear();
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
tests/threads/thread_helpers.c
Normal file
44
tests/threads/thread_helpers.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#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);
|
||||||
|
}
|
8
tests/threads/thread_helpers.h
Normal file
8
tests/threads/thread_helpers.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "thread-utils.h"
|
||||||
|
|
||||||
|
void run_in_parallel(
|
||||||
|
int repeats,
|
||||||
|
int threads,
|
||||||
|
void *(*func)(void *),
|
||||||
|
void (*before_test)(void),
|
||||||
|
void (*after_test)(void));
|
Loading…
Reference in New Issue
Block a user