diff options
266 files changed, 5579 insertions, 1428 deletions
@@ -25,6 +25,7 @@ Florian Forster Holger Weiss Ingmar Vanhassel J. David Ibáñez +Jacques Germishuys Jakob Pfender Jason Penny Jason R. McNeil diff --git a/CMakeLists.txt b/CMakeLists.txt index 23c5af1fc..918e5b8f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,6 +305,11 @@ ELSE () ENDIF () IF (APPLE) # Apple deprecated OpenSSL 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 () IF (PROFILE) SET(CMAKE_C_FLAGS "-pg ${CMAKE_C_FLAGS}") diff --git a/include/git2.h b/include/git2.h index 704fb585a..f74976061 100644 --- a/include/git2.h +++ b/include/git2.h @@ -14,6 +14,7 @@ #include "git2/branch.h" #include "git2/buffer.h" #include "git2/checkout.h" +#include "git2/cherrypick.h" #include "git2/clone.h" #include "git2/commit.h" #include "git2/common.h" diff --git a/include/git2/branch.h b/include/git2/branch.h index ad2a70b1f..f28410000 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -84,7 +84,7 @@ typedef struct git_branch_iterator git_branch_iterator; * @param repo Repository where to find the branches. * @param list_flags Filtering flags for the branch * listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE - * or a combination of the two. + * or GIT_BRANCH_ALL. * * @return 0 on success or an error code */ diff --git a/include/git2/cherrypick.h b/include/git2/cherrypick.h new file mode 100644 index 000000000..7c48e6659 --- /dev/null +++ b/include/git2/cherrypick.h @@ -0,0 +1,88 @@ +/* + * 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_git_cherrypick_h__ +#define INCLUDE_git_cherrypick_h__ + +#include "common.h" +#include "types.h" +#include "merge.h" + +/** + * @file git2/cherrypick.h + * @brief Git cherry-pick routines + * @defgroup git_cherrypick Git cherry-pick routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +typedef struct { + unsigned int version; + + /** For merge commits, the "mainline" is treated as the parent. */ + unsigned int mainline; + + git_merge_options merge_opts; + git_checkout_options checkout_opts; +} git_cherry_pick_options; + +#define GIT_CHERRY_PICK_OPTIONS_VERSION 1 +#define GIT_CHERRY_PICK_OPTIONS_INIT {GIT_CHERRY_PICK_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT} + +/** + * Initializes a `git_cherry_pick_options` with default values. Equivalent to + * creating an instance with GIT_CHERRY_PICK_OPTIONS_INIT. + * + * @param opts the `git_cherry_pick_options` instance to initialize. + * @param version the version of the struct; you should pass + * `GIT_CHERRY_PICK_OPTIONS_VERSION` here. + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_cherry_pick_init_opts( + git_cherry_pick_options* opts, + int version); + +/** + * Cherry-picks the given commit against the given "our" commit, producing an + * index that reflects the result of the cherry-pick. + * + * The returned index must be freed explicitly with `git_index_free`. + * + * @param out pointer to store the index result in + * @param repo the repository that contains the given commits + * @param cherry_pick_commit the commit to cherry-pick + * @param our_commit the commit to revert against (eg, HEAD) + * @param mainline the parent of the revert commit, if it is a merge + * @param merge_tree_opts the merge tree options (or null for defaults) + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_cherry_pick_commit( + git_index **out, + git_repository *repo, + git_commit *cherry_pick_commit, + git_commit *our_commit, + unsigned int mainline, + const git_merge_options *merge_options); + +/** + * Cherry-pick the given commit, producing changes in the index and working directory. + * + * @param repo the repository to cherry-pick + * @param commit the commit to cherry-pick + * @param cherry_pick_options the cherry-pick options (or null for defaults) + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_cherry_pick( + git_repository *repo, + git_commit *commit, + const git_cherry_pick_options *cherry_pick_options); + +/** @} */ +GIT_END_DECL + +#endif + diff --git a/include/git2/diff.h b/include/git2/diff.h index f855f52ba..a0cfbc918 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -1072,6 +1072,179 @@ GIT_EXTERN(int) git_diff_buffers( git_diff_line_cb line_cb, void *payload); +/** + * This is an opaque structure which is allocated by `git_diff_get_stats`. + * You are responsible for releasing the object memory when done, using the + * `git_diff_stats_free()` function. + */ +typedef struct git_diff_stats git_diff_stats; + +/** + * Formatting options for diff stats + */ +typedef enum { + /** No stats*/ + GIT_DIFF_STATS_NONE = 0, + + /** Full statistics, equivalent of `--stat` */ + GIT_DIFF_STATS_FULL = (1u << 0), + + /** Short statistics, equivalent of `--shortstat` */ + GIT_DIFF_STATS_SHORT = (1u << 1), + + /** Number statistics, equivalent of `--numstat` */ + GIT_DIFF_STATS_NUMBER = (1u << 2), + + /** Extended header information such as creations, renames and mode changes, equivalent of `--summary` */ + GIT_DIFF_STATS_INCLUDE_SUMMARY = (1u << 3), +} git_diff_stats_format_t; + +/** + * Accumlate diff statistics for all patches. + * + * @param out Structure containg the diff statistics. + * @param diff A git_diff generated by one of the above functions. + * @return 0 on success; non-zero on error + */ +GIT_EXTERN(int) git_diff_get_stats( + git_diff_stats **out, + git_diff *diff); + +/** + * Get the total number of files changed in a diff + * + * @param stats A `git_diff_stats` generated by one of the above functions. + * @return total number of files changed in the diff + */ +GIT_EXTERN(size_t) git_diff_stats_files_changed( + const git_diff_stats *stats); + +/** + * Get the total number of insertions in a diff + * + * @param stats A `git_diff_stats` generated by one of the above functions. + * @return total number of insertions in the diff + */ +GIT_EXTERN(size_t) git_diff_stats_insertions( + const git_diff_stats *stats); + +/** + * Get the total number of deletions in a diff + * + * @param stats A `git_diff_stats` generated by one of the above functions. + * @return total number of deletions in the diff + */ +GIT_EXTERN(size_t) git_diff_stats_deletions( + const git_diff_stats *stats); + +/** + * Print diff statistics to a `git_buf`. + * + * @param out buffer to store the formatted diff statistics in. + * @param stats A `git_diff_stats` generated by one of the above functions. + * @param format Formatting option. + * @return 0 on success; non-zero on error + */ +GIT_EXTERN(int) git_diff_stats_to_buf( + git_buf *out, + const git_diff_stats *stats, + git_diff_stats_format_t format); + +/** + * Deallocate a `git_diff_stats`. + * + * @param stats The previously created statistics object; + * cannot be used after free. + */ +GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats); + +/** + * Formatting options for diff e-mail generation + */ +typedef enum { + /** Normal patch, the default */ + GIT_DIFF_FORMAT_EMAIL_NONE = 0, + + /** Don't insert "[PATCH]" in the subject header*/ + GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0), + +} git_diff_format_email_flags_t; + +/** + * Options for controlling the formatting of the generated e-mail. + */ +typedef struct { + unsigned int version; + + git_diff_format_email_flags_t flags; + + /** This patch number */ + size_t patch_no; + + /** Total number of patches in this series */ + size_t total_patches; + + /** id to use for the commit */ + const git_oid *id; + + /** Summary of the change */ + const char *summary; + + /** Author of the change */ + const git_signature *author; +} git_diff_format_email_options; + +#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1 +#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL} + +/** + * Create an e-mail ready patch from a diff. + * + * @param out buffer to store the e-mail patch in + * @param diff containing the commit + * @param opts structure with options to influence content and formatting. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_diff_format_email( + git_buf *out, + git_diff *diff, + const git_diff_format_email_options *opts); + +/** + * Create an e-mail ready patch for a commit. + * + * Does not support creating patches for merge commits (yet). + * + * @param out buffer to store the e-mail patch in + * @param repo containing the commit + * @param commit pointer to up commit + * @param patch_no patch number of the commit + * @param total_patches total number of patches in the patch set + * @param flags determines the formatting of the e-mail + * @param diff_opts structure with options to influence diff or NULL for defaults. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_diff_commit_as_email( + git_buf *out, + git_repository *repo, + git_commit *commit, + size_t patch_no, + size_t total_patches, + git_diff_format_email_flags_t flags, + const git_diff_options *diff_opts); + +/** +* Initializes a `git_diff_format_email_options` with default values. Equivalent to +* creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT. +* +* @param opts the `git_diff_format_email_options` instance to initialize. +* @param version the version of the struct; you should pass +* `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION` here. +* @return Zero on success; -1 on failure. +*/ +GIT_EXTERN(int) git_diff_format_email_init_options( + git_diff_format_email_options *opts, + int version); GIT_END_DECL diff --git a/include/git2/errors.h b/include/git2/errors.h index bcf2f80ab..e22f0d86d 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -86,6 +86,7 @@ typedef enum { GITERR_FILTER, GITERR_REVERT, GITERR_CALLBACK, + GITERR_CHERRYPICK, } git_error_t; /** diff --git a/include/git2/index.h b/include/git2/index.h index dd6a28e40..05e58a632 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -77,7 +77,12 @@ typedef struct git_index_entry { #define GIT_IDXENTRY_VALID (0x8000) #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` @@ -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. - * 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 + * @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 @@ -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 index an existing index object * @param path path to search - * @return a zero-based position in the index if found; - * GIT_ENOTFOUND otherwise + * @return a zero-based position in the index if found; GIT_ENOTFOUND otherwise */ 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 index an existing index object * @param path path to search + * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_get( 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. * * @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); /** - * 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 + * @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. @@ -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); /** - * Create an iterator for the conflicts in the index. You may not modify the - * index while iterating, the results are undefined. + * Create an iterator for the conflicts in the index. + * + * 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 */ GIT_EXTERN(int) git_index_conflict_iterator_new( diff --git a/include/git2/patch.h b/include/git2/patch.h index f5ec682c6..47c395669 100644 --- a/include/git2/patch.h +++ b/include/git2/patch.h @@ -141,12 +141,12 @@ GIT_EXTERN(void) git_patch_free(git_patch *patch); /** * Get the delta associated with a patch */ -GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(git_patch *patch); +GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(const git_patch *patch); /** * Get the number of hunks in a patch */ -GIT_EXTERN(size_t) git_patch_num_hunks(git_patch *patch); +GIT_EXTERN(size_t) git_patch_num_hunks(const git_patch *patch); /** * Get line counts of each type in a patch. @@ -197,7 +197,7 @@ GIT_EXTERN(int) git_patch_get_hunk( * @return Number of lines in hunk or -1 if invalid hunk index */ GIT_EXTERN(int) git_patch_num_lines_in_hunk( - git_patch *patch, + const git_patch *patch, size_t hunk_idx); /** diff --git a/include/git2/sys/diff.h b/include/git2/sys/diff.h new file mode 100644 index 000000000..bc6cdf393 --- /dev/null +++ b/include/git2/sys/diff.h @@ -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 diff --git a/include/git2/types.h b/include/git2/types.h index 2229f6bf4..9db59b16b 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -193,6 +193,7 @@ typedef enum { typedef enum { GIT_BRANCH_LOCAL = 1, GIT_BRANCH_REMOTE = 2, + GIT_BRANCH_ALL = GIT_BRANCH_LOCAL|GIT_BRANCH_REMOTE, } git_branch_t; /** Valid modes for index and tree entries. */ diff --git a/src/attr.c b/src/attr.c index d8a171d0f..622874348 100644 --- a/src/attr.c +++ b/src/attr.c @@ -2,7 +2,7 @@ #include "repository.h" #include "sysdir.h" #include "config.h" -#include "attr.h" +#include "attr_file.h" #include "ignore.h" #include "git2/oid.h" #include <ctype.h> @@ -33,6 +33,7 @@ static int collect_attr_files( const char *path, git_vector *files); +static void release_attr_files(git_vector *files); int git_attr_get( const char **value, @@ -59,6 +60,7 @@ int git_attr_get( if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) goto cleanup; + memset(&attr, 0, sizeof(attr)); attr.name = name; attr.name_hash = git_attr_file__name_hash(name); @@ -76,7 +78,7 @@ int git_attr_get( } cleanup: - git_vector_free(&files); + release_attr_files(&files); git_attr_path__free(&path); return error; @@ -152,7 +154,7 @@ int git_attr_get_many( } cleanup: - git_vector_free(&files); + release_attr_files(&files); git_attr_path__free(&path); git__free(info); @@ -181,12 +183,10 @@ int git_attr_foreach( if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) 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; - seen = git_strmap_alloc(); - GITERR_CHECK_ALLOC(seen); - git_vector_foreach(&files, i, file) { git_attr_file__foreach_matching_rule(file, &path, j, rule) { @@ -211,13 +211,12 @@ int git_attr_foreach( cleanup: git_strmap_free(seen); - git_vector_free(&files); + release_attr_files(&files); git_attr_path__free(&path); return error; } - int git_attr_add_macro( git_repository *repo, const char *name, @@ -252,237 +251,6 @@ int git_attr_add_macro( 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 { git_repository *repo; uint32_t flags; @@ -491,7 +259,7 @@ typedef struct { git_vector *files; } 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) { int count = 0; @@ -499,42 +267,76 @@ int git_attr_cache__decide_sources( switch (flags & 0x03) { case GIT_ATTR_CHECK_FILE_THEN_INDEX: if (has_wd) - srcs[count++] = GIT_ATTR_FILE_FROM_FILE; + srcs[count++] = GIT_ATTR_FILE__FROM_FILE; if (has_index) - srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; break; case GIT_ATTR_CHECK_INDEX_THEN_FILE: if (has_index) - srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; if (has_wd) - srcs[count++] = GIT_ATTR_FILE_FROM_FILE; + srcs[count++] = GIT_ATTR_FILE__FROM_FILE; break; case GIT_ATTR_CHECK_INDEX_ONLY: if (has_index) - srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; break; } 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) { int error = 0, n_src, i; attr_walk_up_info *info = (attr_walk_up_info *)ref; 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); for (i = 0; !error && i < n_src; ++i) - error = git_attr_cache__push_file( - info->repo, path->ptr, GIT_ATTR_FILE, src[i], - git_attr_file__parse_buffer, NULL, info->files); + error = push_attr_file( + info->repo, info->files, src[i], path->ptr, GIT_ATTR_FILE); 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( git_repository *repo, uint32_t flags, @@ -546,9 +348,8 @@ static int collect_attr_files( const char *workdir = git_repository_workdir(repo); attr_walk_up_info info = { NULL }; - if (git_attr_cache__init(repo) < 0 || - git_vector_init(files, 4, NULL) < 0) - return -1; + if ((error = git_attr_cache__init(repo)) < 0) + return error; /* Resolve path in a non-bare repo */ if (workdir != NULL) @@ -566,7 +367,8 @@ static int collect_attr_files( */ 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) goto cleanup; @@ -583,7 +385,8 @@ static int collect_attr_files( if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { 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) goto cleanup; } @@ -591,7 +394,8 @@ static int collect_attr_files( if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); 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) { giterr_clear(); error = 0; @@ -600,153 +404,8 @@ static int collect_attr_files( cleanup: if (error < 0) - git_vector_free(files); + release_attr_files(files); git_buf_free(&dir); 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); -} - diff --git a/src/attr.h b/src/attr.h index 19c979bcd..f9f216d07 100644 --- a/src/attr.h +++ b/src/attr.h @@ -8,38 +8,6 @@ #define INCLUDE_attr_h__ #include "attr_file.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); +#include "attrcache.h" #endif diff --git a/src/attr_file.c b/src/attr_file.c index 4eb732436..65bbf78e8 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -1,86 +1,239 @@ #include "common.h" #include "repository.h" #include "filebuf.h" -#include "attr.h" +#include "attr_file.h" +#include "attrcache.h" #include "git2/blob.h" #include "git2/tree.h" +#include "index.h" #include <ctype.h> -static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); -static void git_attr_rule__clear(git_attr_rule *rule); -static bool parse_optimized_patterns( - git_attr_fnmatch *spec, - git_pool *pool, - const char *pattern); +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 **attrs_ptr, - git_attr_file_source from, - const char *path, - git_pool *pool) + git_attr_file **out, + git_attr_file_entry *entry, + git_attr_file_source source) { - git_attr_file *attrs = NULL; - - attrs = git__calloc(1, sizeof(git_attr_file)); + git_attr_file *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 (git_mutex_init(&attrs->lock) < 0) { + giterr_set(GITERR_OS, "Failed to initialize lock"); + git__free(attrs); + return -1; } - if (path) { - size_t len = strlen(path); + 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; +} - attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3); - GITERR_CHECK_ALLOC(attrs->key); +int git_attr_file__clear_rules(git_attr_file *file, bool need_lock) +{ + unsigned int i; + git_attr_rule *rule; - attrs->key[0] = '0' + (char)from; - attrs->key[1] = '#'; - memcpy(&attrs->key[2], path, len); - attrs->key[len + 2] = '\0'; + if (need_lock && git_mutex_lock(&file->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock attribute file"); + return -1; } - if (git_vector_init(&attrs->rules, 4, NULL) < 0) - goto fail; + 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); - *attrs_ptr = attrs; return 0; +} -fail: - git_attr_file__free(attrs); - attrs_ptr = NULL; - return -1; +void git_attr_file__free(git_attr_file *file) +{ + if (!file) + return; + GIT_REFCOUNT_DEC(file, attr_file_free); } -int git_attr_file__parse_buffer( - git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs) +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; - const char *scan = NULL; - char *context = NULL; - git_attr_rule *rule = NULL; + 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); + } - GIT_UNUSED(parsedata); + default: + giterr_set(GITERR_INVALID, "Invalid file type %d", file->source); + return -1; + } +} - assert(buffer && attrs); +static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); +static void git_attr_rule__clear(git_attr_rule *rule); +static bool parse_optimized_patterns( + git_attr_fnmatch *spec, + git_pool *pool, + const char *pattern); - scan = buffer; +int git_attr_file__parse_buffer( + git_repository *repo, git_attr_file *attrs, const char *data) +{ + int error = 0; + const char *scan = data, *context = NULL; + git_attr_rule *rule = NULL; /* if subdir file path, convert context for file paths */ - if (attrs->key && git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) { - context = attrs->key + 2; - context[strlen(context) - strlen(GIT_ATTR_FILE)] = '\0'; + if (attrs->entry && + git_path_root(attrs->entry->path) < 0 && + !git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE)) + 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) { /* allocate rule if needed */ if (!rule) { - if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) { + if (!(rule = git__calloc(1, sizeof(*rule)))) { error = -1; break; } @@ -90,9 +243,9 @@ int git_attr_file__parse_buffer( /* parse the next "pattern attr attr attr" line */ if (!(error = git_attr_fnmatch__parse( - &rule->match, attrs->pool, context, &scan)) && + &rule->match, &attrs->pool, context, &scan)) && !(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) /* should generate error/warning if this is coming from any @@ -113,66 +266,12 @@ int git_attr_file__parse_buffer( } } + git_mutex_unlock(&attrs->lock); git_attr_rule__free(rule); - /* restore file path used for context */ - if (context) - context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */ - 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 h = 5381; @@ -183,7 +282,6 @@ uint32_t git_attr_file__name_hash(const char *name) return h; } - int git_attr_file__lookup_one( git_attr_file *file, const git_attr_path *path, @@ -212,25 +310,63 @@ int git_attr_file__lookup_one( 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( git_attr_fnmatch *match, const git_attr_path *path) { - int fnm; - int icase_flags = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? FNM_CASEFOLD : 0; + const char *filename; + 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; - if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) - fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME | icase_flags); - 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); + if (match->flags & GIT_ATTR_FNMATCH_ICASE) + flags |= FNM_CASEFOLD; + + if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) { + filename = path->path; + flags |= FNM_PATHNAME; + } else { + filename = path->basename; - return (fnm == FNM_NOMATCH) ? false : true; + if (path->is_dir) + flags |= FNM_LEADING_DIR; + } + + return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH); } bool git_attr_rule__match( @@ -245,7 +381,6 @@ bool git_attr_rule__match( return matched; } - git_attr_assignment *git_attr_rule__lookup_assignment( git_attr_rule *rule, const char *name) { @@ -344,7 +479,7 @@ void git_attr_path__free(git_attr_path *info) int git_attr_fnmatch__parse( git_attr_fnmatch *spec, git_pool *pool, - const char *source, + const char *context, const char **base) { const char *pattern, *scan; @@ -412,18 +547,21 @@ int git_attr_fnmatch__parse( } if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 && - source != NULL && git_path_root(pattern) < 0) + context != NULL && git_path_root(pattern) < 0) { - size_t sourcelen = strlen(source); + /* use context path minus the trailing filename */ + char *slash = strrchr(context, '/'); + size_t contextlen = slash ? slash - context + 1 : 0; + /* given an unrooted fullpath match from a file inside a repo, * prefix the pattern with the relative directory of the source file */ spec->pattern = git_pool_malloc( - pool, (uint32_t)(sourcelen + spec->length + 1)); + pool, (uint32_t)(contextlen + spec->length + 1)); if (spec->pattern) { - memcpy(spec->pattern, source, sourcelen); - memcpy(spec->pattern + sourcelen, pattern, spec->length); - spec->length += sourcelen; + memcpy(spec->pattern, context, contextlen); + memcpy(spec->pattern + contextlen, pattern, spec->length); + spec->length += contextlen; spec->pattern[spec->length] = '\0'; } } else { @@ -436,6 +574,7 @@ int git_attr_fnmatch__parse( } else { /* strip '\' that might have be used for internal whitespace */ spec->length = git__unescape(spec->pattern); + /* TODO: convert remaining '\' into '/' for POSIX ??? */ } return 0; diff --git a/src/attr_file.h b/src/attr_file.h index 3bc7c6cb8..c906be44d 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -35,6 +35,14 @@ (GIT_ATTR_FNMATCH_ALLOWSPACE | \ 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__false; extern const char *git_attr__unset; @@ -63,17 +71,32 @@ typedef struct { const char *value; } git_attr_assignment; +typedef struct git_attr_file_entry git_attr_file_entry; + typedef struct { - char *key; /* cache "source#path" this was loaded from */ - git_vector rules; /* vector of <rule*> or <fnmatch*> */ - git_pool *pool; - bool pool_is_allocated; + 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_pool pool; union { git_oid oid; git_futils_filestamp stamp; } cache_data; } git_attr_file; +struct git_attr_file_entry { + git_attr_file *file[GIT_ATTR_FILE_NUM_SOURCES]; + const char *path; /* points into fullpath */ + char fullpath[GIT_FLEX_ARRAY]; +}; + +typedef int (*git_attr_file_parser)( + git_repository *repo, + git_attr_file *file, + const char *data); + typedef struct { git_buf full; char *path; @@ -81,29 +104,37 @@ typedef struct { int is_dir; } git_attr_path; -typedef enum { - GIT_ATTR_FILE_FROM_FILE = 0, - GIT_ATTR_FILE_FROM_INDEX = 1 -} git_attr_file_source; - /* * git_attr_file API */ -extern int git_attr_file__new( - git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool); +int git_attr_file__new( + git_attr_file **out, + git_attr_file_entry *entry, + git_attr_file_source source); + +void git_attr_file__free(git_attr_file *file); + +int git_attr_file__load( + git_attr_file **out, + git_repository *repo, + git_attr_file_entry *ce, + git_attr_file_source source, + git_attr_file_parser parser); -extern int git_attr_file__new_and_load( - git_attr_file **attrs_ptr, const char *path); +int git_attr_file__load_standalone( + git_attr_file **out, const char *path); -extern void git_attr_file__free(git_attr_file *file); +int git_attr_file__out_of_date( + git_repository *repo, git_attr_file *file); -extern void git_attr_file__clear_rules(git_attr_file *file); +int git_attr_file__parse_buffer( + git_repository *repo, git_attr_file *attrs, const char *data); -extern int git_attr_file__parse_buffer( - git_repository *repo, void *parsedata, const char *buf, git_attr_file *file); +int git_attr_file__clear_rules( + git_attr_file *file, bool need_lock); -extern int git_attr_file__lookup_one( +int git_attr_file__lookup_one( git_attr_file *file, const git_attr_path *path, const char *attr, @@ -114,7 +145,7 @@ extern int git_attr_file__lookup_one( git_vector_rforeach(&(file)->rules, (iter), (rule)) \ 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); /* diff --git a/src/attrcache.c b/src/attrcache.c new file mode 100644 index 000000000..88b68ebb9 --- /dev/null +++ b/src/attrcache.c @@ -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); +} + diff --git a/src/attrcache.h b/src/attrcache.h index 077633b87..be0a22f5c 100644 --- a/src/attrcache.h +++ b/src/attrcache.h @@ -7,18 +7,50 @@ #ifndef INCLUDE_attrcache_h__ #define INCLUDE_attrcache_h__ -#include "pool.h" +#include "attr_file.h" #include "strmap.h" +#define GIT_ATTR_CONFIG "core.attributesfile" +#define GIT_IGNORE_CONFIG "core.excludesfile" + 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_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; -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 diff --git a/src/buffer.c b/src/buffer.c index 83960e912..f6e34a445 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -148,6 +148,15 @@ int git_buf_putc(git_buf *buf, char c) return 0; } +int git_buf_putcn(git_buf *buf, char c, size_t len) +{ + ENSURE_SIZE(buf, buf->size + len + 1); + memset(buf->ptr + buf->size, c, len); + buf->size += len; + buf->ptr[buf->size] = '\0'; + return 0; +} + int git_buf_put(git_buf *buf, const char *data, size_t len) { ENSURE_SIZE(buf, buf->size + len + 1); diff --git a/src/buffer.h b/src/buffer.h index dba594d97..398aec9b7 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -88,6 +88,7 @@ GIT_INLINE(bool) git_buf_oom(const git_buf *buf) */ int git_buf_sets(git_buf *buf, const char *string); int git_buf_putc(git_buf *buf, char c); +int git_buf_putcn(git_buf *buf, char c, size_t len); int git_buf_put(git_buf *buf, const char *data, size_t len); int git_buf_puts(git_buf *buf, const char *string); int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3); diff --git a/src/checkout.c b/src/checkout.c index 141fc1331..0b385226b 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -277,19 +277,23 @@ static int checkout_action_wd_only( /* check if item is tracked in the index but not in the checkout diff */ 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 (!(error = git_index_find(NULL, data->index, wd->path))) { + if (!error) { /* found by git_index__find_pos call */ notify = GIT_CHECKOUT_NOTIFY_DIRTY; remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0); } else if (error != GIT_ENOTFOUND) return error; else - giterr_clear(); + error = 0; /* git_index__find_pos does not set error msg */ } else { /* for tree entries, we have to see if there are any index * 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); 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; } -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; + GIT_UNUSED(payload); + if ((conflict = git_vector_get(conflicts, idx)) == NULL) return -1; @@ -954,7 +961,8 @@ static int checkout_conflicts_coalesce_renames( 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: return error; diff --git a/src/cherrypick.c b/src/cherrypick.c new file mode 100644 index 000000000..6a5ca834c --- /dev/null +++ b/src/cherrypick.c @@ -0,0 +1,230 @@ +/* +* 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. +*/ + +#include "common.h" +#include "repository.h" +#include "filebuf.h" +#include "merge.h" +#include "vector.h" + +#include "git2/types.h" +#include "git2/merge.h" +#include "git2/cherrypick.h" +#include "git2/commit.h" +#include "git2/sys/commit.h" + +#define GIT_CHERRY_PICK_FILE_MODE 0666 + +static int write_cherry_pick_head( + git_repository *repo, + const char *commit_oidstr) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + int error = 0; + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRY_PICK_HEAD_FILE)) >= 0 && + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRY_PICK_FILE_MODE)) >= 0 && + (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) + error = git_filebuf_commit(&file); + + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +static int write_merge_msg( + git_repository *repo, + const char *commit_msg) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + int error = 0; + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRY_PICK_FILE_MODE)) < 0 || + (error = git_filebuf_printf(&file, "%s", commit_msg)) < 0) + goto cleanup; + + error = git_filebuf_commit(&file); + +cleanup: + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +static int cherry_pick_normalize_opts( + git_repository *repo, + git_cherry_pick_options *opts, + const git_cherry_pick_options *given, + const char *their_label) +{ + int error = 0; + unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE_CREATE | + GIT_CHECKOUT_ALLOW_CONFLICTS; + + GIT_UNUSED(repo); + + if (given != NULL) + memcpy(opts, given, sizeof(git_cherry_pick_options)); + else { + git_cherry_pick_options default_opts = GIT_CHERRY_PICK_OPTIONS_INIT; + memcpy(opts, &default_opts, sizeof(git_cherry_pick_options)); + } + + if (!opts->checkout_opts.checkout_strategy) + opts->checkout_opts.checkout_strategy = default_checkout_strategy; + + if (!opts->checkout_opts.our_label) + opts->checkout_opts.our_label = "HEAD"; + + if (!opts->checkout_opts.their_label) + opts->checkout_opts.their_label = their_label; + + return error; +} + +static int cherry_pick_state_cleanup(git_repository *repo) +{ + const char *state_files[] = { GIT_CHERRY_PICK_HEAD_FILE, GIT_MERGE_MSG_FILE }; + + return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); +} + +static int cherry_pick_seterr(git_commit *commit, const char *fmt) +{ + char commit_oidstr[GIT_OID_HEXSZ + 1]; + + giterr_set(GITERR_CHERRYPICK, fmt, + git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit))); + + return -1; +} + +int git_cherry_pick_commit( + git_index **out, + git_repository *repo, + git_commit *cherry_pick_commit, + git_commit *our_commit, + unsigned int mainline, + const git_merge_options *merge_opts) +{ + git_commit *parent_commit = NULL; + git_tree *parent_tree = NULL, *our_tree = NULL, *cherry_pick_tree = NULL; + int parent = 0, error = 0; + + assert(out && repo && cherry_pick_commit && our_commit); + + if (git_commit_parentcount(cherry_pick_commit) > 1) { + if (!mainline) + return cherry_pick_seterr(cherry_pick_commit, + "Mainline branch is not specified but %s is a merge commit"); + + parent = mainline; + } else { + if (mainline) + return cherry_pick_seterr(cherry_pick_commit, + "Mainline branch specified but %s is not a merge commit"); + + parent = git_commit_parentcount(cherry_pick_commit); + } + + if (parent && + ((error = git_commit_parent(&parent_commit, cherry_pick_commit, (parent - 1))) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0)) + goto done; + + if ((error = git_commit_tree(&cherry_pick_tree, cherry_pick_commit)) < 0 || + (error = git_commit_tree(&our_tree, our_commit)) < 0) + goto done; + + error = git_merge_trees(out, repo, parent_tree, our_tree, cherry_pick_tree, merge_opts); + +done: + git_tree_free(parent_tree); + git_tree_free(our_tree); + git_tree_free(cherry_pick_tree); + git_commit_free(parent_commit); + + return error; +} + +int git_cherry_pick( + git_repository *repo, + git_commit *commit, + const git_cherry_pick_options *given_opts) +{ + git_cherry_pick_options opts; + git_reference *our_ref = NULL; + git_commit *our_commit = NULL; + char commit_oidstr[GIT_OID_HEXSZ + 1]; + const char *commit_msg, *commit_summary; + git_buf their_label = GIT_BUF_INIT; + git_index *index_new = NULL, *index_repo = NULL; + int error = 0; + + assert(repo && commit); + + GITERR_CHECK_VERSION(given_opts, GIT_CHERRY_PICK_OPTIONS_VERSION, "git_cherry_pick_options"); + + if ((error = git_repository__ensure_not_bare(repo, "cherry-pick")) < 0) + return error; + + if ((commit_msg = git_commit_message(commit)) == NULL || + (commit_summary = git_commit_summary(commit)) == NULL) { + error = -1; + goto on_error; + } + + git_oid_nfmt(commit_oidstr, sizeof(commit_oidstr), git_commit_id(commit)); + + if ((error = write_merge_msg(repo, commit_msg)) < 0 || + (error = git_buf_printf(&their_label, "%.7s... %s", commit_oidstr, commit_summary)) < 0 || + (error = cherry_pick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 || + (error = write_cherry_pick_head(repo, commit_oidstr)) < 0 || + (error = git_repository_head(&our_ref, repo)) < 0 || + (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 || + (error = git_cherry_pick_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 || + (error = git_merge__indexes(repo, index_new)) < 0 || + (error = git_repository_index(&index_repo, repo)) < 0 || + (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 || + (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0) + goto on_error; + + goto done; + +on_error: + cherry_pick_state_cleanup(repo); + +done: + git_index_free(index_new); + git_index_free(index_repo); + git_commit_free(our_commit); + git_reference_free(our_ref); + git_buf_free(&their_label); + + return error; +} + +int git_cherry_pick_init_opts(git_cherry_pick_options* opts, int version) +{ + if (version != GIT_CHERRY_PICK_OPTIONS_VERSION) { + giterr_set(GITERR_INVALID, "Invalid version %d for git_cherry_pick_options", version); + return -1; + } else { + git_cherry_pick_options o = GIT_CHERRY_PICK_OPTIONS_INIT; + memcpy(opts, &o, sizeof(o)); + return 0; + } +} diff --git a/src/common.h b/src/common.h index d389cf85d..9c8bdc18a 100644 --- a/src/common.h +++ b/src/common.h @@ -46,6 +46,7 @@ # include <unistd.h> # ifdef GIT_THREADS # include <pthread.h> +# include <sched.h> # endif #define GIT_STDLIB_CALL diff --git a/src/config_file.c b/src/config_file.c index aedf2cb12..bb26aa8a3 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -180,11 +180,15 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) b->level = level; - b->values = git_strmap_alloc(); - GITERR_CHECK_ALLOC(b->values); + if ((res = git_strmap_alloc(&b->values)) < 0) + return res; git_array_init(b->readers); reader = git_array_alloc(b->readers); + if (!reader) { + git_strmap_free(b->values); + return -1; + } memset(reader, 0, sizeof(struct reader)); 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); git_buf_free(&reader->buffer); + return res; } @@ -218,8 +223,10 @@ static int config_refresh(git_config_backend *cfg) for (i = 0; i < git_array_size(b->readers); i++) { reader = git_array_get(b->readers, i); + 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) 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 */ old_values = b->values; - b->values = git_strmap_alloc(); - GITERR_CHECK_ALLOC(b->values); - - if ((res = config_parse(b, reader, b->level, 0)) < 0) { + if ((res = git_strmap_alloc(&b->values)) < 0) { + b->values = old_values; + } else if ((res = config_parse(b, reader, b->level, 0)) < 0) { free_vars(b->values); b->values = old_values; } else { diff --git a/src/date.c b/src/date.c index 7849c2f02..0e1b31aee 100644 --- a/src/date.c +++ b/src/date.c @@ -874,3 +874,31 @@ int git__date_parse(git_time_t *out, const char *date) *out = approxidate_str(date, time_sec, &error_ret); return error_ret; } + +int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date) +{ + int written; + struct tm gmt; + time_t t; + + assert(out && date); + + t = (time_t) (date->time + date->offset * 60); + + if (p_gmtime_r (&t, &gmt) == NULL) + return -1; + + written = p_snprintf(out, len, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d", + weekday_names[gmt.tm_wday], + gmt.tm_mday, + month_names[gmt.tm_mon], + gmt.tm_year + 1900, + gmt.tm_hour, gmt.tm_min, gmt.tm_sec, + date->offset / 60, date->offset % 60); + + if (written < 0 || (written > (int) len - 1)) + return -1; + + return 0; +} + diff --git a/src/diff.c b/src/diff.c index 484273f4a..0d1aed4ad 100644 --- a/src/diff.c +++ b/src/diff.c @@ -318,6 +318,31 @@ static const char *diff_mnemonic_prefix( 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( git_repository *repo, git_iterator *old_iter, @@ -344,24 +369,10 @@ static git_diff *diff_list_alloc( /* Use case-insensitive compare if either iterator has * the ignore_case bit set */ - if (!git_iterator_ignore_case(old_iter) && - !git_iterator_ignore_case(new_iter)) { - 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; - } 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); - } + diff_set_ignore_case( + diff, + git_iterator_ignore_case(old_iter) || + git_iterator_ignore_case(new_iter)); return diff; } @@ -876,7 +887,7 @@ static int handle_unmatched_new_item( DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)); /* do not advance into directories that contain a .git file */ - if (recurse_into_dir) { + if (recurse_into_dir && !contains_oitem) { git_buf *full = NULL; if (git_iterator_current_workdir_path(&full, info->new_iter) < 0) return -1; @@ -969,6 +980,16 @@ static int handle_unmatched_new_item( if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) { giterr_clear(); delta_type = GIT_DELTA_IGNORED; + + /* if this contains a tracked item, treat as normal TREE */ + if (contains_oitem) { + error = git_iterator_advance_into(&info->nitem, info->new_iter); + if (error != GIT_ENOTFOUND) + return error; + + giterr_clear(); + return git_iterator_advance(&info->nitem, info->new_iter); + } } } @@ -1173,39 +1194,25 @@ int git_diff_tree_to_index( const git_diff_options *opts) { int error = 0; - bool reset_index_ignore_case = false; + bool index_ignore_case = false; assert(diff && repo); if (!index && (error = diff_load_index(&index, repo)) < 0) return error; - if (index->ignore_case) { - git_index__set_ignore_case(index, false); - reset_index_ignore_case = true; - } + index_ignore_case = index->ignore_case; DIFF_FROM_ITERATORS( - git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), - git_iterator_for_index(&b, index, 0, pfx, pfx) + git_iterator_for_tree( + &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) { - git_index__set_ignore_case(index, 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); - } - } + /* if index is in case-insensitive order, re-sort deltas to match */ + if (!error && index_ignore_case) + diff_set_ignore_case(*diff, true); return error; } @@ -1417,6 +1424,216 @@ int git_diff__paired_foreach( return error; } +int git_diff__commit( + git_diff **diff, + git_repository *repo, + const git_commit *commit, + const git_diff_options *opts) +{ + git_commit *parent = NULL; + git_diff *commit_diff = NULL; + git_tree *old_tree = NULL, *new_tree = NULL; + size_t parents; + int error = 0; + + if ((parents = git_commit_parentcount(commit)) > 1) { + char commit_oidstr[GIT_OID_HEXSZ + 1]; + + error = -1; + giterr_set(GITERR_INVALID, "Commit %s is a merge commit", + git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit))); + goto on_error; + } + + if (parents > 0) + if ((error = git_commit_parent(&parent, commit, 0)) < 0 || + (error = git_commit_tree(&old_tree, parent)) < 0) + goto on_error; + + if ((error = git_commit_tree(&new_tree, commit)) < 0 || + (error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0) + goto on_error; + + *diff = commit_diff; + +on_error: + git_tree_free(new_tree); + git_tree_free(old_tree); + git_commit_free(parent); + + return error; +} + +int git_diff_format_email__append_header_tobuf( + git_buf *out, + const git_oid *id, + const git_signature *author, + const char *summary, + size_t patch_no, + size_t total_patches, + bool exclude_patchno_marker) +{ + char idstr[GIT_OID_HEXSZ + 1]; + char date_str[GIT_DATE_RFC2822_SZ]; + int error = 0; + + git_oid_fmt(idstr, id); + idstr[GIT_OID_HEXSZ] = '\0'; + + if ((error = git__date_rfc2822_fmt(date_str, sizeof(date_str), &author->when)) < 0) + return error; + + error = git_buf_printf(out, + "From %s Mon Sep 17 00:00:00 2001\n" \ + "From: %s <%s>\n" \ + "Date: %s\n" \ + "Subject: ", + idstr, + author->name, author->email, + date_str); + + if (error < 0) + return error; + + if (!exclude_patchno_marker) { + if (total_patches == 1) { + error = git_buf_puts(out, "[PATCH] "); + } else { + error = git_buf_printf(out, "[PATCH %"PRIuZ"/%"PRIuZ"] ", patch_no, total_patches); + } + + if (error < 0) + return error; + } + + error = git_buf_printf(out, "%s\n\n", summary); + + return error; +} + +int git_diff_format_email__append_patches_tobuf( + git_buf *out, + git_diff *diff) +{ + size_t i, deltas; + int error = 0; + + deltas = git_diff_num_deltas(diff); + + for (i = 0; i < deltas; ++i) { + git_patch *patch = NULL; + + if ((error = git_patch_from_diff(&patch, diff, i)) >= 0) + error = git_patch_to_buf(out, patch); + + git_patch_free(patch); + + if (error < 0) + break; + } + + return error; +} + +int git_diff_format_email( + git_buf *out, + git_diff *diff, + const git_diff_format_email_options *opts) +{ + git_diff_stats *stats = NULL; + char *summary = NULL, *loc = NULL; + bool ignore_marker; + unsigned int format_flags = 0; + int error; + + assert(out && diff && opts); + assert(opts->summary && opts->id && opts->author); + + GITERR_CHECK_VERSION(opts, GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, "git_format_email_options"); + + if ((ignore_marker = opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) == false) { + if (opts->patch_no > opts->total_patches) { + giterr_set(GITERR_INVALID, "patch %"PRIuZ" out of range. max %"PRIuZ, opts->patch_no, opts->total_patches); + return -1; + } + + if (opts->patch_no == 0) { + giterr_set(GITERR_INVALID, "invalid patch no %"PRIuZ". should be >0", opts->patch_no); + return -1; + } + } + + /* the summary we receive may not be clean. + * it could potentially contain new line characters + * or not be set, sanitize, */ + if ((loc = strpbrk(opts->summary, "\r\n")) != NULL) { + size_t offset = 0; + + if ((offset = (loc - opts->summary)) == 0) { + giterr_set(GITERR_INVALID, "summary is empty"); + error = -1; + } + + summary = git__calloc(offset + 1, sizeof(char)); + GITERR_CHECK_ALLOC(summary); + strncpy(summary, opts->summary, offset); + } + + error = git_diff_format_email__append_header_tobuf(out, + opts->id, opts->author, summary == NULL ? opts->summary : summary, + opts->patch_no, opts->total_patches, ignore_marker); + + if (error < 0) + goto on_error; + + format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY; + + if ((error = git_buf_puts(out, "---\n")) < 0 || + (error = git_diff_get_stats(&stats, diff)) < 0 || + (error = git_diff_stats_to_buf(out, stats, format_flags)) < 0 || + (error = git_diff_format_email__append_patches_tobuf(out, diff)) < 0) + goto on_error; + + error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n"); + +on_error: + git__free(summary); + git_diff_stats_free(stats); + + return error; +} + +int git_diff_commit_as_email( + git_buf *out, + git_repository *repo, + git_commit *commit, + size_t patch_no, + size_t total_patches, + git_diff_format_email_flags_t flags, + const git_diff_options *diff_opts) +{ + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + int error; + + assert (out && repo && commit); + + opts.flags = flags; + opts.patch_no = patch_no; + opts.total_patches = total_patches; + opts.id = git_commit_id(commit); + opts.summary = git_commit_summary(commit); + opts.author = git_commit_author(commit); + + if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0) + return error; + + error = git_diff_format_email(out, diff, &opts); + + git_diff_free(diff); + return error; +} + int git_diff_init_options(git_diff_options* opts, int version) { if (version != GIT_DIFF_OPTIONS_VERSION) { @@ -1440,3 +1657,15 @@ int git_diff_find_init_options(git_diff_find_options* opts, int version) return 0; } } + +int git_diff_format_email_init_options(git_diff_format_email_options* opts, int version) +{ + if (version != GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION) { + giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_format_email_options", version); + return -1; + } else { + git_diff_format_email_options o = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + memcpy(opts, &o, sizeof(o)); + return 0; + } +} diff --git a/src/diff.h b/src/diff.h index c588f6301..aae8fbff1 100644 --- a/src/diff.h +++ b/src/diff.h @@ -116,6 +116,9 @@ extern void git_diff_find_similar__hashsig_free(void *sig, void *payload); extern int git_diff_find_similar__calc_similarity( int *score, void *siga, void *sigb, void *payload); +extern int git_diff__commit( + git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts); + /* * Sometimes a git_diff_file will have a zero size; this attempts to * fill in the size without loading the blob if possible. If that is diff --git a/src/diff_driver.c b/src/diff_driver.c index 4c9a0af65..8136e0dd9 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -66,7 +66,7 @@ git_diff_driver_registry *git_diff_driver_registry_new() if (!reg) return NULL; - if ((reg->drivers = git_strmap_alloc()) == NULL) { + if (git_strmap_alloc(®->drivers) < 0) { git_diff_driver_registry_free(reg); return NULL; } diff --git a/src/diff_patch.c b/src/diff_patch.c index dd8b73938..38d5f4257 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -631,13 +631,13 @@ void git_patch_free(git_patch *patch) GIT_REFCOUNT_DEC(patch, diff_patch_free); } -const git_diff_delta *git_patch_get_delta(git_patch *patch) +const git_diff_delta *git_patch_get_delta(const git_patch *patch) { assert(patch); return patch->delta; } -size_t git_patch_num_hunks(git_patch *patch) +size_t git_patch_num_hunks(const git_patch *patch) { assert(patch); return git_array_size(patch->hunks); @@ -708,7 +708,7 @@ int git_patch_get_hunk( return 0; } -int git_patch_num_lines_in_hunk(git_patch *patch, size_t hunk_idx) +int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx) { diff_patch_hunk *hunk; assert(patch); diff --git a/src/diff_print.c b/src/diff_print.c index 1a09bed54..a7f7b6fe8 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -8,6 +8,7 @@ #include "diff.h" #include "diff_patch.h" #include "fileops.h" +#include "git2/sys/diff.h" typedef struct { git_diff *diff; @@ -435,7 +436,7 @@ int git_patch_print( return error; } -static int diff_print_to_buffer_cb( +int git_diff_print_callback__to_buf( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, @@ -444,6 +445,11 @@ static int diff_print_to_buffer_cb( git_buf *output = payload; 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 || line->origin == GIT_DIFF_LINE_DELETION || 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); } +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 */ int git_patch_to_buf( git_buf *out, 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); } diff --git a/src/diff_stats.c b/src/diff_stats.c new file mode 100644 index 000000000..bb436bf7b --- /dev/null +++ b/src/diff_stats.c @@ -0,0 +1,343 @@ +/* + * 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. + */ +#include "common.h" +#include "vector.h" +#include "diff.h" +#include "diff_patch.h" + +#define DIFF_RENAME_FILE_SEPARATOR " => " + +struct git_diff_stats { + git_vector patches; + + size_t files_changed; + size_t insertions; + size_t deletions; +}; + +static size_t diff_get_filename_padding( + int has_renames, + const git_diff_stats *stats) +{ + const git_patch *patch = NULL; + size_t i, max_padding = 0; + + if (has_renames) { + git_vector_foreach(&stats->patches, i, patch) { + const git_diff_delta *delta = NULL; + size_t len; + + delta = git_patch_get_delta(patch); + if (strcmp(delta->old_file.path, delta->new_file.path) == 0) + continue; + + if ((len = strlen(delta->old_file.path) + strlen(delta->new_file.path)) > max_padding) + max_padding = len; + } + } + + git_vector_foreach(&stats->patches, i, patch) { + const git_diff_delta *delta = NULL; + size_t len; + + delta = git_patch_get_delta(patch); + len = strlen(delta->new_file.path); + + if (strcmp(delta->old_file.path, delta->new_file.path) != 0) + continue; + + if (len > max_padding) + max_padding = len; + } + + return max_padding; +} + +int git_diff_file_stats__full_to_buf( + git_buf *out, + size_t max_padding, + int has_renames, + const git_patch *patch) +{ + const char *old_path = NULL, *new_path = NULL; + const git_diff_delta *delta = NULL; + size_t padding, old_size, new_size; + int error; + + delta = git_patch_get_delta(patch); + + old_path = delta->old_file.path; + new_path = delta->new_file.path; + old_size = delta->old_file.size; + new_size = delta->new_file.size; + + if ((error = git_buf_printf(out, " %s", old_path)) < 0) + goto on_error; + + if (strcmp(old_path, new_path) != 0) { + padding = max_padding - strlen(old_path) - strlen(new_path); + + if ((error = git_buf_printf(out, DIFF_RENAME_FILE_SEPARATOR "%s", new_path)) < 0) + goto on_error; + } + else { + padding = max_padding - strlen(old_path); + + if (has_renames) + padding += strlen(DIFF_RENAME_FILE_SEPARATOR); + } + + if ((error = git_buf_putcn(out, ' ', padding)) < 0 || + (error = git_buf_puts(out, " | ")) < 0) + goto on_error; + + if (delta->flags & GIT_DIFF_FLAG_BINARY) { + if ((error = git_buf_printf(out, "Bin %" PRIuZ " -> %" PRIuZ " bytes", old_size, new_size)) < 0) + goto on_error; + } + else { + size_t insertions, deletions; + + if ((error = git_patch_line_stats(NULL, &insertions, &deletions, patch)) < 0) + goto on_error; + + if ((error = git_buf_printf(out, "%" PRIuZ, insertions + deletions)) < 0) + goto on_error; + + if (insertions || deletions) { + if ((error = git_buf_putc(out, ' ')) < 0 || + (error = git_buf_putcn(out, '+', insertions)) < 0 || + (error = git_buf_putcn(out, '-', deletions)) < 0) + goto on_error; + } + } + + error = git_buf_putc(out, '\n'); + +on_error: + return error; +} + +int git_diff_file_stats__number_to_buf( + git_buf *out, + const git_patch *patch) +{ + const git_diff_delta *delta = NULL; + const char *path = NULL; + size_t insertions, deletions; + int error; + + delta = git_patch_get_delta(patch); + path = delta->new_file.path; + + if ((error = git_patch_line_stats(NULL, &insertions, &deletions, patch)) < 0) + return error; + + if (delta->flags & GIT_DIFF_FLAG_BINARY) + error = git_buf_printf(out, "%-8c" "%-8c" "%s\n", '-', '-', path); + else + error = git_buf_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n", insertions, deletions, path); + + return error; +} + +int git_diff_file_stats__summary_to_buf( + git_buf *out, + const git_patch *patch) +{ + const git_diff_delta *delta = NULL; + + delta = git_patch_get_delta(patch); + + if (delta->old_file.mode != delta->new_file.mode) { + if (delta->old_file.mode == 0) { + git_buf_printf(out, " create mode %06o %s\n", + delta->new_file.mode, delta->new_file.path); + } + else if (delta->new_file.mode == 0) { + git_buf_printf(out, " delete mode %06o %s\n", + delta->old_file.mode, delta->old_file.path); + } + else { + git_buf_printf(out, " mode change %06o => %06o %s\n", + delta->old_file.mode, delta->new_file.mode, delta->new_file.path); + } + } + + return 0; +} + +int git_diff_stats__has_renames( + const git_diff_stats *stats) +{ + git_patch *patch = NULL; + size_t i; + + git_vector_foreach(&stats->patches, i, patch) { + const git_diff_delta *delta = git_patch_get_delta(patch); + + if (strcmp(delta->old_file.path, delta->new_file.path) != 0) { + return 1; + } + } + + return 0; +} + +int git_diff_stats__add_file_stats( + git_diff_stats *stats, + git_patch *patch) +{ + const git_diff_delta *delta = NULL; + int error = 0; + + if ((delta = git_patch_get_delta(patch)) == NULL) + return -1; + + if ((error = git_vector_insert(&stats->patches, patch)) < 0) + return error; + + return error; +} + +int git_diff_get_stats( + git_diff_stats **out, + git_diff *diff) +{ + size_t i, deltas; + size_t total_insertions = 0, total_deletions = 0; + git_diff_stats *stats = NULL; + int error = 0; + + assert(out && diff); + + stats = git__calloc(1, sizeof(git_diff_stats)); + GITERR_CHECK_ALLOC(stats); + + deltas = git_diff_num_deltas(diff); + + for (i = 0; i < deltas; ++i) { + git_patch *patch = NULL; + size_t add, remove; + + if ((error = git_patch_from_diff(&patch, diff, i)) < 0) + goto on_error; + + if ((error = git_patch_line_stats(NULL, &add, &remove, patch)) < 0 || + (error = git_diff_stats__add_file_stats(stats, patch)) < 0) { + git_patch_free(patch); + goto on_error; + } + + total_insertions += add; + total_deletions += remove; + } + + stats->files_changed = deltas; + stats->insertions = total_insertions; + stats->deletions = total_deletions; + + *out = stats; + + goto done; + +on_error: + git_diff_stats_free(stats); + +done: + return error; +} + +size_t git_diff_stats_files_changed( + const git_diff_stats *stats) +{ + assert(stats); + + return stats->files_changed; +} + +size_t git_diff_stats_insertions( + const git_diff_stats *stats) +{ + assert(stats); + + return stats->insertions; +} + +size_t git_diff_stats_deletions( + const git_diff_stats *stats) +{ + assert(stats); + + return stats->deletions; +} + +int git_diff_stats_to_buf( + git_buf *out, + const git_diff_stats *stats, + git_diff_stats_format_t format) +{ + git_patch *patch = NULL; + size_t i; + int has_renames = 0, error = 0; + + assert(out && stats); + + /* check if we have renames, it affects the padding */ + has_renames = git_diff_stats__has_renames(stats); + + git_vector_foreach(&stats->patches, i, patch) { + if (format & GIT_DIFF_STATS_FULL) { + size_t max_padding = diff_get_filename_padding(has_renames, stats); + + error = git_diff_file_stats__full_to_buf(out, max_padding, has_renames, patch); + } + else if (format & GIT_DIFF_STATS_NUMBER) { + error = git_diff_file_stats__number_to_buf(out, patch); + } + + if (error < 0) + return error; + } + + if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) { + error = git_buf_printf(out, " %" PRIuZ " file%s changed, %" PRIuZ " insertions(+), %" PRIuZ " deletions(-)\n", + stats->files_changed, stats->files_changed > 1 ? "s" : "", + stats->insertions, stats->deletions); + + if (error < 0) + return error; + } + + if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) { + git_vector_foreach(&stats->patches, i, patch) { + if ((error = git_diff_file_stats__summary_to_buf(out, patch)) < 0) + return error; + } + + if (git_vector_length(&stats->patches) > 0) + git_buf_putc(out, '\n'); + } + + return error; +} + +void git_diff_stats_free(git_diff_stats *stats) +{ + size_t i; + git_patch *patch; + + if (stats == NULL) + return; + + git_vector_foreach(&stats->patches, i, patch) + git_patch_free(patch); + + git_vector_free(&stats->patches); + git__free(stats); +} + diff --git a/src/fileops.c b/src/fileops.c index 5709499b0..13b8f6a39 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -132,6 +132,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) if (read_size != (ssize_t)len) { giterr_set(GITERR_OS, "Failed to read descriptor"); + git_buf_free(buf); return -1; } @@ -804,10 +805,8 @@ int git_futils_filestamp_check( if (stamp == NULL) return 1; - if (p_stat(path, &st) < 0) { - giterr_set(GITERR_OS, "Could not stat '%s'", path); + if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - } if (stamp->mtime == (git_time_t)st.st_mtime && stamp->size == (git_off_t)st.st_size && @@ -831,3 +830,16 @@ void git_futils_filestamp_set( else 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)); + } +} diff --git a/src/fileops.h b/src/fileops.h index 6a65235de..62227abae 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -292,13 +292,14 @@ typedef struct { * Compare stat information for file with reference info. * * 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 - * 1 if the file has been changed. (This also may return GIT_ENOTFOUND if - * the file doesn't exist.) + * and returns 0 if the file is up-to-date relative to the prior setting, + * 1 if the file has been changed, or GIT_ENOTFOUND if the file doesn't + * 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 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( git_futils_filestamp *stamp, const char *path); @@ -316,4 +317,10 @@ extern int git_futils_filestamp_check( extern void git_futils_filestamp_set( 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__ */ diff --git a/src/global.c b/src/global.c index 8c8f55a90..15baf1eb8 100644 --- a/src/global.c +++ b/src/global.c @@ -16,8 +16,9 @@ git_mutex git__mwindow_mutex; #define MAX_SHUTDOWN_CB 8 -git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; -git_atomic git__n_shutdown_callbacks; +static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; +static git_atomic git__n_shutdown_callbacks; +static git_atomic git__n_inits; void git__on_shutdown(git_global_shutdown_fn callback) { @@ -74,7 +75,6 @@ static void git__shutdown(void) static DWORD _tls_index; static DWORD _mutex = 0; -static DWORD _n_inits = 0; static int synchronized_threads_init() { @@ -101,7 +101,7 @@ int git_threads_init(void) while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } /* 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(); /* Exit the lock */ @@ -124,7 +124,7 @@ void git_threads_shutdown(void) while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } /* 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(); /* Exit the lock */ @@ -135,7 +135,7 @@ git_global_st *git__global_state(void) { void *ptr; - assert(_n_inits); + assert(git_atomic_get(&git__n_inits) > 0); if ((ptr = TlsGetValue(_tls_index)) != NULL) return ptr; @@ -153,7 +153,6 @@ git_global_st *git__global_state(void) static pthread_key_t _tls_key; static pthread_once_t _once_init = PTHREAD_ONCE_INIT; -static git_atomic git__n_inits; int init_error = 0; static void cb__free_status(void *st) @@ -183,6 +182,7 @@ int git_threads_init(void) void git_threads_shutdown(void) { + void *ptr = NULL; pthread_once_t new_once = PTHREAD_ONCE_INIT; if (git_atomic_dec(&git__n_inits) > 0) return; @@ -190,7 +190,7 @@ void git_threads_shutdown(void) /* Shut down any subsystems that have global state */ git__shutdown(); - void *ptr = pthread_getspecific(_tls_key); + ptr = pthread_getspecific(_tls_key); pthread_setspecific(_tls_key, NULL); git__free(ptr); @@ -203,7 +203,7 @@ git_global_st *git__global_state(void) { void *ptr; - assert(git__n_inits.val); + assert(git_atomic_get(&git__n_inits) > 0); if ((ptr = pthread_getspecific(_tls_key)) != NULL) return ptr; @@ -223,14 +223,15 @@ static git_global_st __state; int git_threads_init(void) { - /* noop */ + git_atomic_inc(&git__n_inits); return 0; } void git_threads_shutdown(void) { /* Shut down any subsystems that have global state */ - git__shutdown(); + if (0 == git_atomic_dec(&git__n_inits)) + git__shutdown(); } git_global_st *git__global_state(void) diff --git a/src/graph.c b/src/graph.c index 96fda7add..1c264d997 100644 --- a/src/graph.c +++ b/src/graph.c @@ -180,7 +180,12 @@ int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const g if (git_oid_equal(commit, ancestor)) return 0; - if ((error = git_merge_base(&merge_base, repo, commit, ancestor) < 0)) + error = git_merge_base(&merge_base, repo, commit, ancestor); + /* No merge-base found, it's not a descendant */ + if (error == GIT_ENOTFOUND) + return 0; + + if (error < 0) return error; return git_oid_equal(&merge_base, ancestor); diff --git a/src/ignore.c b/src/ignore.c index c79fe4871..deae204f8 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -1,7 +1,7 @@ #include "git2/ignore.h" #include "common.h" #include "ignore.h" -#include "attr.h" +#include "attrcache.h" #include "path.h" #include "config.h" @@ -10,27 +10,26 @@ #define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n" 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; - git_attr_fnmatch *match = NULL; - const char *scan = NULL; - char *context = NULL; 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 - * object. If they did not, then look up the value of ignore_case */ - 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 (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0) + giterr_clear(); - if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) { - context = ignores->key + 2; - context[strlen(context) - strlen(GIT_IGNORE_FILE)] = '\0'; - } + /* if subdir file path, convert context for file paths */ + if (attrs->entry && + git_path_root(attrs->entry->path) < 0 && + !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) { if (!match) { @@ -41,7 +40,7 @@ static int parse_ignore_file( match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG; if (!(error = git_attr_fnmatch__parse( - match, ignores->pool, context, &scan))) + match, &attrs->pool, context, &scan))) { match->flags |= GIT_ATTR_FNMATCH_IGNORE; @@ -49,7 +48,7 @@ static int parse_ignore_file( match->flags |= GIT_ATTR_FNMATCH_ICASE; scan = git__next_line(scan); - error = git_vector_insert(&ignores->rules, match); + error = git_vector_insert(&attrs->rules, match); } if (error != 0) { @@ -63,34 +62,55 @@ static int parse_ignore_file( } } + git_mutex_unlock(&attrs->lock); git__free(match); - /* restore file path used for context */ - if (context) - context[strlen(context)] = '.'; /* first char of GIT_IGNORE_FILE */ return error; } -#define push_ignore_file(R,IGN,S,B,F) \ - git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S)) +static int push_ignore_file( + 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) { git_ignores *ign = payload; - - return push_ignore_file( - ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); + ign->depth++; + return push_ignore_file(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; - if (!(error = git_attr_cache__init(repo))) - error = git_attr_cache__internal_file(repo, GIT_IGNORE_INTERNAL, ign); + if ((error = git_attr_cache__init(repo)) < 0) + return error; - if (!error && !(*ign)->rules.length) - error = parse_ignore_file(repo, NULL, GIT_IGNORE_DEFAULT_RULES, *ign); + error = git_attr_cache__get( + 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; } @@ -105,18 +125,15 @@ int git_ignore__for_path( assert(ignores); + memset(ignores, 0, sizeof(*ignores)); ignores->repo = repo; - git_buf_init(&ignores->dir, 0); - ignores->ign_internal = NULL; /* Read the ignore_case flag */ if ((error = git_repository__cvar( &ignores->ignore_case, repo, GIT_CVAR_IGNORECASE)) < 0) goto cleanup; - if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 || - (error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 || - (error = git_attr_cache__init(repo)) < 0) + if ((error = git_attr_cache__init(repo)) < 0) goto cleanup; /* given a unrooted path in a non-bare repo, resolve it */ @@ -128,8 +145,7 @@ int git_ignore__for_path( goto cleanup; /* set up internals */ - error = get_internal_ignores(&ignores->ign_internal, repo); - if (error < 0) + if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0) goto cleanup; /* load .gitignore up the path */ @@ -141,14 +157,16 @@ int git_ignore__for_path( } /* 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); if (error < 0) goto cleanup; /* load core.excludesfile */ 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); cleanup: @@ -163,55 +181,76 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir) if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0) return -1; + ign->depth++; + 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) { if (ign->ign_path.length > 0) { git_attr_file *file = git_vector_last(&ign->ign_path); - const char *start, *end, *scan; - size_t keylen; + const char *start = file->entry->path, *end; - /* - ign->dir looks something like "a/b" (or "a/b/c/d") - * - file->key looks something like "0#a/b/.gitignore + /* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/") + * - file->path looks something like "a/b/.gitignore * - * We are popping the last directory off ign->dir. We also want to - * remove the file from the vector if the directory part of the key - * matches the ign->dir path. We need to test if the "a/b" part of + * We are popping the last directory off ign->dir. We also want + * to remove the file from the vector if the popped directory + * 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. */ - for (start = end = scan = &file->key[2]; *scan; ++scan) - if (*scan == '/') - end = scan; /* point 'end' to last '/' in key */ - keylen = (end - start) + 1; + if ((end = strrchr(start, '/')) != NULL) { + size_t dirlen = (end - start) + 1; - if (ign->dir.size >= keylen && - !memcmp(ign->dir.ptr + ign->dir.size - keylen, start, keylen)) - git_vector_pop(&ign->ign_path); + if (ign->dir.size >= dirlen && + !memcmp(ign->dir.ptr + ign->dir.size - dirlen, start, dirlen)) + { + git_vector_pop(&ign->ign_path); + git_attr_file__free(file); + } + } + } + if (--ign->depth > 0) { git_buf_rtruncate_at_char(&ign->dir, '/'); + git_path_to_dir(&ign->dir); } + return 0; } 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_foreach(&ignores->ign_global, i, file) { + git_attr_file__free(file); + ignores->ign_global.contents[i] = NULL; + } git_vector_free(&ignores->ign_global); + git_buf_free(&ignores->dir); } 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; git_attr_fnmatch *match; - git_vector_rforeach(rules, j, match) { + git_vector_rforeach(&file->rules, j, match) { if (git_attr_fnmatch__match(match, path)) { *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0); return true; @@ -233,19 +272,18 @@ int git_ignore__lookup( return -1; /* first process builtins - success means path was found */ - if (ignore_lookup_in_rules( - &ignores->ign_internal->rules, &path, ignored)) + if (ignore_lookup_in_rules(ignores->ign_internal, &path, ignored)) goto cleanup; /* next process files in the path */ 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; } /* last process global ignores */ 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; } @@ -256,32 +294,33 @@ cleanup: return 0; } -int git_ignore_add_rule( - git_repository *repo, - const char *rules) +int git_ignore_add_rule(git_repository *repo, const char *rules) { int error; - git_attr_file *ign_internal; + git_attr_file *ign_internal = NULL; + + if ((error = get_internal_ignores(&ign_internal, repo)) < 0) + return error; - if (!(error = get_internal_ignores(&ign_internal, repo))) - error = parse_ignore_file(repo, NULL, rules, ign_internal); + error = parse_ignore_file(repo, ign_internal, rules); + git_attr_file__free(ign_internal); return error; } -int git_ignore_clear_internal_rules( - git_repository *repo) +int git_ignore_clear_internal_rules(git_repository *repo) { int error; git_attr_file *ign_internal; - if (!(error = get_internal_ignores(&ign_internal, repo))) { - git_attr_file__clear_rules(ign_internal); + if ((error = get_internal_ignores(&ign_internal, repo)) < 0) + return error; - return parse_ignore_file( - repo, NULL, GIT_IGNORE_DEFAULT_RULES, ign_internal); - } + if (!(error = git_attr_file__clear_rules(ign_internal, true))) + error = parse_ignore_file( + repo, ign_internal, GIT_IGNORE_DEFAULT_RULES); + git_attr_file__free(ign_internal); return error; } @@ -326,19 +365,18 @@ int git_ignore_path_is_ignored( break; /* first process builtins - success means path was found */ - if (ignore_lookup_in_rules( - &ignores.ign_internal->rules, &path, ignored)) + if (ignore_lookup_in_rules(ignores.ign_internal, &path, ignored)) goto cleanup; /* next process files in the path */ 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; } /* last process global ignores */ 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; } diff --git a/src/ignore.h b/src/ignore.h index 851c824bf..46172c72f 100644 --- a/src/ignore.h +++ b/src/ignore.h @@ -29,6 +29,7 @@ typedef struct { git_vector ign_path; git_vector ign_global; int ignore_case; + int depth; } git_ignores; extern int git_ignore__for_path( diff --git a/src/index.c b/src/index.c index b0b5eae9d..083c01fe4 100644 --- a/src/index.c +++ b/src/index.c @@ -90,13 +90,24 @@ struct entry_long { struct entry_srch_key { const char *path; - size_t path_len; + size_t pathlen; int stage; }; +struct entry_internal { + git_index_entry entry; + size_t pathlen; + char path[GIT_FLEX_ARRAY]; +}; + +struct reuc_entry_internal { + git_index_reuc_entry entry; + size_t pathlen; + char path[GIT_FLEX_ARRAY]; +}; + /* local declarations */ static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size); -static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size); static int read_header(struct index_header *dest, const void *buffer); static int parse_index(git_index *index, const char *buffer, size_t buffer_size); @@ -106,15 +117,15 @@ static int write_index(git_index *index, git_filebuf *file); static void index_entry_free(git_index_entry *entry); static void index_entry_reuc_free(git_index_reuc_entry *reuc); -static int index_srch(const void *key, const void *array_member) +int git_index_entry_srch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; - const git_index_entry *entry = array_member; + const struct entry_internal *entry = array_member; int cmp; size_t len1, len2, len; - len1 = srch_key->path_len; - len2 = strlen(entry->path); + len1 = srch_key->pathlen; + len2 = entry->pathlen; len = len1 < len2 ? len1 : len2; cmp = memcmp(srch_key->path, entry->path, len); @@ -126,20 +137,20 @@ static int index_srch(const void *key, const void *array_member) return 1; if (srch_key->stage != GIT_INDEX_STAGE_ANY) - return srch_key->stage - GIT_IDXENTRY_STAGE(entry); + return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry); return 0; } -static int index_isrch(const void *key, const void *array_member) +int git_index_entry_isrch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; - const git_index_entry *entry = array_member; + const struct entry_internal *entry = array_member; int cmp; size_t len1, len2, len; - len1 = srch_key->path_len; - len2 = strlen(entry->path); + len1 = srch_key->pathlen; + len2 = entry->pathlen; len = len1 < len2 ? len1 : len2; cmp = strncasecmp(srch_key->path, entry->path, len); @@ -152,36 +163,26 @@ static int index_isrch(const void *key, const void *array_member) return 1; if (srch_key->stage != GIT_INDEX_STAGE_ANY) - return srch_key->stage - GIT_IDXENTRY_STAGE(entry); + return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry); return 0; } -static int index_cmp_path(const void *a, const void *b) -{ - return strcmp((const char *)a, (const char *)b); -} - -static int index_icmp_path(const void *a, const void *b) -{ - return strcasecmp((const char *)a, (const char *)b); -} - -static int index_srch_path(const void *path, const void *array_member) +static int index_entry_srch_path(const void *path, const void *array_member) { const git_index_entry *entry = array_member; return strcmp((const char *)path, entry->path); } -static int index_isrch_path(const void *path, const void *array_member) +static int index_entry_isrch_path(const void *path, const void *array_member) { const git_index_entry *entry = array_member; return strcasecmp((const char *)path, entry->path); } -static int index_cmp(const void *a, const void *b) +int git_index_entry_cmp(const void *a, const void *b) { int diff; const git_index_entry *entry_a = a; @@ -195,7 +196,7 @@ static int index_cmp(const void *a, const void *b) return diff; } -static int index_icmp(const void *a, const void *b) +int git_index_entry_icmp(const void *a, const void *b) { int diff; const git_index_entry *entry_a = a; @@ -286,17 +287,12 @@ static int reuc_icmp(const void *a, const void *b) static void index_entry_reuc_free(git_index_reuc_entry *reuc) { - if (!reuc) - return; - git__free(reuc->path); git__free(reuc); } static void index_entry_free(git_index_entry *entry) { - if (!entry) - return; - git__free(entry->path); + memset(&entry->id, 0, sizeof(entry->id)); git__free(entry); } @@ -325,18 +321,69 @@ static unsigned int index_merge_mode( return git_index__create_mode(mode); } -void git_index__set_ignore_case(git_index *index, bool ignore_case) +static int index_sort_if_needed(git_index *index, bool need_lock) { - index->ignore_case = ignore_case; + /* not truly threadsafe because between when this checks and/or + * sorts the array another thread could come in and unsort it + */ - index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path; - index->entries_search = ignore_case ? index_isrch : index_srch; - index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path; + if (git_vector_is_sorted(&index->entries)) + return 0; + + if (need_lock && git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to lock index"); + return -1; + } - git_vector_set_cmp(&index->entries, ignore_case ? index_icmp : index_cmp); git_vector_sort(&index->entries); - index->reuc_search = ignore_case ? reuc_isrch : reuc_srch; + if (need_lock) + git_mutex_unlock(&index->lock); + + return 0; +} + +GIT_INLINE(int) index_find_in_entries( + size_t *out, git_vector *entries, git_vector_cmp entry_srch, + const char *path, size_t path_len, int stage) +{ + struct entry_srch_key srch_key; + srch_key.path = path; + srch_key.pathlen = !path_len ? strlen(path) : path_len; + srch_key.stage = stage; + return git_vector_bsearch2(out, entries, entry_srch, &srch_key); +} + +GIT_INLINE(int) index_find( + size_t *out, git_index *index, + const char *path, size_t path_len, int stage, bool need_lock) +{ + if (index_sort_if_needed(index, need_lock) < 0) + return -1; + + return index_find_in_entries( + out, &index->entries, index->entries_search, path, path_len, stage); +} + +void git_index__set_ignore_case(git_index *index, bool ignore_case) +{ + index->ignore_case = ignore_case; + + if (ignore_case) { + index->entries_cmp_path = git__strcasecmp_cb; + index->entries_search = git_index_entry_isrch; + index->entries_search_path = index_entry_isrch_path; + index->reuc_search = reuc_isrch; + } else { + index->entries_cmp_path = git__strcmp_cb; + index->entries_search = git_index_entry_srch; + index->entries_search_path = index_entry_srch_path; + index->reuc_search = reuc_srch; + } + + git_vector_set_cmp(&index->entries, + ignore_case ? git_index_entry_icmp : git_index_entry_cmp); + index_sort_if_needed(index, true); git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp); git_vector_sort(&index->reuc); @@ -345,41 +392,51 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case) int git_index_open(git_index **index_out, const char *index_path) { git_index *index; - int error; + int error = -1; assert(index_out); index = git__calloc(1, sizeof(git_index)); GITERR_CHECK_ALLOC(index); + if (git_mutex_init(&index->lock)) { + giterr_set(GITERR_OS, "Failed to initialize lock"); + git__free(index); + return -1; + } + if (index_path != NULL) { index->index_file_path = git__strdup(index_path); - GITERR_CHECK_ALLOC(index->index_file_path); + if (!index->index_file_path) + goto fail; /* Check if index file is stored on disk already */ if (git_path_exists(index->index_file_path) == true) index->on_disk = 1; } - if (git_vector_init(&index->entries, 32, index_cmp) < 0 || - git_vector_init(&index->names, 32, conflict_name_cmp) < 0 || - git_vector_init(&index->reuc, 32, reuc_cmp) < 0) - return -1; + if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 || + git_vector_init(&index->names, 8, conflict_name_cmp) < 0 || + git_vector_init(&index->reuc, 8, reuc_cmp) < 0 || + git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0) + goto fail; - index->entries_cmp_path = index_cmp_path; - index->entries_search = index_srch; - index->entries_search_path = index_srch_path; + index->entries_cmp_path = git__strcmp_cb; + index->entries_search = git_index_entry_srch; + index->entries_search_path = index_entry_srch_path; index->reuc_search = reuc_srch; - if ((index_path != NULL) && ((error = git_index_read(index, true)) < 0)) { - git_index_free(index); - return error; - } + if (index_path != NULL && (error = git_index_read(index, true)) < 0) + goto fail; *index_out = index; GIT_REFCOUNT_INC(index); return 0; + +fail: + git_index_free(index); + return error; } int git_index_new(git_index **out) @@ -389,12 +446,19 @@ int git_index_new(git_index **out) static void index_free(git_index *index) { + /* index iterators increment the refcount of the index, so if we + * get here then there should be no outstanding iterators. + */ + assert(!git_atomic_get(&index->readers)); + git_index_clear(index); git_vector_free(&index->entries); git_vector_free(&index->names); git_vector_free(&index->reuc); + git_vector_free(&index->deleted); git__free(index->index_file_path); + git_mutex_free(&index->lock); git__memzero(index, sizeof(*index)); git__free(index); @@ -408,28 +472,71 @@ void git_index_free(git_index *index) GIT_REFCOUNT_DEC(index, index_free); } -static void index_entries_free(git_vector *entries) +/* call with locked index */ +static void index_free_deleted(git_index *index) { + int readers = (int)git_atomic_get(&index->readers); size_t i; - for (i = 0; i < entries->length; ++i) - index_entry_free(git__swap(entries->contents[i], NULL)); + if (readers > 0 || !index->deleted.length) + return; + + for (i = 0; i < index->deleted.length; ++i) { + git_index_entry *ie = git__swap(index->deleted.contents[i], NULL); + index_entry_free(ie); + } - git_vector_clear(entries); + git_vector_clear(&index->deleted); } -void git_index_clear(git_index *index) +/* call with locked index */ +static int index_remove_entry(git_index *index, size_t pos) { + int error = 0; + git_index_entry *entry = git_vector_get(&index->entries, pos); + + if (entry != NULL) + git_tree_cache_invalidate_path(index->tree, entry->path); + + error = git_vector_remove(&index->entries, pos); + + if (!error) { + if (git_atomic_get(&index->readers) > 0) { + error = git_vector_insert(&index->deleted, entry); + } else { + index_entry_free(entry); + } + } + + return error; +} + +int git_index_clear(git_index *index) +{ + int error = 0; + assert(index); - index_entries_free(&index->entries); + git_tree_cache_free(index->tree); + index->tree = NULL; + + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } + + while (!error && index->entries.length > 0) + error = index_remove_entry(index, index->entries.length - 1); + index_free_deleted(index); + git_index_reuc_clear(index); git_index_name_clear(index); git_futils_filestamp_set(&index->stamp, NULL); - git_tree_cache_free(index->tree); - index->tree = NULL; + git_mutex_unlock(&index->lock); + + return error; } static int create_index_error(int error, const char *msg) @@ -495,20 +602,29 @@ int git_index_read(git_index *index, int force) if (!index->on_disk) { if (force) - git_index_clear(index); + return git_index_clear(index); return 0; } updated = git_futils_filestamp_check(&stamp, index->index_file_path); - if (updated < 0 || (!updated && !force)) + if (updated < 0) { + giterr_set( + GITERR_INDEX, + "Failed to read index: '%s' no longer exists", + index->index_file_path); return updated; + } + if (!updated && !force) + return 0; error = git_futils_readbuffer(&buffer, index->index_file_path); if (error < 0) return error; - git_index_clear(index); - error = parse_index(index, buffer.ptr, buffer.size); + error = git_index_clear(index); + + if (!error) + error = parse_index(index, buffer.ptr, buffer.size); if (!error) git_futils_filestamp_set(&index->stamp, &stamp); @@ -538,7 +654,8 @@ int git_index_write(git_index *index) return create_index_error(-1, "Failed to read index: The index is in-memory only"); - git_vector_sort(&index->entries); + if (index_sort_if_needed(index, true) < 0) + return -1; git_vector_sort(&index->reuc); if ((error = git_filebuf_open( @@ -557,11 +674,11 @@ int git_index_write(git_index *index) if ((error = git_filebuf_commit(&file)) < 0) return error; - error = git_futils_filestamp_check(&index->stamp, index->index_file_path); - if (error < 0) - return error; + if (git_futils_filestamp_check(&index->stamp, index->index_file_path) < 0) + /* index could not be read from disk! */; + else + index->on_disk = 1; - index->on_disk = 1; return 0; } @@ -586,7 +703,8 @@ int git_index_write_tree(git_oid *oid, git_index *index) return git_tree__write_index(oid, index, repo); } -int git_index_write_tree_to(git_oid *oid, git_index *index, git_repository *repo) +int git_index_write_tree_to( + git_oid *oid, git_index *index, git_repository *repo) { assert(oid && index && repo); return git_tree__write_index(oid, index, repo); @@ -602,7 +720,8 @@ const git_index_entry *git_index_get_byindex( git_index *index, size_t n) { assert(index); - git_vector_sort(&index->entries); + if (index_sort_if_needed(index, true) < 0) + return NULL; return git_vector_get(&index->entries, n); } @@ -613,7 +732,7 @@ const git_index_entry *git_index_get_bypath( assert(index); - if (git_index__find(&pos, index, path, 0, stage) < 0) { + if (index_find(&pos, index, path, 0, stage, true) < 0) { giterr_set(GITERR_INDEX, "Index does not contain %s", path); return NULL; } @@ -637,20 +756,19 @@ void git_index_entry__init_from_stat( entry->file_size = st->st_size; } -int git_index_entry__cmp(const void *a, const void *b) +static git_index_entry *index_entry_alloc(const char *path) { - const git_index_entry *entry_a = a; - const git_index_entry *entry_b = b; - - return strcmp(entry_a->path, entry_b->path); -} + size_t pathlen = strlen(path); + struct entry_internal *entry = + git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1); + if (!entry) + return NULL; -int git_index_entry__cmp_icase(const void *a, const void *b) -{ - const git_index_entry *entry_a = a; - const git_index_entry *entry_b = b; + entry->pathlen = pathlen; + memcpy(entry->path, path, pathlen); + entry->entry.path = entry->path; - return strcasecmp(entry_a->path, entry_b->path); + return (git_index_entry *)entry; } static int index_entry_init( @@ -672,19 +790,31 @@ static int index_entry_init( if (error < 0) return error; - entry = git__calloc(1, sizeof(git_index_entry)); + entry = index_entry_alloc(rel_path); GITERR_CHECK_ALLOC(entry); - git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); - entry->id = oid; - entry->path = git__strdup(rel_path); - GITERR_CHECK_ALLOC(entry->path); + git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); - *entry_out = entry; + *entry_out = (git_index_entry *)entry; return 0; } +static git_index_reuc_entry *reuc_entry_alloc(const char *path) +{ + size_t pathlen = strlen(path); + struct reuc_entry_internal *entry = + git__calloc(sizeof(struct reuc_entry_internal) + pathlen + 1, 1); + if (!entry) + return NULL; + + entry->pathlen = pathlen; + memcpy(entry->path, path, pathlen); + entry->entry.path = entry->path; + + return (git_index_reuc_entry *)entry; +} + static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, const char *path, int ancestor_mode, const git_oid *ancestor_oid, @@ -695,15 +825,9 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, assert(reuc_out && path); - *reuc_out = NULL; - - reuc = git__calloc(1, sizeof(git_index_reuc_entry)); + *reuc_out = reuc = reuc_entry_alloc(path); GITERR_CHECK_ALLOC(reuc); - reuc->path = git__strdup(path); - if (reuc->path == NULL) - return -1; - if ((reuc->mode[0] = ancestor_mode) > 0) git_oid_cpy(&reuc->oid[0], ancestor_oid); @@ -713,26 +837,31 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, if ((reuc->mode[2] = their_mode) > 0) git_oid_cpy(&reuc->oid[2], their_oid); - *reuc_out = reuc; return 0; } -static git_index_entry *index_entry_dup(const git_index_entry *source_entry) +static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src) +{ + char *tgt_path = tgt->path; + memcpy(tgt, src, sizeof(*tgt)); + tgt->path = tgt_path; /* reset to existing path data */ +} + +static int index_entry_dup(git_index_entry **out, const git_index_entry *src) { git_index_entry *entry; - entry = git__malloc(sizeof(git_index_entry)); - if (!entry) - return NULL; + if (!src) { + *out = NULL; + return 0; + } - memcpy(entry, source_entry, sizeof(git_index_entry)); + *out = entry = index_entry_alloc(src->path); + GITERR_CHECK_ALLOC(entry); - /* duplicate the path string so we own it */ - entry->path = git__strdup(entry->path); - if (!entry->path) - return NULL; + index_entry_cpy(entry, src); - return entry; + return 0; } static int has_file_name(git_index *index, @@ -744,20 +873,22 @@ static int has_file_name(git_index *index, const char *name = entry->path; while (pos < index->entries.length) { - git_index_entry *p = index->entries.contents[pos++]; + struct entry_internal *p = index->entries.contents[pos++]; - if (len >= strlen(p->path)) + if (len >= p->pathlen) break; if (memcmp(name, p->path, len)) break; - if (GIT_IDXENTRY_STAGE(p) != stage) + if (GIT_IDXENTRY_STAGE(&p->entry) != stage) continue; if (p->path[len] != '/') continue; retval = -1; if (!ok_to_replace) break; - git_vector_remove(&index->entries, --pos); + + if (index_remove_entry(index, --pos) < 0) + break; } return retval; } @@ -775,7 +906,7 @@ static int has_dir_name(git_index *index, const char *slash = name + strlen(name); for (;;) { - size_t len, position; + size_t len, pos; for (;;) { if (*--slash == '/') @@ -785,12 +916,13 @@ static int has_dir_name(git_index *index, } len = slash - name; - if (git_index__find(&position, index, name, len, stage) == 0) { + if (!index_find(&pos, index, name, len, stage, false)) { retval = -1; if (!ok_to_replace) break; - git_vector_remove(&index->entries, position); + if (index_remove_entry(index, pos) < 0) + break; continue; } @@ -799,20 +931,19 @@ static int has_dir_name(git_index *index, * already matches the sub-directory, then we know * we're ok, and we can exit. */ - while (position < index->entries.length) { - git_index_entry *p = index->entries.contents[position]; + for (; pos < index->entries.length; ++pos) { + struct entry_internal *p = index->entries.contents[pos]; - if ((strlen(p->path) <= len) || - (p->path[len] != '/') || + if (p->pathlen <= len || + p->path[len] != '/' || memcmp(p->path, name, len)) break; /* not our subdirectory */ - if (GIT_IDXENTRY_STAGE(p) == stage) + if (GIT_IDXENTRY_STAGE(&p->entry) == stage) return retval; - - position++; } } + return retval; } @@ -823,22 +954,41 @@ static int check_file_directory_collision(git_index *index, retval = retval + has_dir_name(index, entry, ok_to_replace); if (retval) { - giterr_set(GITERR_INDEX, "'%s' appears as both a file an a directory", entry->path); + giterr_set(GITERR_INDEX, + "'%s' appears as both a file and a directory", entry->path); return -1; } return 0; } -static int index_insert(git_index *index, git_index_entry *entry, int replace) +static int index_no_dups(void **old, void *new) +{ + const git_index_entry *entry = new; + GIT_UNUSED(old); + giterr_set(GITERR_INDEX, "'%s' appears multiple times at stage %d", + entry->path, GIT_IDXENTRY_STAGE(entry)); + return GIT_EEXISTS; +} + +/* index_insert takes ownership of the new entry - if it can't insert + * it, then it will return an error **and also free the entry**. When + * it replaces an existing entry, it will update the entry_ptr with the + * actual entry in the index (and free the passed in one). + */ +static int index_insert( + git_index *index, git_index_entry **entry_ptr, int replace) { + int error = 0; size_t path_length, position; - git_index_entry **existing = NULL; + git_index_entry *existing = NULL, *entry; + + assert(index && entry_ptr); - assert(index && entry && entry->path != NULL); + entry = *entry_ptr; /* make sure that the path length flag is correct */ - path_length = strlen(entry->path); + path_length = ((struct entry_internal *)entry)->pathlen; entry->flags &= ~GIT_IDXENTRY_NAMEMASK; @@ -847,30 +997,51 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace) else entry->flags |= GIT_IDXENTRY_NAMEMASK; + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire index lock"); + return -1; + } + + git_vector_sort(&index->entries); + /* look if an entry with this path already exists */ - if (!git_index__find( - &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry))) { - existing = (git_index_entry **)&index->entries.contents[position]; + if (!index_find( + &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) { + existing = index->entries.contents[position]; /* update filemode to existing values if stat is not trusted */ - entry->mode = index_merge_mode(index, *existing, entry->mode); + entry->mode = index_merge_mode(index, existing, entry->mode); } - if (check_file_directory_collision(index, entry, position, replace) < 0) - return -1; + /* look for tree / blob name collisions, removing conflicts if requested */ + error = check_file_directory_collision(index, entry, position, replace); + if (error < 0) + /* skip changes */; - /* if replacing is not requested or no existing entry exists, just - * insert entry at the end; the index is no longer sorted + /* if we are replacing an existing item, overwrite the existing entry + * and return it in place of the passed in one. */ - if (!replace || !existing) - return git_vector_insert(&index->entries, entry); + else if (existing) { + if (replace) + index_entry_cpy(existing, entry); + index_entry_free(entry); + *entry_ptr = entry = existing; + } + else { + /* if replace is not requested or no existing entry exists, insert + * at the sorted position. (Since we re-sort after each insert to + * check for dups, this is actually cheaper in the long run.) + */ + error = git_vector_insert_sorted(&index->entries, entry, index_no_dups); + } - /* exists, replace it (preserving name from existing entry) */ - git__free(entry->path); - entry->path = (*existing)->path; - git__free(*existing); - *existing = entry; + if (error < 0) { + index_entry_free(*entry_ptr); + *entry_ptr = NULL; + } - return 0; + git_mutex_unlock(&index->lock); + + return error; } static int index_conflict_to_reuc(git_index *index, const char *path) @@ -907,19 +1078,15 @@ int git_index_add_bypath(git_index *index, const char *path) assert(index && path); if ((ret = index_entry_init(&entry, index, path)) < 0 || - (ret = index_insert(index, entry, 1)) < 0) - goto on_error; + (ret = index_insert(index, &entry, 1)) < 0) + return ret; /* Adding implies conflict was resolved, move conflict entries to REUC */ if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND) - goto on_error; + return ret; git_tree_cache_invalidate_path(index->tree, entry->path); return 0; - -on_error: - index_entry_free(entry); - return ret; } int git_index_remove_bypath(git_index *index, const char *path) @@ -942,14 +1109,11 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) git_index_entry *entry = NULL; int ret; - entry = index_entry_dup(source_entry); - if (entry == NULL) - return -1; + assert(index && source_entry && source_entry->path); - if ((ret = index_insert(index, entry, 1)) < 0) { - index_entry_free(entry); + if ((ret = index_entry_dup(&entry, source_entry)) < 0 || + (ret = index_insert(index, &entry, 1)) < 0) return ret; - } git_tree_cache_invalidate_path(index->tree, entry->path); return 0; @@ -957,25 +1121,23 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) int git_index_remove(git_index *index, const char *path, int stage) { - size_t position; int error; - git_index_entry *entry; + size_t position; - if (git_index__find(&position, index, path, 0, stage) < 0) { - giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d", - path, stage); - return GIT_ENOTFOUND; + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; } - entry = git_vector_get(&index->entries, position); - if (entry != NULL) - git_tree_cache_invalidate_path(index->tree, entry->path); - - error = git_vector_remove(&index->entries, position); - - if (!error) - index_entry_free(entry); + if (index_find(&position, index, path, 0, stage, false) < 0) { + giterr_set( + GITERR_INDEX, "Index does not contain %s at stage %d", path, stage); + error = GIT_ENOTFOUND; + } else { + error = index_remove_entry(index, position); + } + git_mutex_unlock(&index->lock); return error; } @@ -986,14 +1148,16 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) size_t pos; git_index_entry *entry; - if (git_buf_sets(&pfx, dir) < 0 || git_path_to_dir(&pfx) < 0) + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); return -1; + } - git_vector_sort(&index->entries); - - pos = git_index__prefix_position(index, pfx.ptr); + if (!(error = git_buf_sets(&pfx, dir)) && + !(error = git_path_to_dir(&pfx))) + index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY, false); - while (1) { + while (!error) { entry = git_vector_get(&index->entries, pos); if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0) break; @@ -1003,35 +1167,22 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) continue; } - git_tree_cache_invalidate_path(index->tree, entry->path); - - if ((error = git_vector_remove(&index->entries, pos)) < 0) - break; - index_entry_free(entry); + error = index_remove_entry(index, pos); - /* removed entry at 'pos' so we don't need to increment it */ + /* removed entry at 'pos' so we don't need to increment */ } + git_mutex_unlock(&index->lock); git_buf_free(&pfx); return error; } -int git_index__find( +int git_index__find_pos( size_t *out, git_index *index, const char *path, size_t path_len, int stage) { - struct entry_srch_key srch_key; - - assert(path); - - git_vector_sort(&index->entries); - - srch_key.path = path; - srch_key.path_len = !path_len ? strlen(path) : path_len; - srch_key.stage = stage; - - return git_vector_bsearch2( - out, &index->entries, index->entries_search, &srch_key); + assert(index && path); + return index_find(out, index, path, path_len, stage, true); } int git_index_find(size_t *at_pos, git_index *index, const char *path) @@ -1040,7 +1191,14 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path) assert(index && path); - if (git_vector_bsearch2(&pos, &index->entries, index->entries_search_path, path) < 0) { + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } + + if (git_vector_bsearch2( + &pos, &index->entries, index->entries_search_path, path) < 0) { + git_mutex_unlock(&index->lock); giterr_set(GITERR_INDEX, "Index does not contain %s", path); return GIT_ENOTFOUND; } @@ -1048,37 +1206,20 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path) /* Since our binary search only looked at path, we may be in the * middle of a list of stages. */ - while (pos > 0) { - const git_index_entry *prev = git_vector_get(&index->entries, pos-1); + for (; pos > 0; --pos) { + const git_index_entry *prev = git_vector_get(&index->entries, pos - 1); if (index->entries_cmp_path(prev->path, path) != 0) break; - - --pos; } if (at_pos) *at_pos = pos; + git_mutex_unlock(&index->lock); return 0; } -size_t git_index__prefix_position(git_index *index, const char *path) -{ - struct entry_srch_key srch_key; - size_t pos; - - srch_key.path = path; - srch_key.path_len = strlen(path); - srch_key.stage = 0; - - git_vector_sort(&index->entries); - git_vector_bsearch2( - &pos, &index->entries, index->entries_search, &srch_key); - - return pos; -} - int git_index_conflict_add(git_index *index, const git_index_entry *ancestor_entry, const git_index_entry *our_entry, @@ -1090,21 +1231,22 @@ int git_index_conflict_add(git_index *index, assert (index); - if ((ancestor_entry != NULL && (entries[0] = index_entry_dup(ancestor_entry)) == NULL) || - (our_entry != NULL && (entries[1] = index_entry_dup(our_entry)) == NULL) || - (their_entry != NULL && (entries[2] = index_entry_dup(their_entry)) == NULL)) - return -1; + if ((ret = index_entry_dup(&entries[0], ancestor_entry)) < 0 || + (ret = index_entry_dup(&entries[1], our_entry)) < 0 || + (ret = index_entry_dup(&entries[2], their_entry)) < 0) + goto on_error; for (i = 0; i < 3; i++) { if (entries[i] == NULL) continue; /* Make sure stage is correct */ - entries[i]->flags = (entries[i]->flags & ~GIT_IDXENTRY_STAGEMASK) | - ((i+1) << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); - if ((ret = index_insert(index, entries[i], 1)) < 0) + if ((ret = index_insert(index, &entries[i], 1)) < 0) goto on_error; + + entries[i] = NULL; /* don't free if later entry fails */ } return 0; @@ -1194,23 +1336,24 @@ int git_index_conflict_get( return 0; } -int git_index_conflict_remove(git_index *index, const char *path) +static int index_conflict_remove(git_index *index, const char *path) { - size_t pos, posmax; + size_t pos = 0; git_index_entry *conflict_entry; int error = 0; - assert(index && path); - - if (git_index_find(&pos, index, path) < 0) + if (path != NULL && git_index_find(&pos, index, path) < 0) return GIT_ENOTFOUND; - posmax = git_index_entrycount(index); + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to lock index"); + return -1; + } - while (pos < posmax) { - conflict_entry = git_vector_get(&index->entries, pos); + while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) { - if (index->entries_cmp_path(conflict_entry->path, path) != 0) + if (path != NULL && + index->entries_cmp_path(conflict_entry->path, path) != 0) break; if (GIT_IDXENTRY_STAGE(conflict_entry) == 0) { @@ -1218,32 +1361,25 @@ int git_index_conflict_remove(git_index *index, const char *path) continue; } - if ((error = git_vector_remove(&index->entries, pos)) < 0) - return error; - - index_entry_free(conflict_entry); - posmax--; + if ((error = index_remove_entry(index, pos)) < 0) + break; } - return 0; + git_mutex_unlock(&index->lock); + + return error; } -static int index_conflicts_match(const git_vector *v, size_t idx) +int git_index_conflict_remove(git_index *index, const char *path) { - git_index_entry *entry = git_vector_get(v, idx); - - if (GIT_IDXENTRY_STAGE(entry) > 0) { - index_entry_free(entry); - return 1; - } - - return 0; + assert(index && path); + return index_conflict_remove(index, path); } -void git_index_conflict_cleanup(git_index *index) +int git_index_conflict_cleanup(git_index *index) { assert(index); - git_vector_remove_matching(&index->entries, index_conflicts_match); + return index_conflict_remove(index, NULL); } int git_index_has_conflicts(const git_index *index) @@ -1338,32 +1474,36 @@ const git_index_name_entry *git_index_name_get_byindex( return git_vector_get(&index->names, n); } +static void index_name_entry_free(git_index_name_entry *ne) +{ + if (!ne) + return; + git__free(ne->ancestor); + git__free(ne->ours); + git__free(ne->theirs); + git__free(ne); +} + int git_index_name_add(git_index *index, const char *ancestor, const char *ours, const char *theirs) { git_index_name_entry *conflict_name; - assert ((ancestor && ours) || (ancestor && theirs) || (ours && theirs)); + assert((ancestor && ours) || (ancestor && theirs) || (ours && theirs)); conflict_name = git__calloc(1, sizeof(git_index_name_entry)); GITERR_CHECK_ALLOC(conflict_name); - if (ancestor) { - conflict_name->ancestor = git__strdup(ancestor); - GITERR_CHECK_ALLOC(conflict_name->ancestor); - } - - if (ours) { - conflict_name->ours = git__strdup(ours); - GITERR_CHECK_ALLOC(conflict_name->ours); - } - - if (theirs) { - conflict_name->theirs = git__strdup(theirs); - GITERR_CHECK_ALLOC(conflict_name->theirs); + if ((ancestor && !(conflict_name->ancestor = git__strdup(ancestor))) || + (ours && !(conflict_name->ours = git__strdup(ours))) || + (theirs && !(conflict_name->theirs = git__strdup(theirs))) || + git_vector_insert(&index->names, conflict_name) < 0) + { + index_name_entry_free(conflict_name); + return -1; } - return git_vector_insert(&index->names, conflict_name); + return 0; } void git_index_name_clear(git_index *index) @@ -1373,18 +1513,8 @@ void git_index_name_clear(git_index *index) assert(index); - git_vector_foreach(&index->names, i, conflict_name) { - if (conflict_name->ancestor) - git__free(conflict_name->ancestor); - - if (conflict_name->ours) - git__free(conflict_name->ours); - - if (conflict_name->theirs) - git__free(conflict_name->theirs); - - git__free(conflict_name); - } + git_vector_foreach(&index->names, i, conflict_name) + index_name_entry_free(conflict_name); git_vector_clear(&index->names); } @@ -1412,7 +1542,6 @@ static int index_reuc_insert( return git_vector_insert(&index->reuc, reuc); /* exists, replace it */ - git__free((*existing)->path); git__free(*existing); *existing = reuc; @@ -1429,15 +1558,13 @@ int git_index_reuc_add(git_index *index, const char *path, assert(index && path); - if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || + if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, + ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || (error = index_reuc_insert(index, reuc, 1)) < 0) - { index_entry_reuc_free(reuc); - return error; - } return error; -} +} int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path) { @@ -1522,13 +1649,9 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) if (size <= len) return index_error_invalid("reading reuc entries"); - lost = git__calloc(1, sizeof(git_index_reuc_entry)); + lost = reuc_entry_alloc(buffer); GITERR_CHECK_ALLOC(lost); - /* read NUL-terminated pathname for entry */ - lost->path = git__strdup(buffer); - GITERR_CHECK_ALLOC(lost->path); - size -= len; buffer += len; @@ -1626,41 +1749,41 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size return 0; } -static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size) +static size_t read_entry( + git_index_entry **out, const void *buffer, size_t buffer_size) { size_t path_length, entry_size; uint16_t flags_raw; const char *path_ptr; const struct entry_short *source = buffer; + git_index_entry entry = {{0}}; if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size) return 0; - memset(dest, 0x0, sizeof(git_index_entry)); - - dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds); - dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds); - dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds); - dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds); - dest->dev = ntohl(source->dev); - dest->ino = ntohl(source->ino); - dest->mode = ntohl(source->mode); - dest->uid = ntohl(source->uid); - dest->gid = ntohl(source->gid); - dest->file_size = ntohl(source->file_size); - git_oid_cpy(&dest->id, &source->oid); - dest->flags = ntohs(source->flags); - - if (dest->flags & GIT_IDXENTRY_EXTENDED) { + entry.ctime.seconds = (git_time_t)ntohl(source->ctime.seconds); + entry.ctime.nanoseconds = ntohl(source->ctime.nanoseconds); + entry.mtime.seconds = (git_time_t)ntohl(source->mtime.seconds); + entry.mtime.nanoseconds = ntohl(source->mtime.nanoseconds); + entry.dev = ntohl(source->dev); + entry.ino = ntohl(source->ino); + entry.mode = ntohl(source->mode); + entry.uid = ntohl(source->uid); + entry.gid = ntohl(source->gid); + entry.file_size = ntohl(source->file_size); + git_oid_cpy(&entry.id, &source->oid); + entry.flags = ntohs(source->flags); + + if (entry.flags & GIT_IDXENTRY_EXTENDED) { const struct entry_long *source_l = (const struct entry_long *)source; path_ptr = source_l->path; flags_raw = ntohs(source_l->flags_extended); - memcpy(&dest->flags_extended, &flags_raw, 2); + memcpy(&entry.flags_extended, &flags_raw, 2); } else path_ptr = source->path; - path_length = dest->flags & GIT_IDXENTRY_NAMEMASK; + path_length = entry.flags & GIT_IDXENTRY_NAMEMASK; /* if this is a very long string, we must find its * real length without overflowing */ @@ -1674,7 +1797,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe path_length = path_end - path_ptr; } - if (dest->flags & GIT_IDXENTRY_EXTENDED) + if (entry.flags & GIT_IDXENTRY_EXTENDED) entry_size = long_entry_size(path_length); else entry_size = short_entry_size(path_length); @@ -1682,8 +1805,10 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe if (INDEX_FOOTER_SIZE + entry_size > buffer_size) return 0; - dest->path = git__strdup(path_ptr); - assert(dest->path); + entry.path = (char *)path_ptr; + + if (index_entry_dup(out, &entry) < 0) + return 0; return entry_size; } @@ -1749,6 +1874,7 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer static int parse_index(git_index *index, const char *buffer, size_t buffer_size) { + int error = 0; unsigned int i; struct index_header header = { 0 }; git_oid checksum_calculated, checksum_expected; @@ -1768,35 +1894,41 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE); /* Parse header */ - if (read_header(&header, buffer) < 0) - return -1; + if ((error = read_header(&header, buffer)) < 0) + return error; seek_forward(INDEX_HEADER_SIZE); - git_vector_clear(&index->entries); + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire index lock"); + return -1; + } + + assert(!index->entries.length); /* Parse all the entries */ for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { - size_t entry_size; git_index_entry *entry; - - entry = git__malloc(sizeof(git_index_entry)); - GITERR_CHECK_ALLOC(entry); - - entry_size = read_entry(entry, buffer, buffer_size); + size_t entry_size = read_entry(&entry, buffer, buffer_size); /* 0 bytes read means an object corruption */ - if (entry_size == 0) - return index_error_invalid("invalid entry"); + if (entry_size == 0) { + error = index_error_invalid("invalid entry"); + goto done; + } - if (git_vector_insert(&index->entries, entry) < 0) - return -1; + if ((error = git_vector_insert(&index->entries, entry)) < 0) { + index_entry_free(entry); + goto done; + } seek_forward(entry_size); } - if (i != header.entry_count) - return index_error_invalid("header entries changed while parsing"); + if (i != header.entry_count) { + error = index_error_invalid("header entries changed while parsing"); + goto done; + } /* There's still space for some extensions! */ while (buffer_size > INDEX_FOOTER_SIZE) { @@ -1805,20 +1937,28 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) extension_size = read_extension(index, buffer, buffer_size); /* see if we have read any bytes from the extension */ - if (extension_size == 0) - return index_error_invalid("extension is truncated"); + if (extension_size == 0) { + error = index_error_invalid("extension is truncated"); + goto done; + } seek_forward(extension_size); } - if (buffer_size != INDEX_FOOTER_SIZE) - return index_error_invalid("buffer size does not match index footer size"); + if (buffer_size != INDEX_FOOTER_SIZE) { + error = index_error_invalid( + "buffer size does not match index footer size"); + goto done; + } /* 160-bit SHA-1 over the content of the index file before this checksum. */ git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer); - if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) - return index_error_invalid("calculated checksum does not match expected"); + if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) { + error = index_error_invalid( + "calculated checksum does not match expected"); + goto done; + } #undef seek_forward @@ -1826,9 +1966,11 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) * in-memory index is supposed to be case-insensitive */ git_vector_set_sorted(&index->entries, !index->ignore_case); - git_vector_sort(&index->entries); + error = index_sort_if_needed(index, false); - return 0; +done: + git_mutex_unlock(&index->lock); + return error; } static bool is_index_extended(git_index *index) @@ -1856,7 +1998,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry) size_t path_len, disk_size; char *path; - path_len = strlen(entry->path); + path_len = ((struct entry_internal *)entry)->pathlen; if (entry->flags & GIT_IDXENTRY_EXTENDED) disk_size = long_entry_size(path_len); @@ -1913,22 +2055,30 @@ static int write_entries(git_index *index, git_filebuf *file) { int error = 0; size_t i; - git_vector case_sorted; + git_vector case_sorted, *entries; git_index_entry *entry; - git_vector *out = &index->entries; + + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } /* If index->entries is sorted case-insensitively, then we need * to re-sort it case-sensitively before writing */ if (index->ignore_case) { - git_vector_dup(&case_sorted, &index->entries, index_cmp); + git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp); git_vector_sort(&case_sorted); - out = &case_sorted; + entries = &case_sorted; + } else { + entries = &index->entries; } - git_vector_foreach(out, i, entry) + git_vector_foreach(entries, i, entry) if ((error = write_disk_entry(file, entry)) < 0) break; + git_mutex_unlock(&index->lock); + if (index->ignore_case) git_vector_free(&case_sorted); @@ -2100,7 +2250,7 @@ int git_index_entry_stage(const git_index_entry *entry) typedef struct read_tree_data { git_vector *old_entries; git_vector *new_entries; - git_vector_cmp entries_search; + git_vector_cmp entry_cmp; } read_tree_data; static int read_tree_cb( @@ -2109,6 +2259,7 @@ static int read_tree_cb( read_tree_data *data = payload; git_index_entry *entry = NULL, *old_entry; git_buf path = GIT_BUF_INIT; + size_t pos; if (git_tree_entry__is_tree(tentry)) return 0; @@ -2116,30 +2267,24 @@ static int read_tree_cb( if (git_buf_joinpath(&path, root, tentry->filename) < 0) return -1; - entry = git__calloc(1, sizeof(git_index_entry)); + entry = index_entry_alloc(path.ptr); GITERR_CHECK_ALLOC(entry); entry->mode = tentry->attr; entry->id = tentry->oid; /* look for corresponding old entry and copy data to new entry */ - if (data->old_entries) { - size_t pos; - struct entry_srch_key skey; - - skey.path = path.ptr; - skey.path_len = strlen(path.ptr); - skey.stage = 0; - - if (!git_vector_bsearch2( - &pos, data->old_entries, data->entries_search, &skey) && - (old_entry = git_vector_get(data->old_entries, pos)) != NULL && - entry->mode == old_entry->mode && - git_oid_equal(&entry->id, &old_entry->id)) - { - memcpy(entry, old_entry, sizeof(*entry)); - entry->flags_extended = 0; - } + if (data->old_entries != NULL && + !index_find_in_entries( + &pos, data->old_entries, data->entry_cmp, path.ptr, 0, 0) && + (old_entry = git_vector_get(data->old_entries, pos)) != NULL && + entry->mode == old_entry->mode && + git_oid_equal(&entry->id, &old_entry->id)) + { + char *oldpath = entry->path; + memcpy(entry, old_entry, sizeof(*entry)); + entry->path = oldpath; + entry->flags_extended = 0; } if (path.size < GIT_IDXENTRY_NAMEMASK) @@ -2147,7 +2292,6 @@ static int read_tree_cb( else entry->flags = GIT_IDXENTRY_NAMEMASK; - entry->path = git_buf_detach(&path); git_buf_free(&path); if (git_vector_insert(data->new_entries, entry) < 0) { @@ -2168,16 +2312,25 @@ int git_index_read_tree(git_index *index, const git_tree *tree) data.old_entries = &index->entries; data.new_entries = &entries; - data.entries_search = index->entries_search; + data.entry_cmp = index->entries_search; - git_vector_sort(&index->entries); + if (index_sort_if_needed(index, true) < 0) + return -1; error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); if (!error) { git_vector_sort(&entries); - git_index_clear(index); - git_vector_swap(&entries, &index->entries); + + if ((error = git_index_clear(index)) < 0) + /* well, this isn't good */; + else if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire index lock"); + error = -1; + } else { + git_vector_swap(&entries, &index->entries); + git_mutex_unlock(&index->lock); + } } git_vector_free(&entries); @@ -2247,7 +2400,7 @@ int git_index_add_all( /* skip ignored items that are not already in the index */ if ((flags & GIT_INDEX_ADD_FORCE) == 0 && git_iterator_current_is_ignored(wditer) && - git_index__find(&existing, index, wd->path, 0, 0) < 0) + index_find(&existing, index, wd->path, 0, 0, true) < 0) continue; /* issue notification callback if requested */ @@ -2269,17 +2422,14 @@ int git_index_add_all( break; /* make the new entry to insert */ - if ((entry = index_entry_dup(wd)) == NULL) { - error = -1; + if ((error = index_entry_dup(&entry, wd)) < 0) break; - } + entry->id = blobid; /* add working directory item to index */ - if ((error = index_insert(index, entry, 1)) < 0) { - index_entry_free(entry); + if ((error = index_insert(index, &entry, 1)) < 0) break; - } git_tree_cache_invalidate_path(index->tree, wd->path); @@ -2411,3 +2561,48 @@ int git_index_update_all( return error; } + +int git_index_snapshot_new(git_vector *snap, git_index *index) +{ + int error; + + GIT_REFCOUNT_INC(index); + + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } + + git_atomic_inc(&index->readers); + git_vector_sort(&index->entries); + + error = git_vector_dup(snap, &index->entries, index->entries._cmp); + + git_mutex_unlock(&index->lock); + + if (error < 0) + git_index_free(index); + + return error; +} + +void git_index_snapshot_release(git_vector *snap, git_index *index) +{ + git_vector_free(snap); + + git_atomic_dec(&index->readers); + + if (!git_mutex_lock(&index->lock)) { + index_free_deleted(index); /* try to free pending deleted items */ + git_mutex_unlock(&index->lock); + } + + git_index_free(index); +} + +int git_index_snapshot_find( + size_t *out, git_vector *entries, git_vector_cmp entry_srch, + const char *path, size_t path_len, int stage) +{ + return index_find_in_entries(out, entries, entry_srch, path, path_len, stage); +} diff --git a/src/index.h b/src/index.h index 17f04f0ad..50a0b4b6c 100644 --- a/src/index.h +++ b/src/index.h @@ -21,12 +21,15 @@ struct git_index { git_refcount rc; char *index_file_path; - git_futils_filestamp stamp; + 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 distrust_filemode:1; unsigned int no_symlinks:1; @@ -50,12 +53,21 @@ struct git_index_conflict_iterator { extern void git_index_entry__init_from_stat( 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); -extern int git_index_entry__cmp_icase(const void *a, const void *b); +/* Index entry search functions for search using a search spec */ +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); 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); +/* 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 diff --git a/src/iterator.c b/src/iterator.c index a7a44914c..63c14f962 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -644,6 +644,8 @@ typedef struct { git_iterator base; git_iterator_callbacks cb; git_index *index; + git_vector entries; + git_vector_cmp entry_srch; size_t current; /* when not in autoexpand mode, use these to represent "tree" state */ git_buf partial; @@ -654,10 +656,10 @@ typedef struct { 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)) { - ii->current = git_index_entrycount(ii->index); + ii->current = git_vector_length(&ii->entries); ie = NULL; } @@ -726,7 +728,7 @@ static int index_iterator__current( const git_index_entry **entry, git_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)) { 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) { 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( const git_index_entry **entry, git_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; if (!iterator__has_been_accessed(ii)) @@ -766,7 +768,7 @@ static int index_iterator__advance( while (ii->current < entrycount) { 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) break; } @@ -789,7 +791,7 @@ static int index_iterator__advance_into( const git_index_entry **entry, git_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 (ii->restore_terminator) @@ -815,8 +817,11 @@ static int index_iterator__reset( if (iterator__reset_range(self, start, end) < 0) return -1; - ii->current = ii->base.start ? - git_index__prefix_position(ii->index, ii->base.start) : 0; + ii->current = 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) return 0; @@ -841,9 +846,8 @@ static int index_iterator__reset( static void index_iterator__free(git_iterator *self) { index_iterator *ii = (index_iterator *)self; - git_index_free(ii->index); + git_index_snapshot_release(&ii->entries, ii->index); ii->index = NULL; - git_buf_free(&ii->partial); } @@ -854,18 +858,29 @@ int git_iterator_for_index( const char *start, const char *end) { + int error = 0; index_iterator *ii = git__calloc(1, sizeof(index_iterator)); 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)); - if (index->ignore_case) { - ii->base.flags |= GIT_ITERATOR_IGNORE_CASE; - ii->base.prefixcomp = git__prefixcmp_icase; + if ((error = iterator__update_ignore_case((git_iterator *)ii, flags)) < 0) { + git_iterator_free((git_iterator *)ii); + return error; } - ii->index = index; - GIT_REFCOUNT_INC(index); + ii->entry_srch = iterator__ignore_case(ii) ? + 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); ii->tree_entry.mode = GIT_FILEMODE_TREE; @@ -873,7 +888,6 @@ int git_iterator_for_index( index_iterator__reset((git_iterator *)ii, NULL, NULL); *iter = (git_iterator *)ii; - return 0; } diff --git a/src/merge.c b/src/merge.c index dd6a39f37..2e40b6db8 100644 --- a/src/merge.c +++ b/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_UNUSED(p); + return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_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); /* 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: if (cache != NULL) { @@ -1228,7 +1230,7 @@ done: return error; } -GIT_INLINE(int) index_entry_dup( +GIT_INLINE(int) index_entry_dup_pool( git_index_entry *out, git_pool *pool, 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) return NULL; - if (index_entry_dup(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || - index_entry_dup(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 || - index_entry_dup(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0) + if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || + index_entry_dup_pool(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 || + index_entry_dup_pool(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0) return NULL; 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)); 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); return error; @@ -1330,7 +1332,7 @@ int git_merge_diff_list__find_differences( { git_iterator *iterators[3] = {0}; 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}; int cur_item_modified; size_t i, j; @@ -2469,6 +2471,47 @@ done: return error; } +int git_merge__append_conflicts_to_merge_msg( + git_repository *repo, + git_index *index) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + const char *last = NULL; + size_t i; + int error; + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0) + goto cleanup; + + if (git_index_has_conflicts(index)) + git_filebuf_printf(&file, "\nConflicts:\n"); + + for (i = 0; i < git_index_entrycount(index); i++) { + const git_index_entry *e = git_index_get_byindex(index, i); + + if (git_index_entry_stage(e) == 0) + continue; + + if (last == NULL || strcmp(e->path, last) != 0) + git_filebuf_printf(&file, "\t%s\n", e->path); + + last = e->path; + } + + error = git_filebuf_commit(&file); + +cleanup: + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + + static int merge_state_cleanup(git_repository *repo) { const char *state_files[] = { @@ -2621,6 +2664,7 @@ int git_merge( if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], merge_opts)) < 0 || (error = git_merge__indexes(repo, index_new)) < 0 || (error = git_repository_index(&index_repo, repo)) < 0 || + (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 || (error = git_checkout_index(repo, index_repo, &checkout_opts)) < 0) goto on_error; diff --git a/src/merge.h b/src/merge.h index 2362da04d..00f6197bf 100644 --- a/src/merge.h +++ b/src/merge.h @@ -151,4 +151,6 @@ int git_merge__setup( int git_merge__indexes(git_repository *repo, git_index *index_new); +int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index); + #endif diff --git a/src/pathspec.c b/src/pathspec.c index 471488495..09650de7c 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -445,7 +445,7 @@ static int pathspec_match_from_iterator( /* check if path is ignored and untracked */ if (index != NULL && 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; /* mark the matched pattern as used */ diff --git a/src/repository.c b/src/repository.c index 49d1bc63e..6b2705bfa 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1955,24 +1955,32 @@ int git_repository_state(git_repository *repo) return state; } -int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len) +int git_repository__cleanup_files( + git_repository *repo, const char *files[], size_t files_len) { - git_buf path = GIT_BUF_INIT; + git_buf buf = GIT_BUF_INIT; size_t i; - int error = 0; + int error; - for (i = 0; i < files_len; ++i) { - git_buf_clear(&path); + for (error = 0, i = 0; !error && i < files_len; ++i) { + const char *path; - if ((error = git_buf_joinpath(&path, repo->path_repository, files[i])) < 0 || - (git_path_isfile(git_buf_cstr(&path)) && - (error = p_unlink(git_buf_cstr(&path))) < 0)) - goto done; - } + if (git_buf_joinpath(&buf, repo->path_repository, files[i]) < 0) + return -1; -done: - git_buf_free(&path); + path = git_buf_cstr(&buf); + + if (git_path_isfile(path)) { + error = p_unlink(path); + } else if (git_path_isdir(path)) { + error = git_futils_rmdir_r(path, NULL, + GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS); + } + + git_buf_clear(&buf); + } + git_buf_free(&buf); return error; } @@ -1982,6 +1990,9 @@ static const char *state_files[] = { GIT_MERGE_MSG_FILE, GIT_REVERT_HEAD_FILE, GIT_CHERRY_PICK_HEAD_FILE, + GIT_BISECT_LOG_FILE, + GIT_REBASE_MERGE_DIR, + GIT_REBASE_APPLY_DIR, }; int git_repository_state_cleanup(git_repository *repo) diff --git a/src/repository.h b/src/repository.h index e401a82b8..27eec9dd8 100644 --- a/src/repository.h +++ b/src/repository.h @@ -110,7 +110,7 @@ struct git_repository { git_submodule_cache *_submodules; git_cache objects; - git_attr_cache attrcache; + git_attr_cache *attrcache; git_diff_driver_registry *diff_drivers; char *path_repository; @@ -125,7 +125,7 @@ struct git_repository { 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); diff --git a/src/revert.c b/src/revert.c index 4039ec34c..29e124f6c 100644 --- a/src/revert.c +++ b/src/revert.c @@ -201,6 +201,7 @@ int git_revert( (error = git_revert_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 || (error = git_merge__indexes(repo, index_new)) < 0 || (error = git_repository_index(&index_repo, repo)) < 0 || + (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 || (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0) goto on_error; diff --git a/src/sortedcache.c b/src/sortedcache.c index 13f0921f1..c6b226153 100644 --- a/src/sortedcache.c +++ b/src/sortedcache.c @@ -20,7 +20,7 @@ int git_sortedcache_new( if (git_pool_init(&sc->pool, 1, 0) < 0 || git_vector_init(&sc->items, 4, item_cmp) < 0 || - (sc->map = git_strmap_alloc()) == NULL) + git_strmap_alloc(&sc->map) < 0) goto fail; if (git_rwlock_init(&sc->lock)) { @@ -39,8 +39,7 @@ int git_sortedcache_new( return 0; fail: - if (sc->map) - git_strmap_free(sc->map); + git_strmap_free(sc->map); git_vector_free(&sc->items); git_pool_clear(&sc->pool); git__free(sc); @@ -233,9 +232,8 @@ unlock: void git_sortedcache_updated(git_sortedcache *sc) { - /* update filestamp to latest value */ - if (git_futils_filestamp_check(&sc->stamp, sc->path) < 0) - giterr_clear(); + /* update filestamp to latest value */ + git_futils_filestamp_check(&sc->stamp, sc->path); } /* release all items in sorted cache */ diff --git a/src/strmap.h b/src/strmap.h index 8276ab468..8985aaf7e 100644 --- a/src/strmap.h +++ b/src/strmap.h @@ -22,7 +22,9 @@ typedef khiter_t git_strmap_iter; #define GIT__USE_STRMAP \ __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_clear(h) kh_clear(str, h) diff --git a/src/submodule.c b/src/submodule.c index bea096df5..5ddbfe828 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1638,8 +1638,7 @@ static int submodule_cache_alloc( return -1; } - cache->submodules = git_strmap_alloc(); - if (!cache->submodules) { + if (git_strmap_alloc(&cache->submodules) < 0) { submodule_cache_free(cache); return -1; } @@ -1694,8 +1693,6 @@ static int submodule_cache_refresh(git_submodule_cache *cache, int refresh) update_gitmod = (wd != NULL) ? git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr) : (cache->gitmodules_stamp.mtime != 0); - if (update_gitmod < 0) - giterr_clear(); } /* clear submodule flags that are to be refreshed */ diff --git a/src/thread-utils.h b/src/thread-utils.h index 914c1357d..50d8610a3 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -47,6 +47,12 @@ typedef git_atomic git_atomic_ssize; #define git_thread_exit(status) pthread_exit(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 */ #define git_mutex pthread_mutex_t #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_exit(status) (void)0 #define git_thread_join(id, status) (void)0 +#define git_thread_yield() (void)0 /* Pthreads Mutex */ #define git_mutex unsigned int diff --git a/src/transports/cred.c b/src/transports/cred.c index abae70b51..460ed04a2 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -11,31 +11,10 @@ int git_cred_has_username(git_cred *cred) { - int ret = 0; + if (cred->credtype == GIT_CREDTYPE_DEFAULT) + return 0; - switch (cred->credtype) { - case GIT_CREDTYPE_USERPASS_PLAINTEXT: { - git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; - ret = !!c->username; - break; - } - case GIT_CREDTYPE_SSH_KEY: { - git_cred_ssh_key *c = (git_cred_ssh_key *)cred; - ret = !!c->username; - break; - } - case GIT_CREDTYPE_SSH_CUSTOM: { - git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred; - ret = !!c->username; - break; - } - case GIT_CREDTYPE_DEFAULT: { - ret = 0; - break; - } - } - - return ret; + return 1; } static void plaintext_free(struct git_cred *cred) @@ -135,7 +114,7 @@ int git_cred_ssh_key_new( { git_cred_ssh_key *c; - assert(cred && privatekey); + assert(username && cred && privatekey); c = git__calloc(1, sizeof(git_cred_ssh_key)); GITERR_CHECK_ALLOC(c); @@ -143,10 +122,8 @@ int git_cred_ssh_key_new( c->parent.credtype = GIT_CREDTYPE_SSH_KEY; c->parent.free = ssh_key_free; - if (username) { - c->username = git__strdup(username); - GITERR_CHECK_ALLOC(c->username); - } + c->username = git__strdup(username); + GITERR_CHECK_ALLOC(c->username); c->privatekey = git__strdup(privatekey); GITERR_CHECK_ALLOC(c->privatekey); @@ -168,7 +145,7 @@ int git_cred_ssh_key_new( int git_cred_ssh_key_from_agent(git_cred **cred, const char *username) { git_cred_ssh_key *c; - assert(cred); + assert(username && cred); c = git__calloc(1, sizeof(git_cred_ssh_key)); GITERR_CHECK_ALLOC(c); @@ -176,10 +153,8 @@ int git_cred_ssh_key_from_agent(git_cred **cred, const char *username) { c->parent.credtype = GIT_CREDTYPE_SSH_KEY; c->parent.free = ssh_key_free; - if (username) { - c->username = git__strdup(username); - GITERR_CHECK_ALLOC(c->username); - } + c->username = git__strdup(username); + GITERR_CHECK_ALLOC(c->username); c->privatekey = NULL; @@ -197,7 +172,7 @@ int git_cred_ssh_custom_new( { git_cred_ssh_custom *c; - assert(cred); + assert(username && cred); c = git__calloc(1, sizeof(git_cred_ssh_custom)); GITERR_CHECK_ALLOC(c); @@ -205,10 +180,8 @@ int git_cred_ssh_custom_new( c->parent.credtype = GIT_CREDTYPE_SSH_CUSTOM; c->parent.free = ssh_custom_free; - if (username) { - c->username = git__strdup(username); - GITERR_CHECK_ALLOC(c->username); - } + c->username = git__strdup(username); + GITERR_CHECK_ALLOC(c->username); if (publickey_len > 0) { c->publickey = git__malloc(publickey_len); diff --git a/src/transports/ssh.c b/src/transports/ssh.c index bece0b45d..879af9059 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -282,7 +282,6 @@ shutdown: static int _git_ssh_authenticate_session( LIBSSH2_SESSION* session, - const char *user, git_cred* cred) { int rc; @@ -291,13 +290,11 @@ static int _git_ssh_authenticate_session( switch (cred->credtype) { case GIT_CREDTYPE_USERPASS_PLAINTEXT: { git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; - user = c->username ? c->username : user; - rc = libssh2_userauth_password(session, user, c->password); + rc = libssh2_userauth_password(session, c->username, c->password); break; } case GIT_CREDTYPE_SSH_KEY: { git_cred_ssh_key *c = (git_cred_ssh_key *)cred; - user = c->username ? c->username : user; if (c->privatekey) rc = libssh2_userauth_publickey_fromfile( @@ -311,7 +308,6 @@ static int _git_ssh_authenticate_session( case GIT_CREDTYPE_SSH_CUSTOM: { git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred; - user = c->username ? c->username : user; rc = libssh2_userauth_publickey( session, c->username, (const unsigned char *)c->publickey, c->publickey_len, c->sign_callback, &c->sign_data); @@ -415,15 +411,10 @@ static int _git_ssh_setup_conn( } assert(t->cred); - if (!user && !git_cred_has_username(t->cred)) { - giterr_set_str(GITERR_NET, "Cannot authenticate without a username"); - goto on_error; - } - if (_git_ssh_session_create(&session, s->socket) < 0) goto on_error; - if (_git_ssh_authenticate_session(session, user, t->cred) < 0) + if (_git_ssh_authenticate_session(session, t->cred) < 0) goto on_error; channel = libssh2_channel_open_session(session); diff --git a/src/tree-cache.c b/src/tree-cache.c index 1d3997154..49afd6e49 100644 --- a/src/tree-cache.c +++ b/src/tree-cache.c @@ -7,23 +7,16 @@ #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; - const char *end; - - end = strchr(path, '/'); - if (end == NULL) { - end = strrchr(path, '\0'); - } - - dirlen = end - path; + size_t i, dirlen = end ? (size_t)(end - path) : strlen(path); 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)) - return tree->children[i]; + if (child->namelen == dirlen && !memcmp(path, child->name, dirlen)) + return child; } 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 */ break; - tree = find_child(tree, ptr); + tree = find_child(tree, ptr, end); if (tree == NULL) /* We don't have that tree */ return; @@ -64,10 +57,9 @@ const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char while (1) { end = strchr(ptr, '/'); - tree = find_child(tree, ptr); - if (tree == NULL) { /* Can't find it */ + tree = find_child(tree, ptr, end); + if (tree == NULL) /* Can't find it */ return NULL; - } if (end == NULL || *end + 1 == '\0') return tree; @@ -100,6 +92,7 @@ static int read_tree_internal(git_tree_cache **out, tree->parent = parent; /* NUL-terminated tree name */ + tree->namelen = name_len; memcpy(tree->name, name_start, name_len); tree->name[name_len] = '\0'; diff --git a/src/tree-cache.h b/src/tree-cache.h index 805483a78..90c82dbbf 100644 --- a/src/tree-cache.h +++ b/src/tree-cache.h @@ -18,6 +18,7 @@ struct git_tree_cache { ssize_t entries; git_oid oid; + size_t namelen; char name[GIT_FLEX_ARRAY]; }; diff --git a/src/userdiff.h b/src/userdiff.h index 7eb095246..523f2f8d4 100644 --- a/src/userdiff.h +++ b/src/userdiff.h @@ -45,13 +45,13 @@ typedef struct { static git_diff_driver_definition builtin_defs[] = { IPATTERN("ada", - "!^(.*[ \t])?(is new|renames|is separate)([ \t].*)?$\n" + "!^(.*[ \t])?(is[ \t]+new|renames|is[ \t]+separate)([ \t].*)?$\n" "!^[ \t]*with[ \t].*$\n" "^[ \t]*((procedure|function)[ \t]+.*)$\n" "^[ \t]*((package|protected|task)[ \t]+.*)$", /* -- */ "[a-zA-Z][a-zA-Z0-9_]*" - "|[0-9][-+0-9#_.eE]" + "|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?" "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"), IPATTERN("fortran", @@ -159,15 +159,13 @@ PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", PATTERNS("cpp", /* Jump targets or access declarations */ - "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n" - /* C/++ functions/methods at top level */ - "^([A-Za-z_][A-Za-z_0-9]*([ \t*]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n" - /* compound type at top level */ - "^((struct|class|enum)[^;]*)$", + "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n" + /* functions/methods, variables, and compounds at top level */ + "^((::[[:space:]]*)?[A-Za-z_].*)$", /* -- */ "[a-zA-Z_][a-zA-Z0-9_]*" - "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" - "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"), PATTERNS("csharp", /* Keywords */ diff --git a/src/util.c b/src/util.c index 3767b890c..39858254f 100644 --- a/src/util.c +++ b/src/util.c @@ -542,10 +542,12 @@ int git__bsearch_r( */ int git__strcmp_cb(const void *a, const void *b) { - const char *stra = (const char *)a; - const char *strb = (const char *)b; + return strcmp((const char *)a, (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) diff --git a/src/util.h b/src/util.h index e378786d9..d94463c65 100644 --- a/src/util.h +++ b/src/util.h @@ -20,6 +20,8 @@ # define max(a,b) ((a) > (b) ? (a) : (b)) #endif +#define GIT_DATE_RFC2822_SZ 32 + /* * Custom memory allocation wrappers * that set error code and error message @@ -194,6 +196,7 @@ extern int git__bsearch_r( size_t *position); 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__strcasecmp(const char *a, const char *b); @@ -329,6 +332,16 @@ extern int git__parse_bool(int *out, const char *value); extern int git__date_parse(git_time_t *out, const char *date); /* + * Format a git_time as a RFC2822 string + * + * @param out buffer to store formatted date; a '\\0' terminator will automatically be added. + * @param len size of the buffer; should be atleast `GIT_DATE_RFC2822_SZ` in size; + * @param date the date to be formatted + * @return 0 if successful; -1 on error + */ +extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date); + +/* * Unescapes a string in-place. * * Edge cases behavior: diff --git a/src/vector.c b/src/vector.c index 37ea07f0b..c769b696a 100644 --- a/src/vector.c +++ b/src/vector.c @@ -177,7 +177,8 @@ void git_vector_sort(git_vector *v) if (git_vector_is_sorted(v) || !v->_cmp) return; - git__tsort(v->contents, v->length, v->_cmp); + if (v->length > 1) + git__tsort(v->contents, v->length, v->_cmp); git_vector_set_sorted(v, 1); } @@ -276,14 +277,16 @@ void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *)) } 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; for (i = 0, j = 0; j < v->length; ++j) { v->contents[i] = v->contents[j]; - if (!match(v, i)) + if (!match(v, i, payload)) i++; } @@ -339,3 +342,18 @@ int git_vector_set(void **old, git_vector *v, size_t position, void *value) 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; +} diff --git a/src/vector.h b/src/vector.h index 682b6ad27..aac46c4b3 100644 --- a/src/vector.h +++ b/src/vector.h @@ -85,8 +85,11 @@ int git_vector_insert_sorted(git_vector *v, void *element, int git_vector_remove(git_vector *v, size_t idx); void git_vector_pop(git_vector *v); void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *)); + 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_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 diff --git a/tests/attr/file.c b/tests/attr/file.c index 4eb1d22fe..1f4108c3c 100644 --- a/tests/attr/file.c +++ b/tests/attr/file.c @@ -11,9 +11,9 @@ void test_attr_file__simple_read(void) git_attr_assignment *assign; git_attr_rule *rule; - cl_git_pass(git_attr_file__new_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); rule = get_rule(0); @@ -37,9 +37,9 @@ void test_attr_file__match_variants(void) git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__new_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); /* 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_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); 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_assignment *assign; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3"))); - cl_assert_equal_s(cl_fixture("attr/attr3"), file->key + 2); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3"))); + cl_assert_equal_s(cl_fixture("attr/attr3"), file->entry->path); cl_assert(file->rules.length == 3); rule = get_rule(0); diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c index 4ed92387c..a83c5bd74 100644 --- a/tests/attr/ignore.c +++ b/tests/attr/ignore.c @@ -130,22 +130,18 @@ void test_attr_ignore__skip_gitignore_directory(void) void test_attr_ignore__expand_tilde_to_homedir(void) { - git_buf path = GIT_BUF_INIT; + git_buf cleanup = GIT_BUF_INIT; git_config *cfg; assert_is_ignored(false, "example.global_with_tilde"); - /* construct fake home with fake global excludes */ - - cl_must_pass(p_mkdir("home", 0777)); - cl_git_pass(git_path_prettify(&path, "home", NULL)); - cl_git_pass(git_libgit2_opts( - GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_fake_home(&cleanup); - cl_git_mkfile("home/globalexcludes", "# found me\n*.global_with_tilde\n"); + /* construct fake home with fake global excludes */ + cl_git_mkfile("home/globalexclude", "# found me\n*.global_with_tilde\n"); cl_git_pass(git_repository_config(&cfg, g_repo)); - cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexcludes")); + cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexclude")); git_config_free(cfg); git_attr_cache_flush(g_repo); /* must reset to pick up change */ @@ -154,8 +150,9 @@ void test_attr_ignore__expand_tilde_to_homedir(void) cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES)); - cl_git_pass(git_libgit2_opts( - GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL)); + cl_fake_home_cleanup(&cleanup); - git_buf_free(&path); + git_attr_cache_flush(g_repo); /* must reset to pick up change */ + + assert_is_ignored(false, "example.global_with_tilde"); } diff --git a/tests/attr/lookup.c b/tests/attr/lookup.c index 200bdd2c7..030ea075d 100644 --- a/tests/attr/lookup.c +++ b/tests/attr/lookup.c @@ -9,8 +9,8 @@ void test_attr_lookup__simple(void) git_attr_path path; const char *value = NULL; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr0"))); - cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0"))); + cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path); cl_assert(file->rules.length == 1); 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 } }; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr1"))); - cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1"))); + cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path); cl_assert(file->rules.length == 10); 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 } }; - 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); run_test_cases(file, cases, 0); @@ -225,7 +225,7 @@ void test_attr_lookup__check_attr_examples(void) { 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); run_test_cases(file, cases, 0); @@ -250,9 +250,9 @@ void test_attr_lookup__from_buffer(void) { 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); diff --git a/tests/attr/repo.c b/tests/attr/repo.c index 49cccdc5a..71dc7a5b5 100644 --- a/tests/attr/repo.c +++ b/tests/attr/repo.c @@ -68,9 +68,12 @@ void test_attr_repo__get_one(void) 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(g_repo, 0, ".gitattributes")); - 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, ".git/info/attributes")); + 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) diff --git a/tests/cherrypick/bare.c b/tests/cherrypick/bare.c new file mode 100644 index 000000000..7ac1054a1 --- /dev/null +++ b/tests/cherrypick/bare.c @@ -0,0 +1,106 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "fileops.h" +#include "git2/cherrypick.h" + +#include "../merge/merge_helpers.h" + +#define TEST_REPO_PATH "cherrypick" + +static git_repository *repo; + +void test_cherrypick_bare__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_cherrypick_bare__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_cherrypick_bare__automerge(void) +{ + git_commit *head = NULL, *commit = NULL; + git_index *index = NULL; + git_oid head_oid, cherry_oid; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" }, + { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" }, + { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" }, + }; + + git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + + git_oid_fromstr(&cherry_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherry_pick_commit(&index, repo, commit, head, 0, NULL)); + cl_assert(merge_test_index(index, merge_index_entries, 3)); + + git_index_free(index); + git_commit_free(head); + git_commit_free(commit); +} + +void test_cherrypick_bare__conflicts(void) +{ + git_commit *head = NULL, *commit = NULL; + git_index *index = NULL; + git_oid head_oid, cherry_oid; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" }, + { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" }, + { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" }, + { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" }, + }; + + git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + + git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherry_pick_commit(&index, repo, commit, head, 0, NULL)); + cl_assert(merge_test_index(index, merge_index_entries, 7)); + + git_index_free(index); + git_commit_free(head); + git_commit_free(commit); +} + +void test_cherrypick_bare__orphan(void) +{ + git_commit *head = NULL, *commit = NULL; + git_index *index = NULL; + git_oid head_oid, cherry_oid; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" }, + { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" }, + { 0100644, "85a4a1d791973644f24c72f5e89420d3064cc452", 0, "file3.txt" }, + { 0100644, "9ccb9bf50c011fd58dcbaa65df917bf79539717f", 0, "orphan.txt" }, + }; + + git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + + git_oid_fromstr(&cherry_oid, "74f06b5bfec6d33d7264f73606b57a7c0b963819"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherry_pick_commit(&index, repo, commit, head, 0, NULL)); + cl_assert(merge_test_index(index, merge_index_entries, 4)); + + git_index_free(index); + git_commit_free(head); + git_commit_free(commit); +} + diff --git a/tests/cherrypick/workdir.c b/tests/cherrypick/workdir.c new file mode 100644 index 000000000..581a5f997 --- /dev/null +++ b/tests/cherrypick/workdir.c @@ -0,0 +1,429 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "fileops.h" +#include "git2/cherrypick.h" + +#include "../merge/merge_helpers.h" + +#define TEST_REPO_PATH "cherrypick" + +static git_repository *repo; +static git_index *repo_index; + +// Fixture setup and teardown +void test_cherrypick_workdir__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); + git_repository_index(&repo_index, repo); +} + +void test_cherrypick_workdir__cleanup(void) +{ + git_index_free(repo_index); + cl_git_sandbox_cleanup(); +} + +/* git reset --hard d3d77487660ee3c0194ee01dc5eaf478782b1c7e + * git cherry-pick cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick 964ea3da044d9083181a88ba6701de9e35778bf4 + * git cherry-pick a43a050c588d4e92f11a6b139680923e9728477d + */ +void test_cherrypick_workdir__automerge(void) +{ + git_oid head_oid; + git_signature *signature = NULL; + size_t i; + + const char *cherry_pick_oids[] = { + "cfc4f0999a8367568e049af4f72e452d40828a15", + "964ea3da044d9083181a88ba6701de9e35778bf4", + "a43a050c588d4e92f11a6b139680923e9728477d", + }; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" }, + { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" }, + { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" }, + + { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" }, + { 0100644, "bd8fc3c59fb52d3c8b5907ace7defa5803f82419", 0, "file2.txt" }, + { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" }, + + { 0100644, "f06427bee380364bc7e0cb26a9245158e4726ce0", 0, "file1.txt" }, + { 0100644, "bd8fc3c59fb52d3c8b5907ace7defa5803f82419", 0, "file2.txt" }, + { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" }, + }; + + cl_git_pass(git_signature_new(&signature, "Picker", "picker@example.org", time(NULL), 0)); + + git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e"); + + for (i = 0; i < 3; ++i) { + git_commit *head = NULL, *commit = NULL; + git_oid cherry_oid, cherry_picked_oid, cherry_picked_tree_oid; + git_tree *cherry_picked_tree = NULL; + + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, cherry_pick_oids[i]); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherry_pick(repo, commit, NULL)); + + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD")); + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + + cl_git_pass(git_index_write_tree(&cherry_picked_tree_oid, repo_index)); + cl_git_pass(git_tree_lookup(&cherry_picked_tree, repo, &cherry_picked_tree_oid)); + cl_git_pass(git_commit_create(&cherry_picked_oid, repo, "HEAD", signature, signature, NULL, + "Cherry picked!", cherry_picked_tree, 1, (const git_commit **)&head)); + + cl_assert(merge_test_index(repo_index, merge_index_entries + i * 3, 3)); + + git_oid_cpy(&head_oid, &cherry_picked_oid); + + git_tree_free(cherry_picked_tree); + git_commit_free(head); + git_commit_free(commit); + } + + git_signature_free(signature); +} + +/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8 + * git cherry-pick e9b63f3655b2ad80c0ff587389b5a9589a3a7110 + */ +void test_cherrypick_workdir__conflicts(void) +{ + git_commit *head = NULL, *commit = NULL; + git_oid head_oid, cherry_oid; + git_buf conflicting_buf = GIT_BUF_INIT, mergemsg_buf = GIT_BUF_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" }, + { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" }, + { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" }, + { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" }, + }; + + git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8"); + + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherry_pick(repo, commit, NULL)); + + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD")); + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 7)); + + cl_git_pass(git_futils_readbuffer(&mergemsg_buf, + TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(strcmp(git_buf_cstr(&mergemsg_buf), + "Change all files\n" \ + "\n" \ + "Conflicts:\n" \ + "\tfile2.txt\n" \ + "\tfile3.txt\n") == 0); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/file2.txt")); + + cl_assert(strcmp(git_buf_cstr(&conflicting_buf), + "!File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2!!\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "<<<<<<< HEAD\n" \ + "File 2\n" \ + "=======\n" \ + "File 2!\n" \ + "File 2\n" \ + "File 2!\n" \ + ">>>>>>> e9b63f3... Change all files\n") == 0); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/file3.txt")); + + cl_assert(strcmp(git_buf_cstr(&conflicting_buf), + "!File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3!!\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "<<<<<<< HEAD\n" \ + "=======\n" \ + "File 3!\n" \ + "File 3!\n" \ + ">>>>>>> e9b63f3... Change all files\n") == 0); + + git_commit_free(commit); + git_commit_free(head); + git_buf_free(&mergemsg_buf); + git_buf_free(&conflicting_buf); +} + +/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8 + * git cherry-pick -X ours e9b63f3655b2ad80c0ff587389b5a9589a3a7110 + */ +void test_cherrypick_workdir__conflict_use_ours(void) +{ + git_commit *head = NULL, *commit = NULL; + git_oid head_oid, cherry_oid; + git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" }, + { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" }, + { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" }, + { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" }, + }; + + struct merge_index_entry merge_filesystem_entries[] = { + { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" }, + { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 0, "file2.txt" }, + { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 0, "file3.txt" }, + }; + + /* leave the index in a conflicted state, but checkout "ours" to the workdir */ + opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS; + + git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8"); + + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherry_pick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 7)); + cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3)); + + /* resolve conflicts in the index by taking "ours" */ + opts.merge_opts.file_favor = GIT_MERGE_FILE_FAVOR_OURS; + + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + cl_git_pass(git_cherry_pick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_filesystem_entries, 3)); + cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick 2a26c7e88b285613b302ba76712bc998863f3cbc + */ +void test_cherrypick_workdir__rename(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt.renamed" }, + }; + + opts.merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.rename_threshold = 50; + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherry_pick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard 44cd2ed2052c9c68f9a439d208e9614dc2a55c70 + * git cherry-pick 2a26c7e88b285613b302ba76712bc998863f3cbc + */ +void test_cherrypick_workdir__both_renamed(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + git_buf mergemsg_buf = GIT_BUF_INIT; + git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 1, "file3.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt.renamed" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 2, "file3.txt.renamed_on_branch" }, + }; + + opts.merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.rename_threshold = 50; + + git_oid_fromstr(&head_oid, "44cd2ed2052c9c68f9a439d208e9614dc2a55c70"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherry_pick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 5)); + + cl_git_pass(git_futils_readbuffer(&mergemsg_buf, + TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(strcmp(git_buf_cstr(&mergemsg_buf), + "Renamed file3.txt -> file3.txt.renamed\n" \ + "\n" \ + "Conflicts:\n" \ + "\tfile3.txt\n" \ + "\tfile3.txt.renamed\n" \ + "\tfile3.txt.renamed_on_branch\n") == 0); + + git_buf_free(&mergemsg_buf); + git_commit_free(commit); + git_commit_free(head); +} + +void test_cherrypick_workdir__nonmerge_fails_mainline_specified(void) +{ + git_reference *head; + git_commit *commit; + git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT; + + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJ_COMMIT)); + + opts.mainline = 1; + cl_must_fail(git_cherry_pick(repo, commit, &opts)); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD")); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + + git_reference_free(head); + git_commit_free(commit); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick abe4603bc7cd5b8167a267e0e2418fd2348f8cff + */ +void test_cherrypick_workdir__merge_fails_without_mainline_specified(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_must_fail(git_cherry_pick(repo, commit, NULL)); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD")); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick -m1 abe4603bc7cd5b8167a267e0e2418fd2348f8cff + */ +void test_cherrypick_workdir__merge_first_parent(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "f90f9dcbdac2cce5cc166346160e19cb693ef4e8", 0, "file1.txt" }, + { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 0, "file2.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 0, "file3.txt" }, + }; + + opts.mainline = 1; + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherry_pick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick -m2 abe4603bc7cd5b8167a267e0e2418fd2348f8cff + */ +void test_cherrypick_workdir__merge_second_parent(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "487434cace79238a7091e2220611d4f20a765690", 0, "file1.txt" }, + { 0100644, "e5183bfd18e3a0a691fadde2f0d5610b73282d31", 0, "file2.txt" }, + { 0100644, "409a1bec58bf35348e8b62b72bb9c1f45cf5a587", 0, "file3.txt" }, + }; + + opts.mainline = 2; + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherry_pick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(commit); + git_commit_free(head); +} + diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index 9b062ef78..90e53c5e6 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -490,3 +490,24 @@ void clar__assert_equal_file( clar__assert_equal(file, line, "mismatched file length", 1, "%"PRIuZ, (size_t)expected_bytes, (size_t)total_bytes); } + +void cl_fake_home(git_buf *restore) +{ + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_libgit2_opts( + GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore)); + + cl_must_pass(p_mkdir("home", 0777)); + cl_git_pass(git_path_prettify(&path, "home", NULL)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + git_buf_free(&path); +} + +void cl_fake_home_cleanup(git_buf *restore) +{ + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore->ptr)); + git_buf_free(restore); +} diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h index 3de80bfa0..d395bd66f 100644 --- a/tests/clar_libgit2.h +++ b/tests/clar_libgit2.h @@ -11,7 +11,7 @@ * * 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 { \ int _lg2_error; \ @@ -120,4 +120,7 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg); void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value); +void cl_fake_home(git_buf *restore); +void cl_fake_home_cleanup(git_buf *restore); + #endif diff --git a/tests/core/strmap.c b/tests/core/strmap.c index f34a4f89f..a120f1feb 100644 --- a/tests/core/strmap.c +++ b/tests/core/strmap.c @@ -3,12 +3,22 @@ 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) { - git_strmap *table = git_strmap_alloc(); - cl_assert(table != NULL); - cl_assert(git_strmap_num_entries(table) == 0); - git_strmap_free(table); + cl_assert(git_strmap_num_entries(g_table) == 0); } static void insert_strings(git_strmap *table, int count) @@ -37,21 +47,17 @@ void test_core_strmap__1(void) { int i; 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(table, "ggggggggg")); - cl_assert(!git_strmap_exists(table, "aaaaaaaab")); - cl_assert(!git_strmap_exists(table, "abcdefghi")); + cl_assert(git_strmap_exists(g_table, "aaaaaaaaa")); + cl_assert(git_strmap_exists(g_table, "ggggggggg")); + cl_assert(!git_strmap_exists(g_table, "aaaaaaaab")); + cl_assert(!git_strmap_exists(g_table, "abcdefghi")); 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); - - git_strmap_free(table); } void test_core_strmap__2(void) @@ -59,44 +65,36 @@ void test_core_strmap__2(void) khiter_t pos; int i; 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(table, "ggggggggg")); - cl_assert(!git_strmap_exists(table, "aaaaaaaab")); - cl_assert(!git_strmap_exists(table, "abcdefghi")); + cl_assert(git_strmap_exists(g_table, "aaaaaaaaa")); + cl_assert(git_strmap_exists(g_table, "ggggggggg")); + cl_assert(!git_strmap_exists(g_table, "aaaaaaaab")); + cl_assert(!git_strmap_exists(g_table, "abcdefghi")); - cl_assert(git_strmap_exists(table, "bbbbbbbbb")); - pos = git_strmap_lookup_index(table, "bbbbbbbbb"); - cl_assert(git_strmap_valid_index(table, pos)); - cl_assert_equal_s(git_strmap_value_at(table, pos), "bbbbbbbbb"); - free(git_strmap_value_at(table, pos)); - git_strmap_delete_at(table, pos); + cl_assert(git_strmap_exists(g_table, "bbbbbbbbb")); + pos = git_strmap_lookup_index(g_table, "bbbbbbbbb"); + cl_assert(git_strmap_valid_index(g_table, pos)); + cl_assert_equal_s(git_strmap_value_at(g_table, pos), "bbbbbbbbb"); + free(git_strmap_value_at(g_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; - git_strmap_foreach_value(table, str, { i++; free(str); }); + git_strmap_foreach_value(g_table, str, { i++; free(str); }); cl_assert(i == 19); - - git_strmap_free(table); } void test_core_strmap__3(void) { int i; char *str; - git_strmap *table = git_strmap_alloc(); - cl_assert(table != NULL); - insert_strings(table, 10000); + insert_strings(g_table, 10000); 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); - - git_strmap_free(table); } diff --git a/tests/core/vector.c b/tests/core/vector.c index db52c004f..66f90b82b 100644 --- a/tests/core/vector.c +++ b/tests/core/vector.c @@ -190,8 +190,9 @@ void test_core_vector__5(void) 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); } @@ -206,7 +207,7 @@ void test_core_vector__remove_matching(void) git_vector_insert(&x, (void*) 0x001); 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); git_vector_insert(&x, (void*) 0x001); @@ -214,7 +215,7 @@ void test_core_vector__remove_matching(void) git_vector_insert(&x, (void*) 0x001); 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); git_vector_insert(&x, (void*) 0x002); @@ -223,7 +224,7 @@ void test_core_vector__remove_matching(void) git_vector_insert(&x, (void*) 0x001); 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); git_vector_foreach(&x, i, compare) { @@ -238,7 +239,7 @@ void test_core_vector__remove_matching(void) git_vector_insert(&x, (void*) 0x001); 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); git_vector_foreach(&x, i, compare) { @@ -253,7 +254,7 @@ void test_core_vector__remove_matching(void) git_vector_insert(&x, (void*) 0x001); 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); git_vector_foreach(&x, i, compare) { @@ -268,7 +269,7 @@ void test_core_vector__remove_matching(void) git_vector_insert(&x, (void*) 0x003); 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); git_vector_free(&x); diff --git a/tests/date/rfc2822.c b/tests/date/rfc2822.c new file mode 100644 index 000000000..eda475ac9 --- /dev/null +++ b/tests/date/rfc2822.c @@ -0,0 +1,40 @@ +#include "clar_libgit2.h" + +#include "util.h" + +void test_date_rfc2822__format_rfc2822_no_offset(void) +{ + git_time t = {1397031663, 0}; + char buf[GIT_DATE_RFC2822_SZ]; + + cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t)); + cl_assert(strcmp(buf, "Wed, 9 Apr 2014 08:21:03 +0000") == 0); +} + +void test_date_rfc2822__format_rfc2822_positive_offset(void) +{ + git_time t = {1397031663, 120}; + char buf[GIT_DATE_RFC2822_SZ]; + + cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t)); + cl_assert(strcmp(buf, "Wed, 9 Apr 2014 10:21:03 +0200") == 0); +} + +void test_date_rfc2822__format_rfc2822_negative_offset(void) +{ + git_time t = {1397031663, -120}; + char buf[GIT_DATE_RFC2822_SZ]; + + cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t)); + cl_assert(strcmp(buf, "Wed, 9 Apr 2014 06:21:03 -0200") == 0); +} + +void test_date_rfc2822__format_rfc2822_buffer_too_small(void) +{ + // "Wed, 10 Apr 2014 08:21:03 +0000" + git_time t = {1397031663 + 86400, 0}; + char buf[GIT_DATE_RFC2822_SZ-1]; + + cl_git_fail(git__date_rfc2822_fmt(buf, sizeof(buf), &t)); +} + diff --git a/tests/diff/diff_helpers.c b/tests/diff/diff_helpers.c index 33bb561f6..279cb20c5 100644 --- a/tests/diff/diff_helpers.c +++ b/tests/diff/diff_helpers.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "diff_helpers.h" +#include "git2/sys/diff.h" git_tree *resolve_commit_oid_to_tree( git_repository *repo, @@ -215,32 +216,16 @@ abort: 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) { - cl_git_pass(git_diff_print( - diff, GIT_DIFF_FORMAT_PATCH, diff_print_cb, fp ? fp : stderr)); + cl_git_pass( + 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) { - cl_git_pass(git_diff_print( - diff, GIT_DIFF_FORMAT_RAW, diff_print_cb, fp ? fp : stderr)); + cl_git_pass( + git_diff_print(diff, GIT_DIFF_FORMAT_RAW, + git_diff_print_callback__to_file_handle, fp ? fp : stderr)); } diff --git a/tests/diff/format_email.c b/tests/diff/format_email.c new file mode 100644 index 000000000..3260fdea8 --- /dev/null +++ b/tests/diff/format_email.c @@ -0,0 +1,556 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "commit.h" +#include "diff.h" + +static git_repository *repo; + +void test_diff_format_email__initialize(void) +{ + repo = cl_git_sandbox_init("diff_format_email"); +} + +void test_diff_format_email__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_diff_format_email__simple(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + const char *email = + "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ + "Subject: [PATCH] Modify some content\n" \ + "\n" \ + "---\n" \ + " file1.txt | 8 +++++---\n" \ + " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "index 94aaae8..af8f41d 100644\n" \ + "--- a/file1.txt\n" \ + "+++ b/file1.txt\n" \ + "@@ -1,15 +1,17 @@\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+\n" \ + "+\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_buf_clear(&buf); + cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__multiple(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + const char *email = + "From 10808fe9c9be5a190c0ba68d1a002233fb363508 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Thu, 10 Apr 2014 19:37:05 +0200\n" \ + "Subject: [PATCH 1/2] Added file2.txt file3.txt\n" \ + "\n" \ + "---\n" \ + " file2.txt | 5 +++++\n" \ + " file3.txt | 5 +++++\n" \ + " 2 files changed, 10 insertions(+), 0 deletions(-)\n" \ + " create mode 100644 file2.txt\n" \ + " create mode 100644 file3.txt\n" \ + "\n" \ + "diff --git a/file2.txt b/file2.txt\n" \ + "new file mode 100644\n" \ + "index 0000000..e909123\n" \ + "--- /dev/null\n" \ + "+++ b/file2.txt\n" \ + "@@ -0,0 +1,5 @@\n" \ + "+file2\n" \ + "+file2\n" \ + "+file2\n" \ + "+file2\n" \ + "+file2\n" \ + "diff --git a/file3.txt b/file3.txt\n" \ + "new file mode 100644\n" \ + "index 0000000..9435022\n" \ + "--- /dev/null\n" \ + "+++ b/file3.txt\n" \ + "@@ -0,0 +1,5 @@\n" \ + "+file3\n" \ + "+file3\n" \ + "+file3\n" \ + "+file3\n" \ + "+file3\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n" \ + "From 873806f6f27e631eb0b23e4b56bea2bfac14a373 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Thu, 10 Apr 2014 19:37:36 +0200\n" \ + "Subject: [PATCH 2/2] Modified file2.txt, file3.txt\n" \ + "\n" \ + "---\n" \ + " file2.txt | 2 +-\n" \ + " file3.txt | 2 +-\n" \ + " 2 files changed, 2 insertions(+), 2 deletions(-)\n" \ + "\n" \ + "diff --git a/file2.txt b/file2.txt\n" \ + "index e909123..7aff11d 100644\n" \ + "--- a/file2.txt\n" \ + "+++ b/file2.txt\n" \ + "@@ -1,5 +1,5 @@\n" \ + " file2\n" \ + " file2\n" \ + " file2\n" \ + "-file2\n" \ + "+file2!\n" \ + " file2\n" \ + "diff --git a/file3.txt b/file3.txt\n" \ + "index 9435022..9a2d780 100644\n" \ + "--- a/file3.txt\n" \ + "+++ b/file3.txt\n" \ + "@@ -1,5 +1,5 @@\n" \ + " file3\n" \ + "-file3\n" \ + "+file3!\n" \ + " file3\n" \ + " file3\n" \ + " file3\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + git_oid_fromstr(&oid, "10808fe9c9be5a190c0ba68d1a002233fb363508"); + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + opts.patch_no = 1; + opts.total_patches = 2; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + + git_diff_free(diff); + git_commit_free(commit); + diff = NULL; + commit = NULL; + + git_oid_fromstr(&oid, "873806f6f27e631eb0b23e4b56bea2bfac14a373"); + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + opts.patch_no = 2; + opts.total_patches = 2; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__exclude_marker(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + const char *email = + "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ + "Subject: Modify some content\n" \ + "\n" \ + "---\n" \ + " file1.txt | 8 +++++---\n" \ + " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "index 94aaae8..af8f41d 100644\n" \ + "--- a/file1.txt\n" \ + "+++ b/file1.txt\n" \ + "@@ -1,15 +1,17 @@\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+\n" \ + "+\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + + opts.flags |= GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_buf_clear(&buf); + cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, + GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER, NULL)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__invalid_no(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + opts.patch_no = 2; + opts.total_patches = 1; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_fail(git_diff_format_email(&buf, diff, &opts)); + cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 2, 1, 0, NULL)); + cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 0, 0, 0, NULL)); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__mode_change(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + const char *email = + "From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Thu, 10 Apr 2014 10:05:03 +0200\n" \ + "Subject: [PATCH] Update permissions\n" \ + "\n" \ + "---\n" \ + " file1.txt.renamed | 0\n" \ + " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \ + " mode change 100644 => 100755 file1.txt.renamed\n" \ + "\n" \ + "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \ + "old mode 100644\n" \ + "new mode 100755\n" \ + "index a97157a..a97157a\n" \ + "--- a/file1.txt.renamed\n" \ + "+++ b/file1.txt.renamed\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + git_oid_fromstr(&oid, "7ade76dd34bba4733cf9878079f9fd4a456a9189"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_buf_clear(&buf); + cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__rename_add_remove(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + const char *email = + "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \ + "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \ + "\n" \ + "---\n" \ + " file1.txt | 17 -----------------\n" \ + " file1.txt.renamed | 17 +++++++++++++++++\n" \ + " 2 files changed, 17 insertions(+), 17 deletions(-)\n" \ + " delete mode 100644 file1.txt\n" \ + " create mode 100644 file1.txt.renamed\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "deleted file mode 100644\n" \ + "index af8f41d..0000000\n" \ + "--- a/file1.txt\n" \ + "+++ /dev/null\n" \ + "@@ -1,17 +0,0 @@\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-_file1.txt_\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-\n" \ + "-\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-_file1.txt_\n" \ + "-_file1.txt_\n" \ + "-file1.txt\n" \ + "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \ + "new file mode 100644\n" \ + "index 0000000..a97157a\n" \ + "--- /dev/null\n" \ + "+++ b/file1.txt.renamed\n" \ + "@@ -0,0 +1,17 @@\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+_file1.txt_\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+file1.txt_renamed\n" \ + "+file1.txt\n" \ + "+\n" \ + "+\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+file1.txt_renamed\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + "+file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + git_oid_fromstr(&oid, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_buf_clear(&buf); + cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__multiline_summary(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + const char *email = + "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ + "Subject: [PATCH] Modify some content\n" \ + "\n" \ + "---\n" \ + " file1.txt | 8 +++++---\n" \ + " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "index 94aaae8..af8f41d 100644\n" \ + "--- a/file1.txt\n" \ + "+++ b/file1.txt\n" \ + "@@ -1,15 +1,17 @@\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+\n" \ + "+\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = "Modify some content\nSome extra stuff here"; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_buf_clear(&buf); + cl_git_pass(git_diff_commit_as_email(&buf, repo, commit, 1, 1, 0, NULL)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__binary(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */ + const char *email = + "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys <jacquesg@striata.com>\n" \ + "Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \ + "Subject: [PATCH] Modified binary file\n" \ + "\n" \ + "---\n" \ + " binary.bin | Bin 3 -> 0 bytes\n" \ + " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \ + "\n" \ + "diff --git a/binary.bin b/binary.bin\n" \ + "index bd474b2..9ac35ff 100644\n" \ + "Binary files a/binary.bin and b/binary.bin differ\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = "Modified binary file"; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + cl_assert(strcmp(git_buf_cstr(&buf), email) == 0); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + diff --git a/tests/diff/iterator.c b/tests/diff/iterator.c index 891d8a6e5..cdc64eb1d 100644 --- a/tests/diff/iterator.c +++ b/tests/diff/iterator.c @@ -355,6 +355,7 @@ static void index_iterator_test( const char *sandbox, const char *start, const char *end, + git_iterator_flag_t flags, int expected_count, const char **expected_names, const char **expected_oids) @@ -362,11 +363,13 @@ static void index_iterator_test( git_index *index; git_iterator *i; const git_index_entry *entry; - int error, count = 0; + int error, count = 0, caps; git_repository *repo = cl_git_sandbox_init(sandbox); 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))) { cl_assert(entry); @@ -388,6 +391,8 @@ static void index_iterator_test( cl_assert_equal_i(expected_count, count); git_iterator_free(i); + + cl_assert(caps == git_index_caps(index)); git_index_free(index); } @@ -446,7 +451,8 @@ static const char *expected_index_oids_0[] = { void test_diff_iterator__index_0(void) { 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[] = { @@ -466,25 +472,26 @@ static const char *expected_index_oids_range[] = { void test_diff_iterator__index_range(void) { 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) { 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) { 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) { 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[] = { @@ -522,9 +529,45 @@ static const char* expected_index_oids_1[] = { void test_diff_iterator__index_1(void) { 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 -- */ diff --git a/tests/diff/stats.c b/tests/diff/stats.c new file mode 100644 index 000000000..131b7681d --- /dev/null +++ b/tests/diff/stats.c @@ -0,0 +1,428 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "commit.h" +#include "diff.h" + +static git_repository *repo; + +void test_diff_stats__initialize(void) +{ + repo = cl_git_sandbox_init("diff_format_email"); +} + +void test_diff_stats__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_diff_stats__stat(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file1.txt | 8 +++++---\n" \ + " 1 file changed, 5 insertions(+), 3 deletions(-)\n"; + + git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 1); + cl_assert(git_diff_stats_insertions(stats) == 5); + cl_assert(git_diff_stats_deletions(stats) == 3); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__multiple_hunks(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file2.txt | 5 +++--\n" \ + " file3.txt | 6 ++++--\n" \ + " 2 files changed, 7 insertions(+), 4 deletions(-)\n"; + + git_oid_fromstr(&oid, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 2); + cl_assert(git_diff_stats_insertions(stats) == 7); + cl_assert(git_diff_stats_deletions(stats) == 4); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__numstat(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + "3 2 file2.txt\n" + "4 2 file3.txt\n"; + + git_oid_fromstr(&oid, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_NUMBER)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__shortstat(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " 1 file changed, 5 insertions(+), 3 deletions(-)\n"; + + git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 1); + cl_assert(git_diff_stats_insertions(stats) == 5); + cl_assert(git_diff_stats_deletions(stats) == 3); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_SHORT)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__rename(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file2.txt => file2.txt.renamed | 1 +\n" + " file3.txt => file3.txt.renamed | 4 +++-\n" + " 2 files changed, 4 insertions(+), 1 deletions(-)\n"; + + git_oid_fromstr(&oid, "8947a46e2097638ca6040ad4877246f4186ec3bd"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_find_similar(diff, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 2); + cl_assert(git_diff_stats_insertions(stats) == 4); + cl_assert(git_diff_stats_deletions(stats) == 1); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__rename_nochanges(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file2.txt.renamed => file2.txt.renamed2 | 0\n" + " file3.txt.renamed => file3.txt.renamed2 | 0\n" + " 2 files changed, 0 insertions(+), 0 deletions(-)\n"; + + git_oid_fromstr(&oid, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_find_similar(diff, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 2); + cl_assert(git_diff_stats_insertions(stats) == 0); + cl_assert(git_diff_stats_deletions(stats) == 0); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__rename_and_modifiy(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file2.txt.renamed2 | 2 +-\n" + " file3.txt.renamed2 => file3.txt.renamed | 0\n" + " 2 files changed, 1 insertions(+), 1 deletions(-)\n"; + + git_oid_fromstr(&oid, "4ca10087e696d2ba78d07b146a118e9a7096ed4f"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_find_similar(diff, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 2); + cl_assert(git_diff_stats_insertions(stats) == 1); + cl_assert(git_diff_stats_deletions(stats) == 1); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__rename_no_find(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file2.txt | 5 -----\n" + " file2.txt.renamed | 6 ++++++\n" + " file3.txt | 5 -----\n" + " file3.txt.renamed | 7 +++++++\n" + " 4 files changed, 13 insertions(+), 10 deletions(-)\n"; + + git_oid_fromstr(&oid, "8947a46e2097638ca6040ad4877246f4186ec3bd"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 4); + cl_assert(git_diff_stats_insertions(stats) == 13); + cl_assert(git_diff_stats_deletions(stats) == 10); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__rename_nochanges_no_find(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file2.txt.renamed | 6 ------\n" + " file2.txt.renamed2 | 6 ++++++\n" + " file3.txt.renamed | 7 -------\n" + " file3.txt.renamed2 | 7 +++++++\n" + " 4 files changed, 13 insertions(+), 13 deletions(-)\n"; + + git_oid_fromstr(&oid, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 4); + cl_assert(git_diff_stats_insertions(stats) == 13); + cl_assert(git_diff_stats_deletions(stats) == 13); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__rename_and_modifiy_no_find(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file2.txt.renamed2 | 2 +-\n" + " file3.txt.renamed | 7 +++++++\n" + " file3.txt.renamed2 | 7 -------\n" + " 3 files changed, 8 insertions(+), 8 deletions(-)\n"; + + git_oid_fromstr(&oid, "4ca10087e696d2ba78d07b146a118e9a7096ed4f"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 3); + cl_assert(git_diff_stats_insertions(stats) == 8); + cl_assert(git_diff_stats_deletions(stats) == 8); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__binary(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */ + const char *stat = + " binary.bin | Bin 3 -> 0 bytes\n" + " 1 file changed, 0 insertions(+), 0 deletions(-)\n"; + + git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + cl_assert(git_diff_stats_files_changed(stats) == 1); + cl_assert(git_diff_stats_insertions(stats) == 0); + cl_assert(git_diff_stats_deletions(stats) == 0); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__binary_numstat(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + "- - binary.bin\n"; + + git_oid_fromstr(&oid, "8d7523f6fcb2404257889abe0d96f093d9f524f9"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_NUMBER)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_stats__mode_change(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_stats *stats = NULL; + git_buf buf = GIT_BUF_INIT; + + const char *stat = + " file1.txt.renamed | 0\n" \ + " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \ + " mode change 100644 => 100755 file1.txt.renamed\n" \ + "\n"; + + git_oid_fromstr(&oid, "7ade76dd34bba4733cf9878079f9fd4a456a9189"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + + cl_git_pass(git_diff_get_stats(&stats, diff)); + + cl_git_pass(git_diff_stats_to_buf(&buf, stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + + git_diff_stats_free(stats); + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} diff --git a/tests/graph/descendant_of.c b/tests/graph/descendant_of.c index ffdd0cfc8..8e9952a09 100644 --- a/tests/graph/descendant_of.c +++ b/tests/graph/descendant_of.c @@ -45,3 +45,11 @@ void test_graph_descendant_of__returns_correct_result(void) git_commit_free(other); } + +void test_graph_descendant_of__nopath(void) +{ + git_oid oid; + + git_oid_fromstr(&oid, "e90810b8df3e80c413d903f631643c716887138d"); + cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(commit), &oid)); +} diff --git a/tests/index/tests.c b/tests/index/tests.c index 6e28af1f7..fa5c0bb1a 100644 --- a/tests/index/tests.c +++ b/tests/index/tests.c @@ -544,36 +544,22 @@ void test_index_tests__corrupted_extension(void) 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) { git_index *index; unsigned int caps; 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); cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEXCAP_IGNORE_CASE)); 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_read(index, true)); - assert_index_is_sorted(index); + cl_git_pass(git_vector_verify_sorted(&index->entries)); git_index_free(index); } diff --git a/tests/merge/workdir/simple.c b/tests/merge/workdir/simple.c index 032e97f8d..cf5b16e7a 100644 --- a/tests/merge/workdir/simple.c +++ b/tests/merge/workdir/simple.c @@ -214,7 +214,7 @@ void test_merge_workdir_simple__automerge_crlf(void) void test_merge_workdir_simple__mergefile(void) { - git_buf conflicting_buf = GIT_BUF_INIT; + git_buf conflicting_buf = GIT_BUF_INIT, mergemsg_buf = GIT_BUF_INIT; struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, @@ -240,7 +240,15 @@ void test_merge_workdir_simple__mergefile(void) cl_git_pass(git_futils_readbuffer(&conflicting_buf, TEST_REPO_PATH "/conflicting.txt")); cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_MERGE_FILE) == 0); + cl_git_pass(git_futils_readbuffer(&mergemsg_buf, + TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(strcmp(git_buf_cstr(&mergemsg_buf), + "Merge commit '7cb63eed597130ba4abb87b3e544b85021905520'\n" \ + "\n" \ + "Conflicts:\n" \ + "\tconflicting.txt\n") == 0); git_buf_free(&conflicting_buf); + git_buf_free(&mergemsg_buf); cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); diff --git a/tests/refs/branches/iterator.c b/tests/refs/branches/iterator.c index 29ca59aca..76b35a7d6 100644 --- a/tests/refs/branches/iterator.c +++ b/tests/refs/branches/iterator.c @@ -48,7 +48,7 @@ static void assert_retrieval(unsigned int flags, unsigned int expected_count) void test_refs_branches_iterator__retrieve_all_branches(void) { - assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 14); + assert_retrieval(GIT_BRANCH_ALL, 14); } void test_refs_branches_iterator__retrieve_remote_branches(void) @@ -139,7 +139,7 @@ void test_refs_branches_iterator__mix_of_packed_and_loose(void) r2 = cl_git_sandbox_init("testrepo2"); - cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE)); + cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_ALL)); contains_branches(exp, iter); git_branch_iterator_free(iter); diff --git a/tests/repo/open.c b/tests/repo/open.c index f7420bd3a..190adff1c 100644 --- a/tests/repo/open.c +++ b/tests/repo/open.c @@ -102,14 +102,13 @@ void test_repo_open__gitlinked(void) void test_repo_open__from_git_new_workdir(void) { +#ifndef GIT_WIN32 /* The git-new-workdir script that ships with git sets up a bunch of * symlinks to create a second workdir that shares the object db with * another checkout. Libgit2 can open a repo that has been configured * this way. */ - cl_git_sandbox_init("empty_standard_repo"); -#ifndef GIT_WIN32 git_repository *repo2; git_buf link_tgt = GIT_BUF_INIT, link = GIT_BUF_INIT, body = GIT_BUF_INIT; const char **scan; @@ -122,6 +121,8 @@ void test_repo_open__from_git_new_workdir(void) "HEAD", NULL }; + cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(p_mkdir("alternate", 0777)); cl_git_pass(p_mkdir("alternate/.git", 0777)); diff --git a/tests/repo/state.c b/tests/repo/state.c index 5e7227205..2d6c780ee 100644 --- a/tests/repo/state.c +++ b/tests/repo/state.c @@ -45,52 +45,70 @@ void test_repo_state__merge(void) { setup_simple_state(GIT_MERGE_HEAD_FILE); assert_repo_state(GIT_REPOSITORY_STATE_MERGE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__revert(void) { setup_simple_state(GIT_REVERT_HEAD_FILE); assert_repo_state(GIT_REPOSITORY_STATE_REVERT); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__cherry_pick(void) { setup_simple_state(GIT_CHERRY_PICK_HEAD_FILE); assert_repo_state(GIT_REPOSITORY_STATE_CHERRY_PICK); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__bisect(void) { setup_simple_state(GIT_BISECT_LOG_FILE); assert_repo_state(GIT_REPOSITORY_STATE_BISECT); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__rebase_interactive(void) { setup_simple_state(GIT_REBASE_MERGE_INTERACTIVE_FILE); assert_repo_state(GIT_REPOSITORY_STATE_REBASE_INTERACTIVE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__rebase_merge(void) { setup_simple_state(GIT_REBASE_MERGE_DIR "whatever"); assert_repo_state(GIT_REPOSITORY_STATE_REBASE_MERGE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__rebase(void) { setup_simple_state(GIT_REBASE_APPLY_REBASING_FILE); assert_repo_state(GIT_REPOSITORY_STATE_REBASE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__apply_mailbox(void) { setup_simple_state(GIT_REBASE_APPLY_APPLYING_FILE); assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__apply_mailbox_or_rebase(void) { setup_simple_state(GIT_REBASE_APPLY_DIR "whatever"); assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } diff --git a/tests/resources/cherrypick/.gitted/HEAD b/tests/resources/cherrypick/.gitted/HEAD new file mode 100644 index 000000000..656ac0e0a --- /dev/null +++ b/tests/resources/cherrypick/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/automerge-branch diff --git a/tests/resources/cherrypick/.gitted/config b/tests/resources/cherrypick/.gitted/config new file mode 100644 index 000000000..6c9406b7d --- /dev/null +++ b/tests/resources/cherrypick/.gitted/config @@ -0,0 +1,7 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true diff --git a/tests/resources/cherrypick/.gitted/index b/tests/resources/cherrypick/.gitted/index Binary files differnew file mode 100644 index 000000000..7291006c8 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/index diff --git a/tests/resources/cherrypick/.gitted/info/exclude b/tests/resources/cherrypick/.gitted/info/exclude new file mode 100644 index 000000000..a5196d1be --- /dev/null +++ b/tests/resources/cherrypick/.gitted/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253 b/tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253 Binary files differnew file mode 100644 index 000000000..736a7f57b --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253 diff --git a/tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8 b/tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8 Binary files differnew file mode 100644 index 000000000..4eacb26f5 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8 diff --git a/tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37 b/tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37 Binary files differnew file mode 100644 index 000000000..48fa6efcd --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37 diff --git a/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9 b/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9 new file mode 100644 index 000000000..06d1c694e --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9 @@ -0,0 +1,3 @@ +xA +0@Q9i"`LBMӅܾ)籂#բ +^CNb+%bRU!z1Jh)JO}딼b>WI\qyϟ
祖QҔO`D6{tfm_sy@2("O-
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65 b/tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65 Binary files differnew file mode 100644 index 000000000..9a3ea3209 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65 diff --git a/tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88 b/tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88 Binary files differnew file mode 100644 index 000000000..62abc3c5b --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88 diff --git a/tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632a b/tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632a Binary files differnew file mode 100644 index 000000000..162844a70 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632a diff --git a/tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2 b/tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2 Binary files differnew file mode 100644 index 000000000..d5cd6d3f2 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2 diff --git a/tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452af b/tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452af Binary files differnew file mode 100644 index 000000000..f9a841d4f --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452af diff --git a/tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0 b/tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0 Binary files differnew file mode 100644 index 000000000..98b792b64 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0 diff --git a/tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286c b/tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286c Binary files differnew file mode 100644 index 000000000..10a5be6fe --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286c diff --git a/tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661d b/tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661d Binary files differnew file mode 100644 index 000000000..c8b26cd01 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661d diff --git a/tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2 b/tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2 Binary files differnew file mode 100644 index 000000000..80363b016 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2 diff --git a/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc b/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc new file mode 100644 index 000000000..283113999 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc @@ -0,0 +1 @@ +xMj0)f_j]`$*N1C=@{^YZlLOҙUzub/X1"iuWN9b҄ZS&r4mrY:Qo+6{/{?gҎ`\k-<k>U_u+5Οx9a?7W
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1 b/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1 new file mode 100644 index 000000000..a3294d764 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1 @@ -0,0 +1 @@ +xJ0E]+fIwGLkܞ{VG{כ*A9Ěc:PܔCJBk\2]}jPQD6b95xuO7v}{[c3ޢԮ#E̻yɚgToPM/X
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2c b/tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2c Binary files differnew file mode 100644 index 000000000..74b48dd6b --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2c diff --git a/tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87d b/tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87d Binary files differnew file mode 100644 index 000000000..c0466f46a --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87d diff --git a/tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45 b/tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45 Binary files differnew file mode 100644 index 000000000..d4f1cf8ac --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45 diff --git a/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92 b/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92 new file mode 100644 index 000000000..8c4d6d94f --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92 @@ -0,0 +1,5 @@ +xMj0)fjH]dԸDq# +-t=YJ`HOt.DSJN.1I#gUo
$eR8ɇgj/F] +,MW8j-zپVxyk37d){pc +hn +bNzgUǡ}6xz*V8
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587 b/tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587 Binary files differnew file mode 100644 index 000000000..60d5dca4a --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587 diff --git a/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70 b/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70 new file mode 100644 index 000000000..8697c4e66 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70 @@ -0,0 +1 @@ +xN0C9+推$M$đX&i!M%{ gɲZpכ.0c$etBPpLꃛ,RITtA4E.TFrIlOkNl,۴oxcoO[o3wZO`lD.V=vWzd(ءux8O.K%M?Z
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690 b/tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690 Binary files differnew file mode 100644 index 000000000..a1fa599e1 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690 diff --git a/tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9 b/tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9 Binary files differnew file mode 100644 index 000000000..bf96fccad --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9 diff --git a/tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 Binary files differnew file mode 100644 index 000000000..adf64119a --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 diff --git a/tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cdde b/tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cdde Binary files differnew file mode 100644 index 000000000..2e56d7403 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cdde diff --git a/tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22 b/tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22 Binary files differnew file mode 100644 index 000000000..3e01376bd --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22 diff --git a/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209 b/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209 new file mode 100644 index 000000000..7d2b233a6 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209 @@ -0,0 +1,4 @@ +xMj0)fjH]dԸDq# +-t=YJ`HOt.DSJN.1I#gUo
$eR8ɇgj/F] +,MW8j-zپVxyk37d){pc +hn?Js=C:O#6V<
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdc b/tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdc Binary files differnew file mode 100644 index 000000000..2a5bcec27 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdc diff --git a/tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6f b/tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6f Binary files differnew file mode 100644 index 000000000..8847ed689 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6f diff --git a/tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617 b/tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617 Binary files differnew file mode 100644 index 000000000..f161a1941 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617 diff --git a/tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774 b/tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774 Binary files differnew file mode 100644 index 000000000..77deeaf0b --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774 diff --git a/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea b/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea new file mode 100644 index 000000000..aa30f501f --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea @@ -0,0 +1 @@ +x+)JMU044d040031QHI5+(aUE9s\uIXvKY;7nM3KdF"cx?35זzѨ1*
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4a b/tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4a Binary files differnew file mode 100644 index 000000000..5e622a1fa --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4a diff --git a/tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8 b/tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8 Binary files differnew file mode 100644 index 000000000..eafe2c30a --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8 diff --git a/tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7 b/tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7 Binary files differnew file mode 100644 index 000000000..1c1f5034d --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7 diff --git a/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96 b/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96 new file mode 100644 index 000000000..a98378a70 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96 @@ -0,0 +1 @@ +x=j@@{
fvLpgp3̎FժmҾɺ,s7U$ 1:HEc.d*1Qp5nzTh}A"I.SA:Hys}Z\YvminpymGYo`$DծBO{L|f^OA
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819 b/tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819 Binary files differnew file mode 100644 index 000000000..732011fce --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819 diff --git a/tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41a b/tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41a Binary files differnew file mode 100644 index 000000000..302014bff --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41a diff --git a/tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211 b/tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211 Binary files differnew file mode 100644 index 000000000..db6faa9e2 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211 diff --git a/tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452 b/tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452 Binary files differnew file mode 100644 index 000000000..7fe69b6f8 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452 diff --git a/tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897ce b/tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897ce Binary files differnew file mode 100644 index 000000000..8b1638fbb --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897ce diff --git a/tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4 b/tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4 Binary files differnew file mode 100644 index 000000000..2dec33f69 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4 diff --git a/tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36 b/tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36 Binary files differnew file mode 100644 index 000000000..00314454f --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36 diff --git a/tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717f b/tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717f Binary files differnew file mode 100644 index 000000000..1266aff36 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717f diff --git a/tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33 b/tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33 Binary files differnew file mode 100644 index 000000000..7aa0a5dcd --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33 diff --git a/tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394 b/tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394 Binary files differnew file mode 100644 index 000000000..07b7195d2 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394 diff --git a/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d b/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d new file mode 100644 index 000000000..4713fb2db --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d @@ -0,0 +1 @@ +xMj0uHBb4.J2G㽔qW9l=#`5GsDD5(ꋪlX!p!e$2N2{9IїWemו:y/om7pB]Qmw`^þA6 mT
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1e b/tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1e Binary files differnew file mode 100644 index 000000000..1c3f2fb01 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1e diff --git a/tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266b b/tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266b Binary files differnew file mode 100644 index 000000000..d94a9541f --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266b diff --git a/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610 b/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610 new file mode 100644 index 000000000..69feba205 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610 @@ -0,0 +1 @@ +x10@ѭuA,!] `,/hrǰGHKs՛*he8J*(&rTlJI$JD%YF}ipt:kot9楞`p)95]?}nsSnjGPOL
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcab b/tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcab Binary files differnew file mode 100644 index 000000000..5a6db508e --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcab diff --git a/tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76 b/tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76 Binary files differnew file mode 100644 index 000000000..61741aff9 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76 diff --git a/tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8 b/tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8 Binary files differnew file mode 100644 index 000000000..08c4bef57 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8 diff --git a/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff b/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff new file mode 100644 index 000000000..4e4fe6f12 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff @@ -0,0 +1,4 @@ +xKN0DY?B쐐8DӞHNqܞF'ƇT!`*<+,YZ +%LވلM쓖X$N$S.cq89 +Dޚo{xFL)Ɣ}em]
^wv~z,&?iGz\//rS`^ +=ݣXfZ
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0 b/tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0 Binary files differnew file mode 100644 index 000000000..e3bf3a017 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0 diff --git a/tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8 b/tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8 Binary files differnew file mode 100644 index 000000000..956da8b71 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8 diff --git a/tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226b b/tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226b Binary files differnew file mode 100644 index 000000000..b5583685a --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226b diff --git a/tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231e b/tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231e Binary files differnew file mode 100644 index 000000000..01d88a283 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231e diff --git a/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8 b/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8 new file mode 100644 index 000000000..6a0eccb5e --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8 @@ -0,0 +1,2 @@ +xA +B!֞bA6BD[o^@$ݾj-ڛtf]#":dKi51S@Rq:<b~Op^gmH/Q)z}jCpDvEK<
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/bd/6ffc8c6c41f0f85ff9e3d61c9479516bac0024 b/tests/resources/cherrypick/.gitted/objects/bd/6ffc8c6c41f0f85ff9e3d61c9479516bac0024 Binary files differnew file mode 100644 index 000000000..56f836779 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/bd/6ffc8c6c41f0f85ff9e3d61c9479516bac0024 diff --git a/tests/resources/cherrypick/.gitted/objects/bd/a51965cb36c0c5731c8cb50b80a36cac81018e b/tests/resources/cherrypick/.gitted/objects/bd/a51965cb36c0c5731c8cb50b80a36cac81018e Binary files differnew file mode 100644 index 000000000..1187a7089 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/bd/a51965cb36c0c5731c8cb50b80a36cac81018e diff --git a/tests/resources/cherrypick/.gitted/objects/ce/d8fb81b6ec534d5deaf2a48b4b96c799712507 b/tests/resources/cherrypick/.gitted/objects/ce/d8fb81b6ec534d5deaf2a48b4b96c799712507 new file mode 100644 index 000000000..569ee0c99 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/ce/d8fb81b6ec534d5deaf2a48b4b96c799712507 @@ -0,0 +1 @@ +xJ0@ay?] d2i+7)[~á6afYH1K%+E!(>q>qehM}P!:Cxεu!xXϟC>}ṟ,I炓ĥE9{0;KZq_˺Yt3V
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15 b/tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15 Binary files differnew file mode 100644 index 000000000..d7deb0bff --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15 diff --git a/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e b/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e new file mode 100644 index 000000000..65c846fa4 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e @@ -0,0 +1 @@ +xAj0E)f_,]z<]"!<^l4f=iM0Hir: <kiFGhI}S] 4F qfʚl蔵6svׯ[i ]R-7 uà:OI)p[!/=;&WY
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e b/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e new file mode 100644 index 000000000..b42df7e50 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e @@ -0,0 +1 @@ +xKJ1a9E!I@df%x!AooGp-~[[lO2AW
6Rّ=yFW@Փ5l(drXG[.cEKѻgmV!Pso0v*Wd
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2 b/tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2 Binary files differnew file mode 100644 index 000000000..b344c9cc8 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2 diff --git a/tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31 b/tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31 Binary files differnew file mode 100644 index 000000000..fdc05714f --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31 diff --git a/tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129 b/tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129 Binary files differnew file mode 100644 index 000000000..3345907db --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129 diff --git a/tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91 b/tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91 Binary files differnew file mode 100644 index 000000000..238873025 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91 diff --git a/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110 b/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110 new file mode 100644 index 000000000..ab0a27f37 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110 @@ -0,0 +1,2 @@ +xMj0@u<!Eɣ?$/rzn}]HG+z"*k +AHch]n
RN-3Kp~}PK,J=dz#G?Vֲu:NvVficO;iZjEu y^-P@
\ No newline at end of file diff --git a/tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959 b/tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959 Binary files differnew file mode 100644 index 000000000..19d0c5288 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959 diff --git a/tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3ae b/tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3ae Binary files differnew file mode 100644 index 000000000..ab454399e --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3ae diff --git a/tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775 b/tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775 Binary files differnew file mode 100644 index 000000000..558dd0a44 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775 diff --git a/tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4 b/tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4 Binary files differnew file mode 100644 index 000000000..a0117515c --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4 diff --git a/tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978 b/tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978 Binary files differnew file mode 100644 index 000000000..83bb5a0cc --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978 diff --git a/tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8 b/tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8 Binary files differnew file mode 100644 index 000000000..71be9f834 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8 diff --git a/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch b/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch new file mode 100644 index 000000000..9330ef3d1 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch @@ -0,0 +1 @@ +d3d77487660ee3c0194ee01dc5eaf478782b1c7e diff --git a/tests/resources/cherrypick/.gitted/refs/heads/master b/tests/resources/cherrypick/.gitted/refs/heads/master new file mode 100644 index 000000000..aa8913beb --- /dev/null +++ b/tests/resources/cherrypick/.gitted/refs/heads/master @@ -0,0 +1 @@ +2a26c7e88b285613b302ba76712bc998863f3cbc diff --git a/tests/resources/cherrypick/.gitted/refs/heads/merge-branch b/tests/resources/cherrypick/.gitted/refs/heads/merge-branch new file mode 100644 index 000000000..ea5b277ec --- /dev/null +++ b/tests/resources/cherrypick/.gitted/refs/heads/merge-branch @@ -0,0 +1 @@ +abe4603bc7cd5b8167a267e0e2418fd2348f8cff diff --git a/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts b/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts new file mode 100644 index 000000000..f63f17efb --- /dev/null +++ b/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts @@ -0,0 +1 @@ +bafbf6912c09505ac60575cd43d3f2aba3bd84d8 diff --git a/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline b/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline new file mode 100644 index 000000000..0ec5e458c --- /dev/null +++ b/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline @@ -0,0 +1 @@ +cfc4f0999a8367568e049af4f72e452d40828a15 diff --git a/tests/resources/cherrypick/.gitted/refs/heads/orphan b/tests/resources/cherrypick/.gitted/refs/heads/orphan new file mode 100644 index 000000000..f4d6a7467 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/refs/heads/orphan @@ -0,0 +1 @@ +74f06b5bfec6d33d7264f73606b57a7c0b963819 diff --git a/tests/resources/cherrypick/.gitted/refs/heads/renames b/tests/resources/cherrypick/.gitted/refs/heads/renames new file mode 100644 index 000000000..df5587173 --- /dev/null +++ b/tests/resources/cherrypick/.gitted/refs/heads/renames @@ -0,0 +1 @@ +44cd2ed2052c9c68f9a439d208e9614dc2a55c70 diff --git a/tests/resources/cherrypick/file1.txt b/tests/resources/cherrypick/file1.txt new file mode 100644 index 000000000..38c05a857 --- /dev/null +++ b/tests/resources/cherrypick/file1.txt @@ -0,0 +1,15 @@ +!File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 diff --git a/tests/resources/cherrypick/file2.txt b/tests/resources/cherrypick/file2.txt new file mode 100644 index 000000000..a661b5dec --- /dev/null +++ b/tests/resources/cherrypick/file2.txt @@ -0,0 +1,15 @@ +!File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 diff --git a/tests/resources/cherrypick/file3.txt b/tests/resources/cherrypick/file3.txt new file mode 100644 index 000000000..85a4a1d79 --- /dev/null +++ b/tests/resources/cherrypick/file3.txt @@ -0,0 +1,15 @@ +!File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 diff --git a/tests/resources/diff_format_email/.gitted/HEAD b/tests/resources/diff_format_email/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests/resources/diff_format_email/.gitted/config b/tests/resources/diff_format_email/.gitted/config new file mode 100644 index 000000000..6c9406b7d --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/config @@ -0,0 +1,7 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true diff --git a/tests/resources/diff_format_email/.gitted/index b/tests/resources/diff_format_email/.gitted/index Binary files differnew file mode 100644 index 000000000..f73027e56 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/index diff --git a/tests/resources/diff_format_email/.gitted/info/exclude b/tests/resources/diff_format_email/.gitted/info/exclude new file mode 100644 index 000000000..a5196d1be --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce b/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce Binary files differnew file mode 100644 index 000000000..1ece99cde --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce diff --git a/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508 b/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508 Binary files differnew file mode 100644 index 000000000..90313feec --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508 diff --git a/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8 b/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8 Binary files differnew file mode 100644 index 000000000..360dc3bc7 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8 diff --git a/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab b/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab Binary files differnew file mode 100644 index 000000000..603aa496a --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab diff --git a/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e b/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e Binary files differnew file mode 100644 index 000000000..b6f04538e --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e diff --git a/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5 b/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5 Binary files differnew file mode 100644 index 000000000..be85c78ba --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5 diff --git a/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20 b/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20 Binary files differnew file mode 100644 index 000000000..e8145edfd --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20 diff --git a/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6 b/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6 Binary files differnew file mode 100644 index 000000000..3ae87cfa9 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6 diff --git a/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234 b/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234 Binary files differnew file mode 100644 index 000000000..f80b3612c --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234 diff --git a/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a b/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a Binary files differnew file mode 100644 index 000000000..190c3f272 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a diff --git a/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264 b/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264 Binary files differnew file mode 100644 index 000000000..42f49ae8a --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264 diff --git a/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3 b/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3 Binary files differnew file mode 100644 index 000000000..e1ede9ae9 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3 diff --git a/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d b/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d Binary files differnew file mode 100644 index 000000000..1b8d68951 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d diff --git a/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e b/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e Binary files differnew file mode 100644 index 000000000..10c34009d --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e diff --git a/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6 b/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6 Binary files differnew file mode 100644 index 000000000..689f5b9a1 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6 diff --git a/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df b/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df Binary files differnew file mode 100644 index 000000000..6af5dda9d --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df diff --git a/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d b/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d Binary files differnew file mode 100644 index 000000000..d2b911d0e --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d diff --git a/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4 b/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4 Binary files differnew file mode 100644 index 000000000..69d213dcb --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4 diff --git a/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf b/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf new file mode 100644 index 000000000..e5014565b --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf @@ -0,0 +1,2 @@ +xJ0ay_IIqwo0Lo+$&[zU&FY,:YCT8B UsH,BL)(r.ca
}+^Y>mp՚_
sG)&k}GhzRļi* +Rrs X
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa b/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa Binary files differnew file mode 100644 index 000000000..b855408e8 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa diff --git a/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c b/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c Binary files differnew file mode 100644 index 000000000..65740b42c --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c diff --git a/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f b/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f Binary files differnew file mode 100644 index 000000000..b05e7d634 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f diff --git a/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e b/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e Binary files differnew file mode 100644 index 000000000..57a8dfed1 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e diff --git a/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87 b/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87 Binary files differnew file mode 100644 index 000000000..0f31b97d6 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87 diff --git a/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509 b/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509 Binary files differnew file mode 100644 index 000000000..5b96aa5ea --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509 diff --git a/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261 b/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261 Binary files differnew file mode 100644 index 000000000..8db9090bc --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261 diff --git a/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d b/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d Binary files differnew file mode 100644 index 000000000..4bbc0ea43 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d diff --git a/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa b/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa Binary files differnew file mode 100644 index 000000000..680e48762 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa diff --git a/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5 b/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5 Binary files differnew file mode 100644 index 000000000..86a38289b --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5 diff --git a/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448 b/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448 Binary files differnew file mode 100644 index 000000000..81b606f4e --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448 diff --git a/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 b/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 new file mode 100644 index 000000000..aa9d7b0cd --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 @@ -0,0 +1,2 @@ +x; +1a X)$sD3{.+ssj'fg=Cm⤄A <p&m5\H:0
ThaCJ[;̱<^)mY\xupŒS]\s01dTrJq_rIム/PK
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c b/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c Binary files differnew file mode 100644 index 000000000..49e9d65f4 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c diff --git a/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f b/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f Binary files differnew file mode 100644 index 000000000..e32471ba9 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f diff --git a/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d b/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d new file mode 100644 index 000000000..26ee22429 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d @@ -0,0 +1,2 @@ +xAN0EYޏ8` fĒtDܞ"O,!uU1գ*C2G,2pt+{7)JcE,JT 1IC88tx +Oۼ^Zd(K;'Θ N*q/zM`0~W@
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93 b/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93 new file mode 100644 index 000000000..ee55d64d7 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93 @@ -0,0 +1 @@ +x+)JMU01e040031QHI5+(+JKMMaXYB،|-a'{"̊w
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86 b/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86 Binary files differnew file mode 100644 index 000000000..1bae4a6d6 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86 diff --git a/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231 b/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231 new file mode 100644 index 000000000..db037caf9 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231 @@ -0,0 +1,2 @@ +xAj1E)/ +$<N<EoZatU@)3C" "J-z
9'2OH%J(5I2:|﹐L\tqon$MW^p~:L>LqGh~u$+g+H[^`ȏQW
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 b/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 new file mode 100644 index 000000000..cf9bdaa5f --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 @@ -0,0 +1,3 @@ +xA +0E]d4M"`m5IޢGp>X4aWn3y,[FHoD-e +:ёcz0FbCÊ:_HNcwRHqN'6xF{4j[j$0U_y*&R
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7 b/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7 Binary files differnew file mode 100644 index 000000000..d8c9934f7 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7 diff --git a/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6 b/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6 Binary files differnew file mode 100644 index 000000000..df4cbb322 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6 diff --git a/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373 b/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373 Binary files differnew file mode 100644 index 000000000..890abcd4a --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373 diff --git a/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd b/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd new file mode 100644 index 000000000..d4018bf8e --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd @@ -0,0 +1,2 @@ +xAj0E)/ь,Jw.{8 ;Eo_C{nWZnYҩ7:8\u!Ni@0qaWR4Foڰt$ +S8"C'-ΫD1쇖}Gv߿7{y۬]eb"sc|bъL
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c b/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c new file mode 100644 index 000000000..1dce143b7 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c @@ -0,0 +1 @@ +xAN0@Q9Hȵ$Y!q't1g$-~ֵ
FWb[hqXY)9Q[ '͘9d&eus__yGo2lLD3HաsZ!;X"Pq
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 b/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 new file mode 100644 index 000000000..903ec751c --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 @@ -0,0 +1 @@ +xAJ0a9IӤw!f&_Utno#nF7JZLT$%BQVfe,,9+ElL$a$KAu|ެm>zO/@SNOfG!c+6+ ]ER
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76 b/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76 Binary files differnew file mode 100644 index 000000000..b5e08f901 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76 diff --git a/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92 b/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92 Binary files differnew file mode 100644 index 000000000..75b047a64 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92 diff --git a/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a b/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a Binary files differnew file mode 100644 index 000000000..a5286bc68 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a diff --git a/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03 b/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03 Binary files differnew file mode 100644 index 000000000..9e26e34cd --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03 diff --git a/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911 b/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911 Binary files differnew file mode 100644 index 000000000..5fc167e79 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911 diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44 b/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44 Binary files differnew file mode 100644 index 000000000..be48c275e --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44 diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63 b/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63 Binary files differnew file mode 100644 index 000000000..f322a7ce4 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63 diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4 b/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4 Binary files differnew file mode 100644 index 000000000..75cd71426 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4 diff --git a/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b b/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b Binary files differnew file mode 100644 index 000000000..d84ab8dcc --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b diff --git a/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b b/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b new file mode 100644 index 000000000..a693df5c8 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b @@ -0,0 +1 @@ +xMJ1@a9ETDf7 xJRD5^nm]ihbSK5p)RD.V|ۄԢEBR"rPKi6|6!;e}^yK7KC H桏)uҠ;hQK
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238 b/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238 Binary files differnew file mode 100644 index 000000000..f0483943b --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238 diff --git a/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f b/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f Binary files differnew file mode 100644 index 000000000..5c1faf009 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f diff --git a/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961 b/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961 Binary files differnew file mode 100644 index 000000000..3baf494be --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961 diff --git a/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13 b/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13 Binary files differnew file mode 100644 index 000000000..f0dcaa9ac --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13 diff --git a/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235 b/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235 Binary files differnew file mode 100644 index 000000000..28e148f41 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235 diff --git a/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9 b/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9 Binary files differnew file mode 100644 index 000000000..0c74e7696 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9 diff --git a/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1 b/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1 Binary files differnew file mode 100644 index 000000000..2dc82087b --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1 diff --git a/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff b/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff Binary files differnew file mode 100644 index 000000000..af0232aa1 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff diff --git a/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df b/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df new file mode 100644 index 000000000..e03c74935 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df @@ -0,0 +1,3 @@ +xK +0FaYE悤Cq&8t7[![p NĹIM~* }ʬ'lilxqԤw6kK +cX̱ ^0Wy^+ja,㲴:rCYvttZUOrRbmᯈc$M
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2 b/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2 Binary files differnew file mode 100644 index 000000000..155b45273 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2 diff --git a/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8 b/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8 Binary files differnew file mode 100644 index 000000000..fd9363612 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8 diff --git a/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a b/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a Binary files differnew file mode 100644 index 000000000..5863cec1b --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a diff --git a/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e b/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e new file mode 100644 index 000000000..a5d4d78e9 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e @@ -0,0 +1 @@ +x+)JMU01e040075UHI5+(+JKMMaXYB،|-a'{"z
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a b/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a Binary files differnew file mode 100644 index 000000000..85eb8141c --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a diff --git a/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc b/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc Binary files differnew file mode 100644 index 000000000..8e250ece2 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc diff --git a/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0 b/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0 Binary files differnew file mode 100644 index 000000000..92a89a2cf --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0 diff --git a/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 Binary files differnew file mode 100644 index 000000000..711223894 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1 b/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1 Binary files differnew file mode 100644 index 000000000..43a006231 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1 diff --git a/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 b/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 new file mode 100644 index 000000000..ee1edad9a --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 @@ -0,0 +1,2 @@ +xM +0@a9Ed3+o)-x4Imp*9Su$\0Nuؐ1^%u/X$e,BPLK# 9FpPĵ
S^+/u,˰~y|~^Z-CƓHh^iĦj"R,HrK
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 b/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 new file mode 100644 index 000000000..62d747cf6 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 @@ -0,0 +1 @@ +xA @Qלb&fhi)1bA1b#L&=ۿx?̥dN*3x`C]p-&H]ScpYk|[LB0Yq4Qu];KlHB
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 b/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 new file mode 100644 index 000000000..d858a87b5 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 @@ -0,0 +1 @@ +xMN0@a98HCBNvL~ܞ8omkЫ*pń31M6L1'4'rJ\!bk=Ktszgd!6mmir[u.^<Z_9"YPR
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc b/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc new file mode 100644 index 000000000..b9919c225 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc @@ -0,0 +1,2 @@ +xM +0@a9ELL"G[<oxq,ehݴ9 HH`\{jMwфHYX Hǒ9 JvH|-y\0ߗϬݎs4ű4-Zu]m:
eR_K
\ No newline at end of file diff --git a/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514 b/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514 Binary files differnew file mode 100644 index 000000000..776221d21 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514 diff --git a/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb b/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb Binary files differnew file mode 100644 index 000000000..92964252a --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/binary b/tests/resources/diff_format_email/.gitted/refs/heads/binary new file mode 100644 index 000000000..7e563c957 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/refs/heads/binary @@ -0,0 +1 @@ +a3ac918e3a6604294b239cb956363e83d71abb3b diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/master b/tests/resources/diff_format_email/.gitted/refs/heads/master new file mode 100644 index 000000000..f0f3f932a --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/refs/heads/master @@ -0,0 +1 @@ +873806f6f27e631eb0b23e4b56bea2bfac14a373 diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/multihunk b/tests/resources/diff_format_email/.gitted/refs/heads/multihunk new file mode 100644 index 000000000..41bd37f39 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/refs/heads/multihunk @@ -0,0 +1 @@ +cd471f0d8770371e1bc78bcbb38db4c7e4106bd2 diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/rename b/tests/resources/diff_format_email/.gitted/refs/heads/rename new file mode 100644 index 000000000..3025fbc64 --- /dev/null +++ b/tests/resources/diff_format_email/.gitted/refs/heads/rename @@ -0,0 +1 @@ +4ca10087e696d2ba78d07b146a118e9a7096ed4f diff --git a/tests/resources/diff_format_email/file1.txt.renamed b/tests/resources/diff_format_email/file1.txt.renamed new file mode 100755 index 000000000..a97157a0d --- /dev/null +++ b/tests/resources/diff_format_email/file1.txt.renamed @@ -0,0 +1,17 @@ +file1.txt +file1.txt +_file1.txt_ +file1.txt +file1.txt +file1.txt_renamed +file1.txt + + +file1.txt +file1.txt +file1.txt_renamed +file1.txt +file1.txt +_file1.txt_ +_file1.txt_ +file1.txt diff --git a/tests/resources/diff_format_email/file2.txt b/tests/resources/diff_format_email/file2.txt new file mode 100644 index 000000000..7aff11da9 --- /dev/null +++ b/tests/resources/diff_format_email/file2.txt @@ -0,0 +1,5 @@ +file2 +file2 +file2 +file2! +file2 diff --git a/tests/resources/diff_format_email/file3.txt b/tests/resources/diff_format_email/file3.txt new file mode 100644 index 000000000..9a2d780ac --- /dev/null +++ b/tests/resources/diff_format_email/file3.txt @@ -0,0 +1,5 @@ +file3 +file3! +file3 +file3 +file3 diff --git a/tests/revert/workdir.c b/tests/revert/workdir.c index 694f24710..e3d7e968a 100644 --- a/tests/revert/workdir.c +++ b/tests/revert/workdir.c @@ -66,7 +66,7 @@ void test_revert_workdir__conflicts(void) git_reference *head_ref; git_commit *head, *commit; git_oid revert_oid; - git_buf conflicting_buf = GIT_BUF_INIT; + git_buf conflicting_buf = GIT_BUF_INIT, mergemsg_buf = GIT_BUF_INIT; struct merge_index_entry merge_index_entries[] = { { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 1, "file1.txt" }, @@ -112,9 +112,21 @@ void test_revert_workdir__conflicts(void) "File one\n" \ ">>>>>>> parent of 72333f4... automergeable changes\n") == 0); + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_git_pass(git_futils_readbuffer(&mergemsg_buf, + TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(strcmp(mergemsg_buf.ptr, + "Revert \"automergeable changes\"\n" \ + "\n" \ + "This reverts commit 72333f47d4e83616630ff3b0ffe4c0faebcc3c45.\n" + "\n" \ + "Conflicts:\n" \ + "\tfile1.txt\n") == 0); + git_commit_free(commit); git_commit_free(head); git_reference_free(head_ref); + git_buf_free(&mergemsg_buf); git_buf_free(&conflicting_buf); } diff --git a/tests/status/ignore.c b/tests/status/ignore.c index acdc8fb58..052a8eae8 100644 --- a/tests/status/ignore.c +++ b/tests/status/ignore.c @@ -54,8 +54,10 @@ void test_status_ignore__0(void) } /* 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(g_repo, 0, ".gitignore")); + cl_assert(git_attr_cache__is_cached( + 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")); } @@ -228,6 +230,32 @@ void test_status_ignore__subdirectories(void) cl_assert(ignored); } +static void make_test_data(void) +{ + static const char *files[] = { + "empty_standard_repo/dir/a/ignore_me", + "empty_standard_repo/dir/b/ignore_me", + "empty_standard_repo/dir/ignore_me", + "empty_standard_repo/ignore_also/file", + "empty_standard_repo/ignore_me", + "empty_standard_repo/test/ignore_me/file", + "empty_standard_repo/test/ignore_me/file2", + "empty_standard_repo/test/ignore_me/and_me/file", + NULL + }; + static const char *repo = "empty_standard_repo"; + const char **scan; + size_t repolen = strlen(repo) + 1; + + g_repo = cl_git_sandbox_init(repo); + + for (scan = files; *scan != NULL; ++scan) { + cl_git_pass(git_futils_mkdir( + *scan + repolen, repo, 0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST)); + cl_git_mkfile(*scan, "contents"); + } +} + void test_status_ignore__subdirectories_recursion(void) { /* Let's try again with recursing into ignored dirs turned on */ @@ -235,6 +263,9 @@ void test_status_ignore__subdirectories_recursion(void) status_entry_counts counts; static const char *paths_r[] = { ".gitignore", + "dir/a/ignore_me", + "dir/b/ignore_me", + "dir/ignore_me", "ignore_also/file", "ignore_me", "test/ignore_me/and_me/file", @@ -242,49 +273,30 @@ void test_status_ignore__subdirectories_recursion(void) "test/ignore_me/file2", }; static const unsigned int statuses_r[] = { - GIT_STATUS_WT_NEW, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, + GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, }; static const char *paths_nr[] = { ".gitignore", + "dir/a/ignore_me", + "dir/b/ignore_me", + "dir/ignore_me", "ignore_also/", "ignore_me", "test/ignore_me/", }; static const unsigned int statuses_nr[] = { GIT_STATUS_WT_NEW, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, }; - g_repo = cl_git_sandbox_init("empty_standard_repo"); - + make_test_data(); cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n/ignore_also\n"); - cl_git_mkfile( - "empty_standard_repo/ignore_me", "I'm going to be ignored!"); - cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/test/ignore_me", NULL, 0775)); - cl_git_mkfile( - "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!"); - cl_git_mkfile( - "empty_standard_repo/test/ignore_me/file2", "Me, too!"); - cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/test/ignore_me/and_me", NULL, 0775)); - cl_git_mkfile( - "empty_standard_repo/test/ignore_me/and_me/file", "Deeply ignored"); - cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/ignore_also", NULL, 0775)); - cl_git_mkfile( - "empty_standard_repo/ignore_also/file", "I'm going to be ignored!"); - memset(&counts, 0x0, sizeof(status_entry_counts)); - counts.expected_entry_count = 6; + counts.expected_entry_count = 9; counts.expected_paths = paths_r; counts.expected_statuses = statuses_r; @@ -299,7 +311,7 @@ void test_status_ignore__subdirectories_recursion(void) memset(&counts, 0x0, sizeof(status_entry_counts)); - counts.expected_entry_count = 4; + counts.expected_entry_count = 7; counts.expected_paths = paths_nr; counts.expected_statuses = statuses_nr; @@ -313,6 +325,103 @@ void test_status_ignore__subdirectories_recursion(void) cl_assert_equal_i(0, counts.wrong_sorted_path); } +void test_status_ignore__subdirectories_not_at_root(void) +{ + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + static const char *paths_1[] = { + "dir/.gitignore", + "dir/a/ignore_me", + "dir/b/ignore_me", + "dir/ignore_me", + "ignore_also/file", + "ignore_me", + "test/.gitignore", + "test/ignore_me/and_me/file", + "test/ignore_me/file", + "test/ignore_me/file2", + }; + static const unsigned int statuses_1[] = { + GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, + }; + + make_test_data(); + cl_git_rewritefile("empty_standard_repo/dir/.gitignore", "ignore_me\n/ignore_also\n"); + cl_git_rewritefile("empty_standard_repo/test/.gitignore", "and_me\n"); + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = 10; + counts.expected_paths = paths_1; + counts.expected_statuses = statuses_1; + + opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; + + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__normal, &counts)); + + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); +} + +void test_status_ignore__leading_slash_ignores(void) +{ + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + git_buf home = GIT_BUF_INIT; + static const char *paths_2[] = { + "dir/.gitignore", + "dir/a/ignore_me", + "dir/b/ignore_me", + "dir/ignore_me", + "ignore_also/file", + "ignore_me", + "test/.gitignore", + "test/ignore_me/and_me/file", + "test/ignore_me/file", + "test/ignore_me/file2", + }; + static const unsigned int statuses_2[] = { + GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, + }; + + make_test_data(); + + cl_fake_home(&home); + cl_git_mkfile("home/.gitignore", "/ignore_me\n"); + { + git_config *cfg; + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string( + cfg, "core.excludesfile", "~/.gitignore")); + git_config_free(cfg); + } + + cl_git_rewritefile("empty_standard_repo/.git/info/exclude", "/ignore_also\n"); + cl_git_rewritefile("empty_standard_repo/dir/.gitignore", "/ignore_me\n"); + cl_git_rewritefile("empty_standard_repo/test/.gitignore", "/and_me\n"); + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = 10; + counts.expected_paths = paths_2; + counts.expected_statuses = statuses_2; + + opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; + + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__normal, &counts)); + + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); + + cl_fake_home_cleanup(&home); +} + void test_status_ignore__adding_internal_ignores(void) { int ignored; diff --git a/tests/status/submodules.c b/tests/status/submodules.c index 6d0d63a5f..63cf73f36 100644 --- a/tests/status/submodules.c +++ b/tests/status/submodules.c @@ -1,7 +1,5 @@ #include "clar_libgit2.h" -#include "buffer.h" -#include "path.h" -#include "posix.h" +#include "fileops.h" #include "status_helpers.h" #include "../submodule/submodule_helpers.h" @@ -389,3 +387,92 @@ void test_status_submodules__contained_untracked_repo(void) g_repo, &opts, cb_status__match, &counts)); cl_assert_equal_i(5, counts.entry_count); } + +void test_status_submodules__broken_stuff_that_git_allows(void) +{ + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + git_repository *contained; + static const char *expected_files_with_broken[] = { + ".gitmodules", + "added", + "broken/tracked", + "deleted", + "ignored", + "modified", + "untracked" + }; + static unsigned int expected_status_with_broken[] = { + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_IGNORED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + }; + + g_repo = setup_fixture_submodules(); + + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | + GIT_STATUS_OPT_INCLUDE_IGNORED; + + /* make a directory and stick a tracked item into the index */ + { + git_index *idx; + cl_must_pass(p_mkdir("submodules/broken", 0777)); + cl_git_mkfile("submodules/broken/tracked", "tracked content"); + cl_git_pass(git_repository_index(&idx, g_repo)); + cl_git_pass(git_index_add_bypath(idx, "broken/tracked")); + cl_git_pass(git_index_write(idx)); + git_index_free(idx); + } + + status_counts_init( + counts, expected_files_with_broken, expected_status_with_broken); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(7, counts.entry_count); + + /* directory with tracked items that looks a little bit like a repo */ + + cl_must_pass(p_mkdir("submodules/broken/.git", 0777)); + cl_must_pass(p_mkdir("submodules/broken/.git/info", 0777)); + cl_git_mkfile("submodules/broken/.git/info/exclude", "# bogus"); + + status_counts_init( + counts, expected_files_with_broken, expected_status_with_broken); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(7, counts.entry_count); + + /* directory with tracked items that is a repo */ + + cl_git_pass(git_futils_rmdir_r( + "submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_git_pass(git_repository_init(&contained, "submodules/broken", false)); + git_repository_free(contained); + + status_counts_init( + counts, expected_files_with_broken, expected_status_with_broken); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(7, counts.entry_count); + + /* directory with tracked items that claims to be a submodule but is not */ + + cl_git_pass(git_futils_rmdir_r( + "submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_git_append2file("submodules/.gitmodules", + "\n[submodule \"broken\"]\n" + "\tpath = broken\n" + "\turl = https://github.com/not/used\n\n"); + + status_counts_init( + counts, expected_files_with_broken, expected_status_with_broken); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(7, counts.entry_count); +} + diff --git a/tests/threads/diff.c b/tests/threads/diff.c new file mode 100644 index 000000000..79b85800b --- /dev/null +++ b/tests/threads/diff.c @@ -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); +} diff --git a/tests/threads/iterator.c b/tests/threads/iterator.c new file mode 100644 index 000000000..8aeae1a6c --- /dev/null +++ b/tests/threads/iterator.c @@ -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); +} diff --git a/tests/threads/refdb.c b/tests/threads/refdb.c index fbf6ac09b..3b35b45e3 100644 --- a/tests/threads/refdb.c +++ b/tests/threads/refdb.c @@ -37,6 +37,7 @@ static void *iterate_refs(void *arg) git_reference_iterator_free(i); + giterr_clear(); return arg; } @@ -115,6 +116,7 @@ static void *create_refs(void *arg) for (i = 0; i < 10; ++i) git_reference_free(ref[i]); + giterr_clear(); return arg; } @@ -141,6 +143,7 @@ static void *delete_refs(void *arg) } } + giterr_clear(); return arg; } diff --git a/tests/threads/thread_helpers.c b/tests/threads/thread_helpers.c new file mode 100644 index 000000000..25370dddb --- /dev/null +++ b/tests/threads/thread_helpers.c @@ -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); +} diff --git a/tests/threads/thread_helpers.h b/tests/threads/thread_helpers.h new file mode 100644 index 000000000..3c13cfb6b --- /dev/null +++ b/tests/threads/thread_helpers.h @@ -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)); |