diff options
127 files changed, 2252 insertions, 924 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ade3e3b1..7d5a4166a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ v0.23 + 1 * Symlinks are now followed when locking a file, which can be necessary when multiple worktrees share a base repository. +* You can now set your own user-agent to be sent for HTTP requests by + using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`. + ### API additions * `git_config_lock()` has been added, which allow for @@ -17,6 +20,16 @@ v0.23 + 1 the opportunity for concurrent operations and not committing any changes until the unlock. + +* `git_diff_options` added a new callback `progress_cb` to report on the + progress of the diff as files are being compared. The documentation of + the existing callback `notify_cb` was updated to reflect that it only + gets called when new deltas are added to the diff. + +* `git_stream_register_tls()` lets you register a callback to be used + as the constructor for a TLS stream instead of the libgit2 built-in + one. + ### API removals ### Breaking API changes @@ -31,10 +44,14 @@ v0.23 + 1 with which to implement the transactional/atomic semantics for the configuration backend. -* `git_index_add` will now use the case as provided by the caller on - case insensitive systems. Previous versions would keep the case as - it existed in the index. This does not affect the higher-level - `git_index_add_bypath` or `git_index_add_frombuffer` functions. +* `git_index_add` and `git_index_conflict_add()` will now use the case + as provided by the caller on case insensitive systems. Previous + versions would keep the case as it existed in the index. This does + not affect the higher-level `git_index_add_bypath` or + `git_index_add_frombuffer` functions. + +* The `notify_payload` field of `git_diff_options` was renamed to `payload` + to reflect that it's also the payload for the new progress callback. v0.23 ------ diff --git a/CMakeLists.txt b/CMakeLists.txt index f15a6e623..0537b19d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,13 +144,13 @@ FUNCTION(TARGET_OS_LIBRARIES target) ENDIF() ENDFUNCTION() -# For the MSVC IDE, this function splits up the source files like windows -# explorer does. This is esp. useful with the libgit2_clar project, were -# usually 2 or more files share the same name. Sadly, this file grouping -# is a per-directory option in cmake and not per-target, resulting in -# empty virtual folders "tests" for the git2.dll -FUNCTION(MSVC_SPLIT_SOURCES target) - IF(MSVC_IDE) +# This function splits the sources files up into their appropriate +# subdirectories. This is especially useful for IDEs like Xcode and +# Visual Studio, so that you can navigate into the libgit2_clar project, +# and see the folders within the tests folder (instead of just seeing all +# source and tests in a single folder.) +FUNCTION(IDE_SPLIT_SOURCES target) + IF(MSVC_IDE OR CMAKE_GENERATOR STREQUAL Xcode) GET_TARGET_PROPERTY(sources ${target} SOURCES) FOREACH(source ${sources}) IF(source MATCHES ".*/") @@ -571,7 +571,7 @@ IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS) SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64") ENDIF() -MSVC_SPLIT_SOURCES(git2) +IDE_SPLIT_SOURCES(git2) IF (SONAME) SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) @@ -602,7 +602,12 @@ INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} ) # Tests IF (BUILD_CLAR) - FIND_PACKAGE(PythonInterp REQUIRED) + FIND_PACKAGE(PythonInterp) + + IF(NOT PYTHONINTERP_FOUND) + MESSAGE(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. " + "Make sure python is available, or pass -DBUILD_CLAR=OFF to skip building the tests") + ENDIF() SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/") SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests") @@ -635,7 +640,7 @@ IF (BUILD_CLAR) TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES}) TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES}) TARGET_OS_LIBRARIES(libgit2_clar) - MSVC_SPLIT_SOURCES(libgit2_clar) + IDE_SPLIT_SOURCES(libgit2_clar) IF (MSVC_IDE) # Precompiled headers diff --git a/docs/error-handling.md b/docs/error-handling.md index 2dbe64a71..719244d2f 100644 --- a/docs/error-handling.md +++ b/docs/error-handling.md @@ -128,7 +128,7 @@ The public error API bugs, but in the meantime, please code defensively and check for NULL when calling this function. -- `void geterr_clear(void)`: This function clears the last error. The +- `void giterr_clear(void)`: This function clears the last error. The library will call this when an error is generated by low level function and the higher level function handles the error. diff --git a/examples/network/fetch.c b/examples/network/fetch.c index bc7a5a05e..177359b88 100644 --- a/examples/network/fetch.c +++ b/examples/network/fetch.c @@ -63,6 +63,7 @@ static int transfer_progress_cb(const git_transfer_progress *stats, void *payloa stats->received_objects, stats->total_objects, stats->indexed_objects, stats->received_bytes); } + return 0; } /** Entry point for this command */ diff --git a/include/git2/common.h b/include/git2/common.h index 1846e601a..e687977d5 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -10,12 +10,6 @@ #include <time.h> #include <stdlib.h> -#ifdef _MSC_VER -# include "inttypes.h" -#else -# include <inttypes.h> -#endif - #ifdef __cplusplus # define GIT_BEGIN_DECL extern "C" { # define GIT_END_DECL } @@ -26,6 +20,14 @@ # define GIT_END_DECL /* empty */ #endif +#if defined(_MSC_VER) && _MSC_VER < 1800 + GIT_BEGIN_DECL +# include "inttypes.h" + GIT_END_DECL +#else +# include <inttypes.h> +#endif + /** Declare a public function exported for application use. */ #if __GNUC__ >= 4 # define GIT_EXTERN(type) extern \ @@ -144,6 +146,7 @@ typedef enum { GIT_OPT_GET_TEMPLATE_PATH, GIT_OPT_SET_TEMPLATE_PATH, GIT_OPT_SET_SSL_CERT_LOCATIONS, + GIT_OPT_SET_USER_AGENT, } git_libgit2_opt_t; /** @@ -239,6 +242,8 @@ typedef enum { * > * > Either parameter may be `NULL`, but not both. * + * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent) + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff --git a/include/git2/config.h b/include/git2/config.h index 56b5431ac..d0f1ba1b3 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -29,25 +29,28 @@ GIT_BEGIN_DECL * priority levels as well. */ typedef enum { + /** System-wide on Windows, for compatibility with portable git */ + GIT_CONFIG_LEVEL_PROGRAMDATA = 1, + /** System-wide configuration file; /etc/gitconfig on Linux systems */ - GIT_CONFIG_LEVEL_SYSTEM = 1, + GIT_CONFIG_LEVEL_SYSTEM = 2, /** XDG compatible configuration file; typically ~/.config/git/config */ - GIT_CONFIG_LEVEL_XDG = 2, + GIT_CONFIG_LEVEL_XDG = 3, /** User-specific configuration file (also called Global configuration * file); typically ~/.gitconfig */ - GIT_CONFIG_LEVEL_GLOBAL = 3, + GIT_CONFIG_LEVEL_GLOBAL = 4, /** Repository specific configuration file; $WORK_DIR/.git/config on * non-bare repos */ - GIT_CONFIG_LEVEL_LOCAL = 4, + GIT_CONFIG_LEVEL_LOCAL = 5, /** Application specific configuration file; freely defined by applications */ - GIT_CONFIG_LEVEL_APP = 5, + GIT_CONFIG_LEVEL_APP = 6, /** Represents the highest level available config file (i.e. the most * specific config file available that actually is loaded) @@ -142,6 +145,17 @@ GIT_EXTERN(int) git_config_find_xdg(git_buf *out); GIT_EXTERN(int) git_config_find_system(git_buf *out); /** + * Locate the path to the configuration file in ProgramData + * + * Look for the file in %PROGRAMDATA%\Git\config used by portable git. + * + * @param out Pointer to a user-allocated git_buf in which to store the path + * @return 0 if a ProgramData configuration file has been + * found. Its path will be stored in `out`. + */ +GIT_EXTERN(int) git_config_find_programdata(git_buf *out); + +/** * Open the global, XDG and system configuration files * * Utility wrapper that finds the global, XDG and system configuration files diff --git a/include/git2/diff.h b/include/git2/diff.h index a0f6db350..cbffdb49a 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -351,6 +351,22 @@ typedef int (*git_diff_notify_cb)( void *payload); /** + * Diff progress callback. + * + * Called before each file comparison. + * + * @param diff_so_far The diff being generated. + * @param old_path The path to the old file or NULL. + * @param new_path The path to the new file or NULL. + * @return Non-zero to abort the diff. + */ +typedef int (*git_diff_progress_cb)( + const git_diff *diff_so_far, + const char *old_path, + const char *new_path, + void *payload); + +/** * Structure describing options about how the diff should be executed. * * Setting all values of the structure to zero will yield the default @@ -370,8 +386,10 @@ typedef int (*git_diff_notify_cb)( * - `max_size` is a file size (in bytes) above which a blob will be marked * as binary automatically; pass a negative value to disable. * - `notify_cb` is an optional callback function, notifying the consumer of - * which files are being examined as the diff is generated - * - `notify_payload` is the payload data to pass to the `notify_cb` function + * changes to the diff as new deltas are added. + * - `progress_cb` is an optional callback function, notifying the consumer of + * which files are being examined as the diff is generated. + * - `payload` is the payload to pass to the callback functions. * - `ignore_submodules` overrides the submodule ignore setting for all * submodules in the diff. */ @@ -383,8 +401,9 @@ typedef struct { git_submodule_ignore_t ignore_submodules; /**< submodule ignore rule */ git_strarray pathspec; /**< defaults to include all paths */ - git_diff_notify_cb notify_cb; - void *notify_payload; + git_diff_notify_cb notify_cb; + git_diff_progress_cb progress_cb; + void *payload; /* options controlling how to diff text is generated */ @@ -403,7 +422,7 @@ typedef struct { * `git_diff_options_init` programmatic initialization. */ #define GIT_DIFF_OPTIONS_INIT \ - {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, 3} + {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3} /** * Initializes a `git_diff_options` with default values. Equivalent to diff --git a/include/git2/errors.h b/include/git2/errors.h index 4698366d8..1b528cf25 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -49,6 +49,7 @@ typedef enum { GIT_EINVALID = -21, /**< Invalid operation or input */ GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */ GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */ + GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */ GIT_PASSTHROUGH = -30, /**< Internal only */ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */ diff --git a/include/git2/index.h b/include/git2/index.h index 176ba301e..466765be3 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -154,13 +154,27 @@ typedef enum { GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2), } git_index_add_option_t; -/** - * Match any index stage. - * - * Some index APIs take a stage to match; pass this value to match - * any entry matching the path regardless of stage. - */ -#define GIT_INDEX_STAGE_ANY -1 +typedef enum { + /** + * Match any index stage. + * + * Some index APIs take a stage to match; pass this value to match + * any entry matching the path regardless of stage. + */ + GIT_INDEX_STAGE_ANY = -1, + + /** A normal staged file in the index. */ + GIT_INDEX_STAGE_NORMAL = 0, + + /** The ancestor side of a conflict. */ + GIT_INDEX_STAGE_ANCESTOR = 1, + + /** The "ours" side of a conflict. */ + GIT_INDEX_STAGE_OURS = 2, + + /** The "theirs" side of a conflict. */ + GIT_INDEX_STAGE_THEIRS = 3, +} git_index_stage_t; /** @name Index File Functions * diff --git a/include/git2/merge.h b/include/git2/merge.h index 5fef452b9..b7da63e0e 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -72,6 +72,18 @@ typedef enum { * the ability to merge between a modified and renamed file. */ GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), + + /** + * If a conflict occurs, exit immediately instead of attempting to + * continue resolving conflicts. The merge operation will fail with + * GIT_EMERGECONFLICT and no index will be returned. + */ + GIT_MERGE_TREE_FAIL_ON_CONFLICT = (1 << 1), + + /** + * Do not write the REUC extension on the generated index + */ + GIT_MERGE_TREE_SKIP_REUC = (1 << 2), } git_merge_tree_flag_t; /** diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h index 55a714bbb..2b4ff7fd8 100644 --- a/include/git2/sys/stream.h +++ b/include/git2/sys/stream.h @@ -39,6 +39,19 @@ typedef struct git_stream { void (*free)(struct git_stream *); } git_stream; +typedef int (*git_stream_cb)(git_stream **out, const char *host, const char *port); + +/** + * Register a TLS stream constructor for the library to use + * + * If a constructor is already set, it will be overwritten. Pass + * `NULL` in order to deregister the current constructor. + * + * @param ctor the constructor to use + * @return 0 or an error code + */ +GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor); + GIT_END_DECL #endif diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index ca8617f3f..ce0234a18 100644 --- a/include/git2/sys/transport.h +++ b/include/git2/sys/transport.h @@ -10,6 +10,7 @@ #include "git2/net.h" #include "git2/types.h" +#include "git2/strarray.h" /** * @file git2/sys/transport.h diff --git a/src/attr_file.c b/src/attr_file.c index 89706865a..500c99bd9 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -35,11 +35,7 @@ int git_attr_file__new( return -1; } - if (git_pool_init(&attrs->pool, 1, 0) < 0) { - attr_file_free(attrs); - return -1; - } - + git_pool_init(&attrs->pool, 1); GIT_REFCOUNT_INC(attrs); attrs->entry = entry; attrs->source = source; diff --git a/src/attrcache.c b/src/attrcache.c index 5bc260460..a57110684 100644 --- a/src/attrcache.c +++ b/src/attrcache.c @@ -388,10 +388,11 @@ int git_attr_cache__do_init(git_repository *repo) * 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) + (ret = git_strmap_alloc(&cache->macros)) < 0) goto cancel; + git_pool_init(&cache->pool, 1); + cache = git__compare_and_swap(&repo->attrcache, NULL, cache); if (cache) goto cancel; /* raced with another thread, free this but no error */ diff --git a/src/blame.c b/src/blame.c index a52f9402b..08a90dcfd 100644 --- a/src/blame.c +++ b/src/blame.c @@ -331,7 +331,7 @@ static int blame_internal(git_blame *blame) blame->ent = ent; - git_blame__like_git(blame, blame->options.flags); + error = git_blame__like_git(blame, blame->options.flags); cleanup: for (ent = blame->ent; ent; ) { diff --git a/src/blame_git.c b/src/blame_git.c index f481426aa..67bae2384 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -9,6 +9,7 @@ #include "commit.h" #include "blob.h" #include "xdiff/xinclude.h" +#include "diff_xdiff.h" /* * Origin is refcounted and usually we keep the blob contents to be @@ -351,6 +352,13 @@ static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data) ecb.priv = cb_data; trim_common_tail(&file_a, &file_b, 0); + + if (file_a.size > GIT_XDIFF_MAX_SIZE || + file_b.size > GIT_XDIFF_MAX_SIZE) { + giterr_set(GITERR_INVALID, "file too large to blame"); + return -1; + } + return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb); } @@ -379,7 +387,9 @@ static int pass_blame_to_parent( fill_origin_blob(parent, &file_p); fill_origin_blob(target, &file_o); - diff_hunks(file_p, file_o, &d); + if (diff_hunks(file_p, file_o, &d) < 0) + return -1; + /* The reset (i.e. anything after tlno) are the same as the parent */ blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent); @@ -477,12 +487,13 @@ static void pass_whole_blame(git_blame *blame, } } -static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) +static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) { git_commit *commit = origin->commit; int i, num_parents; git_blame__origin *sg_buf[16]; git_blame__origin *porigin, **sg_origin = sg_buf; + int ret, error = 0; num_parents = git_commit_parentcount(commit); if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit)) @@ -540,8 +551,13 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt origin_incref(porigin); origin->previous = porigin; } - if (pass_blame_to_parent(blame, origin, porigin)) + + if ((ret = pass_blame_to_parent(blame, origin, porigin)) != 0) { + if (ret < 0) + error = -1; + goto finish; + } } /* TODO: optionally find moves in parents' files */ @@ -554,7 +570,7 @@ finish: origin_decref(sg_origin[i]); if (sg_origin != sg_buf) git__free(sg_origin); - return; + return error; } /* @@ -583,7 +599,7 @@ static void coalesce(git_blame *blame) } } -void git_blame__like_git(git_blame *blame, uint32_t opt) +int git_blame__like_git(git_blame *blame, uint32_t opt) { while (true) { git_blame__entry *ent; @@ -594,11 +610,13 @@ void git_blame__like_git(git_blame *blame, uint32_t opt) if (!ent->guilty) suspect = ent->suspect; if (!suspect) - return; /* all done */ + return 0; /* all done */ /* We'll use this suspect later in the loop, so hold on to it for now. */ origin_incref(suspect); - pass_blame(blame, suspect, opt); + + if (pass_blame(blame, suspect, opt) < 0) + return -1; /* Take responsibility for the remaining entries */ for (ent = blame->ent; ent; ent = ent->next) { @@ -613,6 +631,8 @@ void git_blame__like_git(git_blame *blame, uint32_t opt) } coalesce(blame); + + return 0; } void git_blame__free_entry(git_blame__entry *ent) diff --git a/src/blame_git.h b/src/blame_git.h index 3ec2710b8..1891b0e1f 100644 --- a/src/blame_git.h +++ b/src/blame_git.h @@ -15,6 +15,6 @@ int git_blame__get_origin( git_commit *commit, const char *path); void git_blame__free_entry(git_blame__entry *ent); -void git_blame__like_git(git_blame *sb, uint32_t flags); +int git_blame__like_git(git_blame *sb, uint32_t flags); #endif diff --git a/src/checkout.c b/src/checkout.c index 2a8bfd558..d09357f2a 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -18,6 +18,7 @@ #include "git2/submodule.h" #include "git2/sys/index.h" #include "git2/sys/filter.h" +#include "git2/merge.h" #include "refs.h" #include "repository.h" @@ -27,7 +28,7 @@ #include "diff.h" #include "pathspec.h" #include "buf_text.h" -#include "merge_file.h" +#include "diff_xdiff.h" #include "path.h" #include "attr.h" #include "pool.h" @@ -1254,11 +1255,13 @@ static int checkout_get_actions( int error = 0, act; const git_index_entry *wditem; git_vector pathspec = GIT_VECTOR_INIT, *deltas; - git_pool pathpool = GIT_POOL_INIT_STRINGPOOL; + git_pool pathpool; git_diff_delta *delta; size_t i, *counts = NULL; uint32_t *actions = NULL; + git_pool_init(&pathpool, 1); + if (data->opts.paths.count > 0 && git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0) return -1; @@ -2438,10 +2441,11 @@ static int checkout_data_init( git_config_entry_free(conflict_style); } + git_pool_init(&data->pool, 1); + if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 || (error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 || (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 || - (error = git_pool_init(&data->pool, 1, 0)) < 0 || (error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 || (error = git_path_to_dir(&data->path)) < 0 || (error = git_strmap_alloc(&data->mkdir_map)) < 0) diff --git a/src/commit.c b/src/commit.c index 616f947db..b42f9de28 100644 --- a/src/commit.c +++ b/src/commit.c @@ -431,22 +431,37 @@ const char *git_commit_summary(git_commit *commit) { git_buf summary = GIT_BUF_INIT; const char *msg, *space; + bool space_contains_newline = false; assert(commit); if (!commit->summary) { for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) { - if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n')) + char next_character = msg[0]; + /* stop processing at the end of the first paragraph */ + if (next_character == '\n' && (!msg[1] || msg[1] == '\n')) break; - else if (msg[0] == '\n') - git_buf_putc(&summary, ' '); - else if (git__isspace(msg[0])) - space = space ? space : msg; - else if (space) { - git_buf_put(&summary, space, (msg - space) + 1); - space = NULL; - } else - git_buf_putc(&summary, *msg); + /* record the beginning of contiguous whitespace runs */ + else if (git__isspace(next_character)) { + if(space == NULL) { + space = msg; + space_contains_newline = false; + } + space_contains_newline |= next_character == '\n'; + } + /* the next character is non-space */ + else { + /* process any recorded whitespace */ + if (space) { + if(space_contains_newline) + git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */ + else + git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */ + space = NULL; + } + /* copy the next character */ + git_buf_putc(&summary, next_character); + } } commit->summary = git_buf_detach(&summary); diff --git a/src/commit_list.c b/src/commit_list.c index 3054c18dd..28948c88b 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -47,7 +47,7 @@ git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_ git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk) { - return (git_commit_list_node *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC); + return (git_commit_list_node *)git_pool_mallocz(&walk->commit_pool, 1); } static int commit_error(git_commit_list_node *commit, const char *msg) @@ -110,7 +110,7 @@ static int commit_quick_parse( const uint8_t *buffer_end = buffer + buffer_len; const uint8_t *parents_start, *committer_start; int i, parents = 0; - int commit_time; + int64_t commit_time; buffer += strlen("tree ") + GIT_OID_HEXSZ + 1; @@ -166,10 +166,10 @@ static int commit_quick_parse( buffer--; } - if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0)) + if ((buffer == committer_start) || (git__strtol64(&commit_time, (char *)(buffer + 1), NULL, 10) < 0)) return commit_error(commit, "cannot parse commit time"); - commit->time = (time_t)commit_time; + commit->time = commit_time; commit->parsed = 1; return 0; } diff --git a/src/commit_list.h b/src/commit_list.h index 6b3f473d3..a6967bcef 100644 --- a/src/commit_list.h +++ b/src/commit_list.h @@ -13,6 +13,7 @@ #define PARENT2 (1 << 1) #define RESULT (1 << 2) #define STALE (1 << 3) +#define ALL_FLAGS (PARENT1 | PARENT2 | STALE | RESULT) #define PARENTS_PER_COMMIT 2 #define COMMIT_ALLOC \ @@ -22,7 +23,7 @@ typedef struct git_commit_list_node { git_oid oid; - uint32_t time; + int64_t time; unsigned int seen:1, uninteresting:1, topo_delay:1, diff --git a/src/common.h b/src/common.h index c87343dda..2913baa92 100644 --- a/src/common.h +++ b/src/common.h @@ -209,6 +209,15 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v #define GITERR_CHECK_ALLOC_ADD(out, one, two) \ if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { return -1; } +#define GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { return -1; } + +#define GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; } + /** Check for multiplicative overflow, failing if it would occur. */ #define GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \ if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; } diff --git a/src/config.c b/src/config.c index f0b2c3a61..f4d4cb2b9 100644 --- a/src/config.c +++ b/src/config.c @@ -1086,6 +1086,12 @@ int git_config_find_system(git_buf *path) return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM); } +int git_config_find_programdata(git_buf *path) +{ + git_buf_sanitize(path); + return git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA); +} + int git_config__global_location(git_buf *buf) { const git_buf *paths; @@ -1133,6 +1139,10 @@ int git_config_open_default(git_config **out) error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_SYSTEM, 0); + if (!error && !git_config_find_programdata(&buf)) + error = git_config_add_file_ondisk(cfg, buf.ptr, + GIT_CONFIG_LEVEL_PROGRAMDATA, 0); + git_buf_free(&buf); if (error) { diff --git a/src/config.h b/src/config.h index ba745331a..00c12b50d 100644 --- a/src/config.h +++ b/src/config.h @@ -12,6 +12,7 @@ #include "vector.h" #include "repository.h" +#define GIT_CONFIG_FILENAME_PROGRAMDATA "config" #define GIT_CONFIG_FILENAME_SYSTEM "gitconfig" #define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig" #define GIT_CONFIG_FILENAME_XDG "config" diff --git a/src/config_file.c b/src/config_file.c index 46f21c0f1..5f5e309e0 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -77,8 +77,7 @@ typedef struct git_config_file_iter { (iter) = (tmp)) struct reader { - time_t file_mtime; - size_t file_size; + git_oid checksum; char *file_path; git_buf buffer; char *read_ptr; @@ -285,7 +284,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) git_buf_init(&reader->buffer, 0); res = git_futils_readbuffer_updated( - &reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL); + &reader->buffer, b->file_path, &reader->checksum, NULL); /* It's fine if the file doesn't exist */ if (res == GIT_ENOTFOUND) @@ -345,7 +344,7 @@ static int config_refresh(git_config_backend *cfg) reader = git_array_get(b->readers, i); error = git_futils_readbuffer_updated( &reader->buffer, reader->file_path, - &reader->file_mtime, &reader->file_size, &updated); + &reader->checksum, &updated); if (error < 0 && error != GIT_ENOTFOUND) return error; @@ -1618,7 +1617,7 @@ static int read_on_variable( git_buf_init(&r->buffer, 0); result = git_futils_readbuffer_updated( - &r->buffer, r->file_path, &r->file_mtime, &r->file_size, NULL); + &r->buffer, r->file_path, &r->checksum, NULL); if (result == 0) { result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1); @@ -1894,7 +1893,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p } else { /* Lock the file */ if ((result = git_filebuf_open( - &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) { + &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) { git_buf_free(&reader->buffer); return result; } @@ -1945,10 +1944,6 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len); } else { git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf)); - - /* refresh stats - if this errors, then commit will error too */ - (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file); - result = git_filebuf_commit(&file); } diff --git a/src/diff.c b/src/diff.c index 09f29fcee..0ab3b8d1f 100644 --- a/src/diff.c +++ b/src/diff.c @@ -56,7 +56,7 @@ static int diff_insert_delta( if (diff->opts.notify_cb) { error = diff->opts.notify_cb( - diff, delta, matched_pathspec, diff->opts.notify_payload); + diff, delta, matched_pathspec, diff->opts.payload); if (error) { git__free(delta); @@ -430,8 +430,9 @@ static git_diff *diff_list_alloc( diff->new_src = new_iter->type; memcpy(&diff->opts, &dflt, sizeof(diff->opts)); - if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 || - git_pool_init(&diff->pool, 1, 0) < 0) { + git_pool_init(&diff->pool, 1); + + if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0) { git_diff_free(diff); return NULL; } @@ -493,8 +494,10 @@ static int diff_list_apply_options( /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ - /* Set GIT_DIFFCAPS_TRUST_NANOSECS on a platform basis */ + /* Don't trust nanoseconds; we do not load nanos from disk */ +#ifdef GIT_USE_NSEC diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_NANOSECS; +#endif /* If not given explicit `opts`, check `diff.xyz` configs */ if (!opts) { @@ -1286,7 +1289,18 @@ int git_diff__from_iterators( /* run iterators building diffs */ while (!error && (info.oitem || info.nitem)) { - int cmp = info.oitem ? + int cmp; + + /* report progress */ + if (opts && opts->progress_cb) { + if ((error = opts->progress_cb(diff, + info.oitem ? info.oitem->path : NULL, + info.nitem ? info.nitem->path : NULL, + opts->payload))) + break; + } + + cmp = info.oitem ? (info.nitem ? diff->entrycomp(info.oitem, info.nitem) : -1) : 1; /* create DELETED records for old items not matched in new */ diff --git a/src/diff_file.c b/src/diff_file.c index c60362865..ecc34cf55 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -259,10 +259,35 @@ static int diff_file_content_load_blob( return error; } +static int diff_file_content_load_workdir_symlink_fake( + git_diff_file_content *fc, git_buf *path) +{ + git_buf target = GIT_BUF_INIT; + int error; + + if ((error = git_futils_readbuffer(&target, path->ptr)) < 0) + return error; + + fc->map.len = git_buf_len(&target); + fc->map.data = git_buf_detach(&target); + fc->flags |= GIT_DIFF_FLAG__FREE_DATA; + + git_buf_free(&target); + return error; +} + static int diff_file_content_load_workdir_symlink( git_diff_file_content *fc, git_buf *path) { ssize_t alloc_len, read_len; + int symlink_supported, error; + + if ((error = git_repository__cvar( + &symlink_supported, fc->repo, GIT_CVAR_SYMLINKS)) < 0) + return -1; + + if (!symlink_supported) + return diff_file_content_load_workdir_symlink_fake(fc, path); /* link path on disk could be UTF-16, so prepare a buffer that is * big enough to handle some UTF-8 data expansion diff --git a/src/diff_patch.c b/src/diff_patch.c index 0628da6f2..50faa3b3f 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -30,6 +30,10 @@ static void diff_patch_update_binary(git_patch *patch) (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) patch->delta->flags |= GIT_DIFF_FLAG_BINARY; + else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE || + patch->nfile.file->size > GIT_XDIFF_MAX_SIZE) + patch->delta->flags |= GIT_DIFF_FLAG_BINARY; + else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 && (patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0) patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; diff --git a/src/diff_tform.c b/src/diff_tform.c index 92647e330..7cff34159 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -134,11 +134,11 @@ int git_diff__merge( return -1; } - if (git_vector_init( - &onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 || - git_pool_init(&onto_pool, 1, 0) < 0) + if (git_vector_init(&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0) return -1; + git_pool_init(&onto_pool, 1); + for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) { git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i); const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j); diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index e5984f1c9..1057df3aa 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -4,6 +4,7 @@ * 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 "git2/errors.h" #include "common.h" #include "diff.h" #include "diff_driver.h" @@ -208,6 +209,12 @@ static int git_xdiff(git_diff_output *output, git_patch *patch) git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch); git_patch__new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch); + if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE || + info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) { + giterr_set(GITERR_INVALID, "files too large for diff"); + return -1; + } + xdl_diff(&info.xd_old_data, &info.xd_new_data, &xo->params, &xo->config, &xo->callback); diff --git a/src/diff_xdiff.h b/src/diff_xdiff.h index c547b00cf..98e11b2cb 100644 --- a/src/diff_xdiff.h +++ b/src/diff_xdiff.h @@ -11,6 +11,11 @@ #include "diff_patch.h" #include "xdiff/xdiff.h" +/* xdiff cannot cope with large files. these files should not be passed to + * xdiff. callers should treat these large files as binary. + */ +#define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023) + /* A git_xdiff_output is a git_diff_output with extra fields necessary * to use libxdiff. Calling git_xdiff_init() will set the diff_cb field * of the output to use xdiff to generate the diffs. diff --git a/src/filebuf.c b/src/filebuf.c index 2bbc210ba..17efe872e 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -357,6 +357,12 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode memcpy(file->path_lock, file->path_original, path_len); memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); + if (git_path_isdir(file->path_original)) { + giterr_set(GITERR_FILESYSTEM, "path '%s' is a directory", file->path_original); + error = GIT_EDIRECTORY; + goto cleanup; + } + /* open the file for locking */ if ((error = lock_file(file, flags, mode)) < 0) goto cleanup; diff --git a/src/fileops.c b/src/fileops.c index a13a40655..07eb504bd 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -153,13 +153,15 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) } int git_futils_readbuffer_updated( - git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated) + git_buf *out, const char *path, git_oid *checksum, int *updated) { + int error; git_file fd; struct stat st; - bool changed = false; + git_buf buf = GIT_BUF_INIT; + git_oid checksum_new; - assert(buf && path && *path); + assert(out && path && *path); if (updated != NULL) *updated = 0; @@ -178,45 +180,50 @@ int git_futils_readbuffer_updated( return -1; } - /* - * If we were given a time and/or a size, we only want to read the file - * if it has been modified. - */ - if (size && *size != (size_t)st.st_size) - changed = true; - if (mtime && *mtime != (time_t)st.st_mtime) - changed = true; - if (!size && !mtime) - changed = true; - - if (!changed) { - return 0; - } - - if (mtime != NULL) - *mtime = st.st_mtime; - if (size != NULL) - *size = (size_t)st.st_size; - if ((fd = git_futils_open_ro(path)) < 0) return fd; - if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) { + if (git_futils_readbuffer_fd(&buf, fd, (size_t)st.st_size) < 0) { p_close(fd); return -1; } p_close(fd); + if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { + git_buf_free(&buf); + return error; + } + + /* + * If we were given a checksum, we only want to use it if it's different + */ + if (checksum && !git_oid__cmp(checksum, &checksum_new)) { + git_buf_free(&buf); + if (updated) + *updated = 0; + + return 0; + } + + /* + * If we're here, the file did change, or the user didn't have an old version + */ + if (checksum) + git_oid_cpy(checksum, &checksum_new); + if (updated != NULL) *updated = 1; + git_buf_swap(out, &buf); + git_buf_free(&buf); + return 0; } int git_futils_readbuffer(git_buf *buf, const char *path) { - return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL); + return git_futils_readbuffer_updated(buf, path, NULL, NULL); } int git_futils_writebuffer( diff --git a/src/fileops.h b/src/fileops.h index d806e83ba..6c6c49dcf 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -13,6 +13,7 @@ #include "path.h" #include "pool.h" #include "strmap.h" +#include "oid.h" /** * Filebuffer methods @@ -21,7 +22,7 @@ */ extern int git_futils_readbuffer(git_buf *obj, const char *path); extern int git_futils_readbuffer_updated( - git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated); + git_buf *obj, const char *path, git_oid *checksum, int *updated); extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len); extern int git_futils_writebuffer( diff --git a/src/global.c b/src/global.c index 3d37ee4de..0eab8d552 100644 --- a/src/global.c +++ b/src/global.c @@ -31,6 +31,7 @@ static git_mutex *openssl_locks; static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; static git_atomic git__n_shutdown_callbacks; static git_atomic git__n_inits; +char *git__user_agent; void git__on_shutdown(git_global_shutdown_fn callback) { @@ -269,6 +270,8 @@ int git_libgit2_shutdown(void) git_win32__crtdbg_stacktrace_cleanup(); git_win32__stack_cleanup(); #endif + + git__free(git__user_agent); } /* Exit the lock */ @@ -369,6 +372,7 @@ int git_libgit2_shutdown(void) git__global_state_cleanup(ptr); git__free(ptr); + git__free(git__user_agent); pthread_key_delete(_tls_key); git_mutex_free(&git__mwindow_mutex); @@ -423,6 +427,7 @@ int git_libgit2_shutdown(void) git__shutdown(); git__global_state_cleanup(&__state); uninit_ssl(); + git__free(git__user_agent); return 0; } diff --git a/src/global.h b/src/global.h index 37e909ac6..9fdcee573 100644 --- a/src/global.h +++ b/src/global.h @@ -35,4 +35,6 @@ extern void git__on_shutdown(git_global_shutdown_fn callback); extern void git__free_tls_data(void); +extern const char *git_libgit2__user_agent(void); + #endif diff --git a/src/index.c b/src/index.c index 5311fe85f..0903770bb 100644 --- a/src/index.c +++ b/src/index.c @@ -439,7 +439,7 @@ int git_index_open(git_index **index_out, const char *index_path) return -1; } - git_pool_init(&index->tree_pool, 1, 0); + git_pool_init(&index->tree_pool, 1); if (index_path != NULL) { index->index_file_path = git__strdup(index_path); @@ -1124,7 +1124,9 @@ static int check_file_directory_collision(git_index *index, } static int canonicalize_directory_path( - git_index *index, git_index_entry *entry) + git_index *index, + git_index_entry *entry, + git_index_entry *existing) { const git_index_entry *match, *best = NULL; char *search, *sep; @@ -1134,8 +1136,8 @@ static int canonicalize_directory_path( return 0; /* item already exists in the index, simply re-use the existing case */ - if ((match = git_index_get_bypath(index, entry->path, 0)) != NULL) { - memcpy((char *)entry->path, match->path, strlen(entry->path)); + if (existing) { + memcpy((char *)entry->path, existing->path, strlen(existing->path)); return 0; } @@ -1200,6 +1202,52 @@ static int index_no_dups(void **old, void *new) return GIT_EEXISTS; } +static void index_existing_and_best( + git_index_entry **existing, + size_t *existing_position, + git_index_entry **best, + git_index *index, + const git_index_entry *entry) +{ + git_index_entry *e; + size_t pos; + int error; + + error = index_find(&pos, + index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false); + + if (error == 0) { + *existing = index->entries.contents[pos]; + *existing_position = pos; + *best = index->entries.contents[pos]; + return; + } + + *existing = NULL; + *existing_position = 0; + *best = NULL; + + if (GIT_IDXENTRY_STAGE(entry) == 0) { + for (; pos < index->entries.length; pos++) { + int (*strcomp)(const char *a, const char *b) = + index->ignore_case ? git__strcasecmp : git__strcmp; + + e = index->entries.contents[pos]; + + if (strcomp(entry->path, e->path) != 0) + break; + + if (GIT_IDXENTRY_STAGE(e) == GIT_INDEX_STAGE_ANCESTOR) { + *best = e; + continue; + } else { + *best = e; + break; + } + } + } +} + /* 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 @@ -1218,7 +1266,7 @@ static int index_insert( { int error = 0; size_t path_length, position; - git_index_entry *existing = NULL, *entry; + git_index_entry *existing, *best, *entry; assert(index && entry_ptr); @@ -1241,20 +1289,19 @@ static int index_insert( git_vector_sort(&index->entries); - /* look if an entry with this path already exists */ - 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 */ - if (trust_mode) - entry->mode = git_index__create_mode(entry->mode); - else - entry->mode = index_merge_mode(index, existing, entry->mode); - } + /* look if an entry with this path already exists, either staged, or (if + * this entry is a regular staged item) as the "ours" side of a conflict. + */ + index_existing_and_best(&existing, &position, &best, index, entry); + + /* update the file mode */ + entry->mode = trust_mode ? + git_index__create_mode(entry->mode) : + index_merge_mode(index, best, entry->mode); /* canonicalize the directory name */ if (!trust_path) - error = canonicalize_directory_path(index, entry); + error = canonicalize_directory_path(index, entry, best); /* look for tree / blob name collisions, removing conflicts if requested */ if (!error) @@ -1654,7 +1701,7 @@ int git_index_conflict_add(git_index *index, for (i = 0; i < 3; i++) { if (entries[i] && !valid_filemode(entries[i]->mode)) { giterr_set(GITERR_INDEX, "invalid filemode for stage %d entry", - i); + i + 1); return -1; } } @@ -1681,7 +1728,7 @@ int git_index_conflict_add(git_index *index, /* Make sure stage is correct */ GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); - if ((ret = index_insert(index, &entries[i], 0, true, true)) < 0) + if ((ret = index_insert(index, &entries[i], 1, true, true)) < 0) goto on_error; entries[i] = NULL; /* don't free if later entry fails */ @@ -1963,27 +2010,24 @@ size_t git_index_reuc_entrycount(git_index *index) return index->reuc.length; } +static int index_reuc_on_dup(void **old, void *new) +{ + index_entry_reuc_free(*old); + *old = new; + return GIT_EEXISTS; +} + static int index_reuc_insert( git_index *index, - git_index_reuc_entry *reuc, - int replace) + git_index_reuc_entry *reuc) { - git_index_reuc_entry **existing = NULL; - size_t position; + int res; assert(index && reuc && reuc->path != NULL); + assert(git_vector_is_sorted(&index->reuc)); - if (!git_index_reuc_find(&position, index, reuc->path)) - existing = (git_index_reuc_entry **)&index->reuc.contents[position]; - - if (!replace || !existing) - return git_vector_insert(&index->reuc, reuc); - - /* exists, replace it */ - git__free(*existing); - *existing = reuc; - - return 0; + res = git_vector_insert_sorted(&index->reuc, reuc, &index_reuc_on_dup); + return res == GIT_EEXISTS ? 0 : res; } int git_index_reuc_add(git_index *index, const char *path, @@ -1998,7 +2042,7 @@ int git_index_reuc_add(git_index *index, const char *path, if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || - (error = index_reuc_insert(index, reuc, 1)) < 0) + (error = index_reuc_insert(index, reuc)) < 0) index_entry_reuc_free(reuc); return error; @@ -2018,7 +2062,7 @@ const git_index_reuc_entry *git_index_reuc_get_bypath( if (!index->reuc.length) return NULL; - git_vector_sort(&index->reuc); + assert(git_vector_is_sorted(&index->reuc)); if (git_index_reuc_find(&pos, index, path) < 0) return NULL; @@ -2030,8 +2074,8 @@ const git_index_reuc_entry *git_index_reuc_get_byindex( git_index *index, size_t n) { assert(index); + assert(git_vector_is_sorted(&index->reuc)); - git_vector_sort(&index->reuc); return git_vector_get(&index->reuc, n); } @@ -2040,7 +2084,7 @@ int git_index_reuc_remove(git_index *index, size_t position) int error; git_index_reuc_entry *reuc; - git_vector_sort(&index->reuc); + assert(git_vector_is_sorted(&index->reuc)); reuc = git_vector_get(&index->reuc, position); error = git_vector_remove(&index->reuc, position); @@ -2865,6 +2909,7 @@ int git_index_read_index( { git_vector new_entries = GIT_VECTOR_INIT, remove_entries = GIT_VECTOR_INIT; + git_idxmap *new_entries_map = NULL; git_iterator *index_iterator = NULL; git_iterator *new_iterator = NULL; git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; @@ -2874,9 +2919,15 @@ int git_index_read_index( int error; if ((error = git_vector_init(&new_entries, new_index->entries.length, index->entries._cmp)) < 0 || - (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0) + (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 || + (error = git_idxmap_alloc(&new_entries_map)) < 0) goto done; + if (index->ignore_case) + kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_index->entries.length); + else + kh_resize(idx, new_entries_map, new_index->entries.length); + opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_index(&index_iterator, index, &opts)) < 0 || @@ -2890,6 +2941,7 @@ int git_index_read_index( goto done; while (true) { + git_index_entry *add_entry = NULL, *remove_entry = NULL; int diff; if (old_entry && new_entry) @@ -2902,27 +2954,37 @@ int git_index_read_index( break; if (diff < 0) { - git_vector_insert(&remove_entries, (git_index_entry *)old_entry); + remove_entry = (git_index_entry *)old_entry; } else if (diff > 0) { - if ((error = index_entry_dup(&entry, index, new_entry)) < 0) + if ((error = index_entry_dup(&add_entry, index, new_entry)) < 0) goto done; - - git_vector_insert(&new_entries, entry); } else { /* Path and stage are equal, if the OID is equal, keep it to * keep the stat cache data. */ if (git_oid_equal(&old_entry->id, &new_entry->id)) { - git_vector_insert(&new_entries, (git_index_entry *)old_entry); + add_entry = (git_index_entry *)old_entry; } else { - if ((error = index_entry_dup(&entry, index, new_entry)) < 0) + if ((error = index_entry_dup(&add_entry, index, new_entry)) < 0) goto done; - git_vector_insert(&new_entries, entry); - git_vector_insert(&remove_entries, (git_index_entry *)old_entry); + remove_entry = (git_index_entry *)old_entry; } } + if (add_entry) { + if ((error = git_vector_insert(&new_entries, add_entry)) == 0) + INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error); + } + + if (remove_entry && !error) + error = git_vector_insert(&remove_entries, remove_entry); + + if (error < 0) { + giterr_set(GITERR_INDEX, "failed to insert entry"); + return error; + } + if (diff <= 0) { if ((error = git_iterator_advance(&old_entry, index_iterator)) < 0 && error != GIT_ITEROVER) @@ -2940,6 +3002,7 @@ int git_index_read_index( git_index_reuc_clear(index); git_vector_swap(&new_entries, &index->entries); + new_entries_map = git__swap(index->entries_map, new_entries_map); git_vector_foreach(&remove_entries, i, entry) { if (index->tree) @@ -2951,6 +3014,7 @@ int git_index_read_index( error = 0; done: + git_idxmap_free(new_entries_map); git_vector_free(&new_entries); git_vector_free(&remove_entries); git_iterator_free(index_iterator); diff --git a/src/iterator.c b/src/iterator.c index e3a2abf66..ee348de6e 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -567,7 +567,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final) tree_iterator__move_to_next(ti, tf); if (!final) { /* if final, don't bother to clean up */ - git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries); + // TODO: maybe free the pool so far? git_buf_rtruncate_at_char(&ti->path, '/'); } @@ -822,8 +822,9 @@ int git_iterator_for_tree( if ((error = iterator__update_ignore_case((git_iterator *)ti, options ? options->flags : 0)) < 0) goto fail; - if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 || - (error = tree_iterator__create_root_frame(ti, tree)) < 0 || + git_pool_init(&ti->pool, sizeof(tree_iterator_entry)); + + if ((error = tree_iterator__create_root_frame(ti, tree)) < 0 || (error = tree_iterator__push_frame(ti)) < 0) /* expand root now */ goto fail; diff --git a/src/merge.c b/src/merge.c index fc5088c82..bad5f9552 100644 --- a/src/merge.c +++ b/src/merge.c @@ -20,7 +20,6 @@ #include "diff.h" #include "checkout.h" #include "tree.h" -#include "merge_file.h" #include "blob.h" #include "oid.h" #include "index.h" @@ -61,12 +60,6 @@ struct merge_diff_df_data { git_merge_diff *prev_conflict; }; -GIT_INLINE(int) merge_diff_detect_binary( - bool *binary_out, - git_repository *repo, - const git_merge_diff *conflict); - - /* Merge base computation */ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[]) @@ -309,30 +302,59 @@ static int interesting(git_pqueue *list) return 0; } -int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos) +static void clear_commit_marks_1(git_commit_list **plist, + git_commit_list_node *commit, unsigned int mark) { - int error; - unsigned int i; - git_commit_list_node *two; - git_commit_list *result = NULL, *tmp = NULL; - git_pqueue list; + while (commit) { + unsigned int i; - /* If there's only the one commit, there can be no merge bases */ - if (twos->length == 0) { - *out = NULL; - return 0; + if (!(mark & commit->flags)) + return; + + commit->flags &= ~mark; + + for (i = 1; i < commit->out_degree; i++) { + git_commit_list_node *p = commit->parents[i]; + git_commit_list_insert(p, plist); + } + + commit = commit->out_degree ? commit->parents[0] : NULL; } +} - /* if the commit is repeated, we have a our merge base already */ - git_vector_foreach(twos, i, two) { - if (one == two) - return git_commit_list_insert(one, out) ? 0 : -1; +static void clear_commit_marks_many(git_vector *commits, unsigned int mark) +{ + git_commit_list *list = NULL; + git_commit_list_node *c; + unsigned int i; + + git_vector_foreach(commits, i, c) { + git_commit_list_insert(c, &list); } - if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0) - return -1; + while (list) + clear_commit_marks_1(&list, git_commit_list_pop(&list), mark); +} - if (git_commit_list_parse(walk, one) < 0) +static void clear_commit_marks(git_commit_list_node *commit, unsigned int mark) +{ + git_commit_list *list = NULL; + git_commit_list_insert(commit, &list); + while (list) + clear_commit_marks_1(&list, git_commit_list_pop(&list), mark); +} + +static int paint_down_to_common( + git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos) +{ + git_pqueue list; + git_commit_list *result = NULL; + git_commit_list_node *two; + + int error; + unsigned int i; + + if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0) return -1; one->flags |= PARENT1; @@ -383,19 +405,138 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l } git_pqueue_free(&list); + *out = result; + return 0; +} + +static int remove_redundant(git_revwalk *walk, git_vector *commits) +{ + git_vector work = GIT_VECTOR_INIT; + unsigned char *redundant; + unsigned int *filled_index; + unsigned int i, j; + int error = 0; + + redundant = git__calloc(commits->length, 1); + GITERR_CHECK_ALLOC(redundant); + filled_index = git__calloc((commits->length - 1), sizeof(unsigned int)); + GITERR_CHECK_ALLOC(filled_index); + + for (i = 0; i < commits->length; ++i) { + if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0) + goto done; + } + + for (i = 0; i < commits->length; ++i) { + git_commit_list *common = NULL; + git_commit_list_node *commit = commits->contents[i]; + + if (redundant[i]) + continue; + + git_vector_clear(&work); + + for (j = 0; j < commits->length; j++) { + if (i == j || redundant[j]) + continue; + + filled_index[work.length] = j; + if ((error = git_vector_insert(&work, commits->contents[j])) < 0) + goto done; + } + + error = paint_down_to_common(&common, walk, commit, &work); + if (error < 0) + goto done; + + if (commit->flags & PARENT2) + redundant[i] = 1; + + for (j = 0; j < work.length; j++) { + git_commit_list_node *w = work.contents[j]; + if (w->flags & PARENT1) + redundant[filled_index[j]] = 1; + } + + clear_commit_marks(commit, ALL_FLAGS); + clear_commit_marks_many(&work, ALL_FLAGS); + + git_commit_list_free(&common); + } + + for (i = 0; i < commits->length; ++i) { + if (redundant[i]) + commits->contents[i] = NULL; + } + +done: + git__free(redundant); + git__free(filled_index); + git_vector_free(&work); + return error; +} + +int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos) +{ + int error; + unsigned int i; + git_commit_list_node *two; + git_commit_list *result = NULL, *tmp = NULL; + + /* If there's only the one commit, there can be no merge bases */ + if (twos->length == 0) { + *out = NULL; + return 0; + } + + /* if the commit is repeated, we have a our merge base already */ + git_vector_foreach(twos, i, two) { + if (one == two) + return git_commit_list_insert(one, out) ? 0 : -1; + } + + if (git_commit_list_parse(walk, one) < 0) + return -1; + + error = paint_down_to_common(&result, walk, one, twos); + if (error < 0) + return error; /* filter out any stale commits in the results */ tmp = result; result = NULL; while (tmp) { - struct git_commit_list *next = tmp->next; - if (!(tmp->item->flags & STALE)) - if (git_commit_list_insert_by_date(tmp->item, &result) == NULL) + git_commit_list_node *c = git_commit_list_pop(&tmp); + if (!(c->flags & STALE)) + if (git_commit_list_insert_by_date(c, &result) == NULL) return -1; + } + + /* + * more than one merge base -- see if there are redundant merge + * bases and remove them + */ + if (result && result->next) { + git_vector redundant = GIT_VECTOR_INIT; - git__free(tmp); - tmp = next; + while (result) + git_vector_insert(&redundant, git_commit_list_pop(&result)); + + clear_commit_marks(one, ALL_FLAGS); + clear_commit_marks_many(twos, ALL_FLAGS); + + if ((error = remove_redundant(walk, &redundant)) < 0) { + git_vector_free(&redundant); + return error; + } + + git_vector_foreach(&redundant, i, two) { + if (two != NULL) + git_commit_list_insert_by_date(two, &result); + } + + git_vector_free(&redundant); } *out = result; @@ -668,7 +809,6 @@ static int merge_conflict_resolve_automerge( git_odb *odb = NULL; git_oid automerge_oid; int error = 0; - bool binary = false; assert(resolved && diff_list && conflict); @@ -703,12 +843,6 @@ static int merge_conflict_resolve_automerge( strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0) return 0; - /* Reject binary conflicts */ - if ((error = merge_diff_detect_binary(&binary, diff_list->repo, conflict)) < 0) - return error; - if (binary) - return 0; - ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? &conflict->ancestor_entry : NULL; ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? @@ -725,7 +859,7 @@ static int merge_conflict_resolve_automerge( (error = git_odb_write(&automerge_oid, odb, result.ptr, result.len, GIT_OBJ_BLOB)) < 0) goto done; - if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) + if ((index_entry = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry))) == NULL) GITERR_CHECK_ALLOC(index_entry); index_entry->path = git_pool_strdup(&diff_list->pool, result.path); @@ -1314,48 +1448,6 @@ GIT_INLINE(int) merge_diff_detect_type( return 0; } -GIT_INLINE(int) merge_diff_detect_binary( - bool *binary_out, - git_repository *repo, - const git_merge_diff *conflict) -{ - git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL; - int error = 0; - bool binary = false; - - if (GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->ancestor_entry)) { - if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor_entry.id)) < 0) - goto done; - - binary = git_blob_is_binary(ancestor_blob); - } - - if (!binary && - GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->our_entry)) { - if ((error = git_blob_lookup(&our_blob, repo, &conflict->our_entry.id)) < 0) - goto done; - - binary = git_blob_is_binary(our_blob); - } - - if (!binary && - GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->their_entry)) { - if ((error = git_blob_lookup(&their_blob, repo, &conflict->their_entry.id)) < 0) - goto done; - - binary = git_blob_is_binary(their_blob); - } - - *binary_out = binary; - -done: - git_blob_free(ancestor_blob); - git_blob_free(our_blob); - git_blob_free(their_blob); - - return error; -} - GIT_INLINE(int) index_entry_dup_pool( git_index_entry *out, git_pool *pool, @@ -1363,7 +1455,6 @@ GIT_INLINE(int) index_entry_dup_pool( { if (src != NULL) { memcpy(out, src, sizeof(git_index_entry)); - if ((out->path = git_pool_strdup(pool, src->path)) == NULL) return -1; } @@ -1399,7 +1490,7 @@ static git_merge_diff *merge_diff_from_index_entries( git_merge_diff *conflict; git_pool *pool = &diff_list->pool; - if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL) + if ((conflict = git_pool_mallocz(pool, sizeof(git_merge_diff))) == NULL) return NULL; if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || @@ -1498,10 +1589,11 @@ git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo) diff_list->repo = repo; + git_pool_init(&diff_list->pool, 1); + if (git_vector_init(&diff_list->staged, 0, NULL) < 0 || git_vector_init(&diff_list->conflicts, 0, NULL) < 0 || - git_vector_init(&diff_list->resolved, 0, NULL) < 0 || - git_pool_init(&diff_list->pool, 1, 0) < 0) { + git_vector_init(&diff_list->resolved, 0, NULL) < 0) { git_merge_diff_list__free(diff_list); return NULL; } @@ -1597,7 +1689,45 @@ static int merge_index_insert_reuc( mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]); } -int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) +static int index_update_reuc(git_index *index, git_merge_diff_list *diff_list) +{ + int error; + size_t i; + git_merge_diff *conflict; + + /* Add each entry in the resolved conflict to the REUC independently, since + * the paths may differ due to renames. */ + git_vector_foreach(&diff_list->resolved, i, conflict) { + const git_index_entry *ancestor = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? + &conflict->ancestor_entry : NULL; + + const git_index_entry *ours = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? + &conflict->our_entry : NULL; + + const git_index_entry *theirs = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? + &conflict->their_entry : NULL; + + if (ancestor != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0) + return error; + + if (ours != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0) + return error; + + if (theirs != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0) + return error; + } + + return 0; +} + +static int index_from_diff_list(git_index **out, + git_merge_diff_list *diff_list, bool skip_reuc) { git_index *index; size_t i; @@ -1656,31 +1786,8 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) } } - /* Add each entry in the resolved conflict to the REUC independently, since - * the paths may differ due to renames. */ - git_vector_foreach(&diff_list->resolved, i, conflict) { - const git_index_entry *ancestor = - GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? - &conflict->ancestor_entry : NULL; - - const git_index_entry *ours = - GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? - &conflict->our_entry : NULL; - - const git_index_entry *theirs = - GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? - &conflict->their_entry : NULL; - - if (ancestor != NULL && - (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0) - goto on_error; - - if (ours != NULL && - (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0) - goto on_error; - - if (theirs != NULL && - (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0) + if (!skip_reuc) { + if ((error = index_update_reuc(index, diff_list)) < 0) goto on_error; } @@ -1689,7 +1796,6 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) on_error: git_index_free(index); - return error; } @@ -1757,16 +1863,24 @@ int git_merge__iterators( if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.file_favor, opts.file_flags)) < 0) goto done; - if (!resolved) + if (!resolved) { + if ((opts.tree_flags & GIT_MERGE_TREE_FAIL_ON_CONFLICT)) { + giterr_set(GITERR_MERGE, "merge conflicts exist"); + error = GIT_EMERGECONFLICT; + goto done; + } + git_vector_insert(&diff_list->conflicts, conflict); + } } + error = index_from_diff_list(out, diff_list, + (opts.tree_flags & GIT_MERGE_TREE_SKIP_REUC)); + +done: if (!given_opts || !given_opts->metric) git__free(opts.metric); - error = index_from_diff_list(out, diff_list); - -done: git_merge_diff_list__free(diff_list); git_iterator_free(empty_ancestor); git_iterator_free(empty_ours); diff --git a/src/merge_file.c b/src/merge_file.c index 6d89b089d..6d4738065 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -7,17 +7,23 @@ #include "common.h" #include "repository.h" -#include "merge_file.h" #include "posix.h" #include "fileops.h" #include "index.h" +#include "diff_xdiff.h" #include "git2/repository.h" #include "git2/object.h" #include "git2/index.h" +#include "git2/merge.h" #include "xdiff/xdiff.h" +/* only examine the first 8000 bytes for binaryness. + * https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197 + */ +#define GIT_MERGE_FILE_BINARY_SIZE 8000 + #define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0) GIT_INLINE(const char *) merge_file_best_path( @@ -100,7 +106,7 @@ static void merge_file_normalize_opts( } } -static int git_merge_file__from_inputs( +static int merge_file__xdiff( git_merge_file_result *out, const git_merge_file_input *ancestor, const git_merge_file_input *ours, @@ -189,6 +195,63 @@ done: return error; } +static bool merge_file__is_binary(const git_merge_file_input *file) +{ + size_t len = file ? file->size : 0; + + if (len > GIT_XDIFF_MAX_SIZE) + return true; + if (len > GIT_MERGE_FILE_BINARY_SIZE) + len = GIT_MERGE_FILE_BINARY_SIZE; + + return len ? (memchr(file->ptr, 0, len) != NULL) : false; +} + +static int merge_file__binary( + git_merge_file_result *out, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *given_opts) +{ + const git_merge_file_input *favored = NULL; + + memset(out, 0x0, sizeof(git_merge_file_result)); + + if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS) + favored = ours; + else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS) + favored = theirs; + else + goto done; + + if ((out->path = git__strdup(favored->path)) == NULL || + (out->ptr = git__malloc(favored->size)) == NULL) + goto done; + + memcpy((char *)out->ptr, favored->ptr, favored->size); + out->len = favored->size; + out->mode = favored->mode; + out->automergeable = 1; + +done: + return 0; +} + +static int merge_file__from_inputs( + git_merge_file_result *out, + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *given_opts) +{ + if (merge_file__is_binary(ancestor) || + merge_file__is_binary(ours) || + merge_file__is_binary(theirs)) + return merge_file__binary(out, ours, theirs, given_opts); + + return merge_file__xdiff(out, ancestor, ours, theirs, given_opts); +} + static git_merge_file_input *git_merge_file__normalize_inputs( git_merge_file_input *out, const git_merge_file_input *given) @@ -223,7 +286,7 @@ int git_merge_file( ours = git_merge_file__normalize_inputs(&inputs[1], ours); theirs = git_merge_file__normalize_inputs(&inputs[2], theirs); - return git_merge_file__from_inputs(out, ancestor, ours, theirs, options); + return merge_file__from_inputs(out, ancestor, ours, theirs, options); } int git_merge_file_from_index( @@ -234,8 +297,8 @@ int git_merge_file_from_index( const git_index_entry *theirs, const git_merge_file_options *options) { - git_merge_file_input inputs[3] = { {0} }, - *ancestor_input = NULL, *our_input = NULL, *their_input = NULL; + git_merge_file_input *ancestor_ptr = NULL, + ancestor_input = {0}, our_input = {0}, their_input = {0}; git_odb *odb = NULL; git_odb_object *odb_object[3] = { 0 }; int error = 0; @@ -249,27 +312,20 @@ int git_merge_file_from_index( if (ancestor) { if ((error = git_merge_file__input_from_index( - &inputs[0], &odb_object[0], odb, ancestor)) < 0) + &ancestor_input, &odb_object[0], odb, ancestor)) < 0) goto done; - ancestor_input = &inputs[0]; + ancestor_ptr = &ancestor_input; } if ((error = git_merge_file__input_from_index( - &inputs[1], &odb_object[1], odb, ours)) < 0) - goto done; - - our_input = &inputs[1]; - - if ((error = git_merge_file__input_from_index( - &inputs[2], &odb_object[2], odb, theirs)) < 0) + &our_input, &odb_object[1], odb, ours)) < 0 || + (error = git_merge_file__input_from_index( + &their_input, &odb_object[2], odb, theirs)) < 0) goto done; - their_input = &inputs[2]; - - if ((error = git_merge_file__from_inputs(out, - ancestor_input, our_input, their_input, options)) < 0) - goto done; + error = merge_file__from_inputs(out, + ancestor_ptr, &our_input, &their_input, options); done: git_odb_object_free(odb_object[0]); @@ -286,7 +342,5 @@ void git_merge_file_result_free(git_merge_file_result *result) return; git__free((char *)result->path); - - /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */ - free((char *)result->ptr); + git__free((char *)result->ptr); } diff --git a/src/merge_file.h b/src/merge_file.h deleted file mode 100644 index 263391ee3..000000000 --- a/src/merge_file.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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_filediff_h__ -#define INCLUDE_filediff_h__ - -#include "xdiff/xdiff.h" - -#include "git2/merge.h" - -#endif @@ -374,10 +374,14 @@ static int backend_sort_cmp(const void *a, const void *b) const backend_internal *backend_a = (const backend_internal *)(a); const backend_internal *backend_b = (const backend_internal *)(b); - if (backend_a->is_alternate == backend_b->is_alternate) - return (backend_b->priority - backend_a->priority); - - return backend_a->is_alternate ? 1 : -1; + if (backend_b->priority == backend_a->priority) { + if (backend_a->is_alternate) + return -1; + if (backend_b->is_alternate) + return 1; + return 0; + } + return (backend_b->priority - backend_a->priority); } int git_odb_new(git_odb **out) @@ -620,23 +624,18 @@ void git_odb_free(git_odb *db) GIT_REFCOUNT_DEC(db, odb_free); } -int git_odb_exists(git_odb *db, const git_oid *id) +static int odb_exists_1(git_odb *db, const git_oid *id, bool only_refreshed) { - git_odb_object *object; size_t i; bool found = false; - assert(db && id); - - if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { - git_odb_object_free(object); - return (int)true; - } - for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; + if (only_refreshed && !b->refresh) + continue; + if (b->exists != NULL) found = (bool)b->exists(b, id); } @@ -644,43 +643,45 @@ int git_odb_exists(git_odb *db, const git_oid *id) return (int)found; } -int git_odb_exists_prefix( - git_oid *out, git_odb *db, const git_oid *short_id, size_t len) +int git_odb_exists(git_odb *db, const git_oid *id) { - int error = GIT_ENOTFOUND, num_found = 0; - size_t i; - git_oid key = {{0}}, last_found = {{0}}, found; - - assert(db && short_id); + git_odb_object *object; - if (len < GIT_OID_MINPREFIXLEN) - return git_odb__error_ambiguous("prefix length too short"); - if (len > GIT_OID_HEXSZ) - len = GIT_OID_HEXSZ; + assert(db && id); - if (len == GIT_OID_HEXSZ) { - if (git_odb_exists(db, short_id)) { - if (out) - git_oid_cpy(out, short_id); - return 0; - } else { - return git_odb__error_notfound("no match for id prefix", short_id); - } + if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { + git_odb_object_free(object); + return (int)true; } - /* just copy valid part of short_id */ - memcpy(&key.id, short_id->id, (len + 1) / 2); - if (len & 1) - key.id[len / 2] &= 0xF0; + if (odb_exists_1(db, id, false)) + return 1; + + if (!git_odb_refresh(db)) + return odb_exists_1(db, id, true); + + /* Failed to refresh, hence not found */ + return 0; +} + +static int odb_exists_prefix_1(git_oid *out, git_odb *db, + const git_oid *key, size_t len, bool only_refreshed) +{ + size_t i; + int error = GIT_ENOTFOUND, num_found = 0; + git_oid last_found = {{0}}, found; for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; + if (only_refreshed && !b->refresh) + continue; + if (!b->exists_prefix) continue; - error = b->exists_prefix(&found, b, &key, len); + error = b->exists_prefix(&found, b, key, len); if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) continue; if (error) @@ -697,13 +698,53 @@ int git_odb_exists_prefix( } if (!num_found) - return git_odb__error_notfound("no match for id prefix", &key); + return GIT_ENOTFOUND; + if (out) git_oid_cpy(out, &last_found); return 0; } +int git_odb_exists_prefix( + git_oid *out, git_odb *db, const git_oid *short_id, size_t len) +{ + int error; + git_oid key = {{0}}; + + assert(db && short_id); + + if (len < GIT_OID_MINPREFIXLEN) + return git_odb__error_ambiguous("prefix length too short"); + if (len > GIT_OID_HEXSZ) + len = GIT_OID_HEXSZ; + + if (len == GIT_OID_HEXSZ) { + if (git_odb_exists(db, short_id)) { + if (out) + git_oid_cpy(out, short_id); + return 0; + } else { + return git_odb__error_notfound("no match for id prefix", short_id); + } + } + + /* just copy valid part of short_id */ + memcpy(&key.id, short_id->id, (len + 1) / 2); + if (len & 1) + key.id[len / 2] &= 0xF0; + + error = odb_exists_prefix_1(out, db, &key, len, false); + + if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) + error = odb_exists_prefix_1(out, db, &key, len, true); + + if (error == GIT_ENOTFOUND) + return git_odb__error_notfound("no match for id prefix", &key); + + return error; +} + int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) { int error; @@ -783,36 +824,38 @@ static int hardcoded_objects(git_rawobj *raw, const git_oid *id) } } -int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) +static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, + bool only_refreshed) { - size_t i, reads = 0; - int error; + size_t i; git_rawobj raw; git_odb_object *object; + bool found = false; - assert(out && db && id); - - *out = git_cache_get_raw(odb_cache(db), id); - if (*out != NULL) - return 0; - - error = hardcoded_objects(&raw, id); + if (!hardcoded_objects(&raw, id)) + found = true; - for (i = 0; i < db->backends.length && error < 0; ++i) { + for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; + if (only_refreshed && !b->refresh) + continue; + if (b->read != NULL) { - ++reads; - error = b->read(&raw.data, &raw.len, &raw.type, b, id); + int error = b->read(&raw.data, &raw.len, &raw.type, b, id); + if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND) + continue; + + if (error < 0) + return error; + + found = true; } } - if (error && error != GIT_PASSTHROUGH) { - if (!reads) - return git_odb__error_notfound("no match for id", id); - return error; - } + if (!found) + return GIT_ENOTFOUND; giterr_clear(); if ((object = odb_object__alloc(id, &raw)) == NULL) @@ -822,42 +865,48 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) return 0; } -int git_odb_read_prefix( - git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) +int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) +{ + int error; + + assert(out && db && id); + + *out = git_cache_get_raw(odb_cache(db), id); + if (*out != NULL) + return 0; + + error = odb_read_1(out, db, id, false); + + if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) + error = odb_read_1(out, db, id, true); + + if (error == GIT_ENOTFOUND) + return git_odb__error_notfound("no match for id", id); + + return error; +} + +static int read_prefix_1(git_odb_object **out, git_odb *db, + const git_oid *key, size_t len, bool only_refreshed) { size_t i; int error = GIT_ENOTFOUND; - git_oid key = {{0}}, found_full_oid = {{0}}; + git_oid found_full_oid = {{0}}; git_rawobj raw; void *data = NULL; bool found = false; git_odb_object *object; - assert(out && db); - - if (len < GIT_OID_MINPREFIXLEN) - return git_odb__error_ambiguous("prefix length too short"); - if (len > GIT_OID_HEXSZ) - len = GIT_OID_HEXSZ; - - if (len == GIT_OID_HEXSZ) { - *out = git_cache_get_raw(odb_cache(db), short_id); - if (*out != NULL) - return 0; - } - - /* just copy valid part of short_id */ - memcpy(&key.id, short_id->id, (len + 1) / 2); - if (len & 1) - key.id[len / 2] &= 0xF0; - for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; + if (only_refreshed && !b->refresh) + continue; + if (b->read_prefix != NULL) { git_oid full_oid; - error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len); + error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len); if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) continue; @@ -878,7 +927,7 @@ int git_odb_read_prefix( } if (!found) - return git_odb__error_notfound("no match for prefix", &key); + return GIT_ENOTFOUND; if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) return -1; @@ -887,6 +936,42 @@ int git_odb_read_prefix( return 0; } +int git_odb_read_prefix( + git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) +{ + git_oid key = {{0}}; + int error; + + assert(out && db); + + if (len < GIT_OID_MINPREFIXLEN) + return git_odb__error_ambiguous("prefix length too short"); + + if (len > GIT_OID_HEXSZ) + len = GIT_OID_HEXSZ; + + if (len == GIT_OID_HEXSZ) { + *out = git_cache_get_raw(odb_cache(db), short_id); + if (*out != NULL) + return 0; + } + + /* just copy valid part of short_id */ + memcpy(&key.id, short_id->id, (len + 1) / 2); + if (len & 1) + key.id[len / 2] &= 0xF0; + + error = read_prefix_1(out, db, &key, len, false); + + if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) + error = read_prefix_1(out, db, &key, len, true); + + if (error == GIT_ENOTFOUND) + return git_odb__error_notfound("no match for prefix", &key); + + return error; +} + int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload) { unsigned int i; diff --git a/src/odb_pack.c b/src/odb_pack.c index 735158d96..77d2c75b9 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -346,7 +346,7 @@ static int pack_backend__refresh(git_odb_backend *backend_) return error; } -static int pack_backend__read_header_internal( +static int pack_backend__read_header( size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid) { @@ -361,24 +361,7 @@ static int pack_backend__read_header_internal( return git_packfile_resolve_header(len_p, type_p, e.p, e.offset); } -static int pack_backend__read_header( - size_t *len_p, git_otype *type_p, - struct git_odb_backend *backend, const git_oid *oid) -{ - int error; - - error = pack_backend__read_header_internal(len_p, type_p, backend, oid); - - if (error != GIT_ENOTFOUND) - return error; - - if ((error = pack_backend__refresh(backend)) < 0) - return error; - - return pack_backend__read_header_internal(len_p, type_p, backend, oid); -} - -static int pack_backend__read_internal( +static int pack_backend__read( void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { @@ -397,24 +380,7 @@ static int pack_backend__read_internal( return 0; } -static int pack_backend__read( - void **buffer_p, size_t *len_p, git_otype *type_p, - git_odb_backend *backend, const git_oid *oid) -{ - int error; - - error = pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid); - - if (error != GIT_ENOTFOUND) - return error; - - if ((error = pack_backend__refresh(backend)) < 0) - return error; - - return pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid); -} - -static int pack_backend__read_prefix_internal( +static int pack_backend__read_prefix( git_oid *out_oid, void **buffer_p, size_t *len_p, @@ -451,45 +417,9 @@ static int pack_backend__read_prefix_internal( return error; } -static int pack_backend__read_prefix( - git_oid *out_oid, - void **buffer_p, - size_t *len_p, - git_otype *type_p, - git_odb_backend *backend, - const git_oid *short_oid, - size_t len) -{ - int error; - - error = pack_backend__read_prefix_internal( - out_oid, buffer_p, len_p, type_p, backend, short_oid, len); - - if (error != GIT_ENOTFOUND) - return error; - - if ((error = pack_backend__refresh(backend)) < 0) - return error; - - return pack_backend__read_prefix_internal( - out_oid, buffer_p, len_p, type_p, backend, short_oid, len); -} - static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) { struct git_pack_entry e; - int error; - - error = pack_entry_find(&e, (struct pack_backend *)backend, oid); - - if (error != GIT_ENOTFOUND) - return error == 0; - - if ((error = pack_backend__refresh(backend)) < 0) { - giterr_clear(); - return (int)false; - } - return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0; } @@ -501,12 +431,7 @@ static int pack_backend__exists_prefix( struct git_pack_entry e = {0}; error = pack_entry_find_prefix(&e, pb, short_id, len); - - if (error == GIT_ENOTFOUND && !(error = pack_backend__refresh(backend))) - error = pack_entry_find_prefix(&e, pb, short_id, len); - git_oid_cpy(out, &e.sha1); - return error; } @@ -674,7 +599,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) git_path_isdir(git_buf_cstr(&path))) { backend->pack_folder = git_buf_detach(&path); - error = pack_backend__refresh((git_odb_backend *)backend); } diff --git a/src/pack-objects.c b/src/pack-objects.c index c4c061a3a..fd181fc5e 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -135,8 +135,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo) if (!pb->walk_objects) goto on_error; - if (git_pool_init(&pb->object_pool, sizeof(git_walk_object), 0) < 0) - goto on_error; + git_pool_init(&pb->object_pool, sizeof(git_walk_object)); pb->repo = repo; pb->nr_threads = 1; /* do not spawn any thread by default */ diff --git a/src/pathspec.c b/src/pathspec.c index 9304da705..5bb69ec4b 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -237,9 +237,9 @@ int git_pathspec__init(git_pathspec *ps, const git_strarray *paths) memset(ps, 0, sizeof(*ps)); ps->prefix = git_pathspec_prefix(paths); + git_pool_init(&ps->pool, 1); - if ((error = git_pool_init(&ps->pool, 1, 0)) < 0 || - (error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0) + if ((error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0) git_pathspec__clear(ps); return error; @@ -312,15 +312,11 @@ static git_pathspec_match_list *pathspec_match_alloc( git_pathspec *ps, int datatype) { git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list)); - - if (m != NULL && git_pool_init(&m->pool, 1, 0) < 0) { - pathspec_match_free(m); - m = NULL; - } - if (!m) return NULL; + git_pool_init(&m->pool, 1); + /* need to keep reference to pathspec and increment refcount because * failures array stores pointers to the pattern strings of the * pathspec that had no matches diff --git a/src/pool.c b/src/pool.c index c93d78182..aab63beee 100644 --- a/src/pool.c +++ b/src/pool.c @@ -8,67 +8,49 @@ struct git_pool_page { git_pool_page *next; uint32_t size; uint32_t avail; - GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8); + char data[GIT_FLEX_ARRAY]; }; -struct pool_freelist { - struct pool_freelist *next; -}; +static void *pool_alloc_page(git_pool *pool, uint32_t size); -#define GIT_POOL_MIN_USABLE 4 -#define GIT_POOL_MIN_PAGESZ 2 * sizeof(void*) +uint32_t git_pool__system_page_size(void) +{ + static uint32_t size = 0; -static void *pool_alloc_page(git_pool *pool, uint32_t size); -static void pool_insert_page(git_pool *pool, git_pool_page *page); + if (!size) { + size_t page_size; + if (git__page_size(&page_size) < 0) + page_size = 4096; + /* allow space for malloc overhead */ + size = page_size - (2 * sizeof(void *)) - sizeof(git_pool_page); + } -int git_pool_init( - git_pool *pool, uint32_t item_size, uint32_t items_per_page) + return size; +} + +void git_pool_init(git_pool *pool, uint32_t item_size) { + const uint32_t align_size = sizeof(void *) - 1; assert(pool); - if (!item_size) - item_size = 1; - /* round up item_size for decent object alignment */ - if (item_size > 4) - item_size = (item_size + 7) & ~7; - else if (item_size == 3) - item_size = 4; - - if (!items_per_page) - items_per_page = git_pool__suggest_items_per_page(item_size); - if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ) - items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size; + if (item_size > 1) + item_size = (item_size + align_size) & ~align_size; memset(pool, 0, sizeof(git_pool)); pool->item_size = item_size; - pool->page_size = item_size * items_per_page; - - return 0; + pool->page_size = git_pool__system_page_size(); } void git_pool_clear(git_pool *pool) { git_pool_page *scan, *next; - for (scan = pool->open; scan != NULL; scan = next) { + for (scan = pool->pages; scan != NULL; scan = next) { next = scan->next; git__free(scan); } - pool->open = NULL; - for (scan = pool->full; scan != NULL; scan = next) { - next = scan->next; - git__free(scan); - } - pool->full = NULL; - - pool->free_list = NULL; - - pool->items = 0; - - pool->has_string_alloc = 0; - pool->has_multi_item_alloc = 0; - pool->has_large_page_alloc = 0; + pool->pages = NULL; } void git_pool_swap(git_pool *a, git_pool *b) @@ -83,110 +65,51 @@ void git_pool_swap(git_pool *a, git_pool *b) memcpy(b, &temp, sizeof(temp)); } -static void pool_insert_page(git_pool *pool, git_pool_page *page) -{ - git_pool_page *scan; - - /* If there are no open pages or this page has the most open space, - * insert it at the beginning of the list. This is the common case. - */ - if (pool->open == NULL || pool->open->avail < page->avail) { - page->next = pool->open; - pool->open = page; - return; - } - - /* Otherwise insert into sorted position. */ - for (scan = pool->open; - scan->next && scan->next->avail > page->avail; - scan = scan->next); - page->next = scan->next; - scan->next = page; -} - static void *pool_alloc_page(git_pool *pool, uint32_t size) { git_pool_page *page; - uint32_t new_page_size; + const uint32_t new_page_size = (size <= pool->page_size) ? pool->page_size : size; size_t alloc_size; - if (size <= pool->page_size) - new_page_size = pool->page_size; - else { - new_page_size = size; - pool->has_large_page_alloc = 1; - } - if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) || - !(page = git__calloc(1, alloc_size))) + !(page = git__malloc(alloc_size))) return NULL; - page->size = new_page_size; + page->size = new_page_size; page->avail = new_page_size - size; + page->next = pool->pages; - if (page->avail > 0) - pool_insert_page(pool, page); - else { - page->next = pool->full; - pool->full = page; - } - - pool->items++; + pool->pages = page; return page->data; } -GIT_INLINE(void) pool_remove_page( - git_pool *pool, git_pool_page *page, git_pool_page *prev) +static void *pool_alloc(git_pool *pool, uint32_t size) { - if (prev == NULL) - pool->open = page->next; - else - prev->next = page->next; -} - -void *git_pool_malloc(git_pool *pool, uint32_t items) -{ - git_pool_page *scan = pool->open, *prev; - uint32_t size = ((items * pool->item_size) + 7) & ~7; + git_pool_page *page = pool->pages; void *ptr = NULL; - pool->has_string_alloc = 0; - if (items > 1) - pool->has_multi_item_alloc = 1; - else if (pool->free_list != NULL) { - ptr = pool->free_list; - pool->free_list = ((struct pool_freelist *)pool->free_list)->next; - return ptr; - } - - /* just add a block if there is no open one to accommodate this */ - if (size >= pool->page_size || !scan || scan->avail < size) + if (!page || page->avail < size) return pool_alloc_page(pool, size); - pool->items++; - - /* find smallest block in free list with space */ - for (scan = pool->open, prev = NULL; - scan->next && scan->next->avail >= size; - prev = scan, scan = scan->next); + ptr = &page->data[page->size - page->avail]; + page->avail -= size; - /* allocate space from the block */ - ptr = &scan->data[scan->size - scan->avail]; - scan->avail -= size; + return ptr; +} - /* move to full list if there is almost no space left */ - if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) { - pool_remove_page(pool, scan, prev); - scan->next = pool->full; - pool->full = scan; - } - /* reorder list if block is now smaller than the one after it */ - else if (scan->next != NULL && scan->next->avail > scan->avail) { - pool_remove_page(pool, scan, prev); - pool_insert_page(pool, scan); - } +void *git_pool_malloc(git_pool *pool, uint32_t items) +{ + const uint32_t size = items * pool->item_size; + return pool_alloc(pool, size); +} +void *git_pool_mallocz(git_pool *pool, uint32_t items) +{ + const uint32_t size = items * pool->item_size; + void *ptr = pool_alloc(pool, size); + if (ptr) + memset(ptr, 0x0, size); return ptr; } @@ -204,15 +127,12 @@ char *git_pool_strndup(git_pool *pool, const char *str, size_t n) ptr[n] = '\0'; } - pool->has_string_alloc = 1; - return ptr; } char *git_pool_strdup(git_pool *pool, const char *str) { assert(pool && str && pool->item_size == sizeof(char)); - return git_pool_strndup(pool, str, strlen(str)); } @@ -238,88 +158,23 @@ char *git_pool_strcat(git_pool *pool, const char *a, const char *b) memcpy(((char *)ptr) + len_a, b, len_b); *(((char *)ptr) + len_a + len_b) = '\0'; } - pool->has_string_alloc = 1; - return ptr; } -void git_pool_free(git_pool *pool, void *ptr) -{ - struct pool_freelist *item = ptr; - - assert(pool && pool->item_size >= sizeof(void*)); - - if (item) { - item->next = pool->free_list; - pool->free_list = item; - } -} - -void git_pool_free_array(git_pool *pool, size_t count, void **ptrs) -{ - struct pool_freelist **items = (struct pool_freelist **)ptrs; - size_t i; - - assert(pool && ptrs && pool->item_size >= sizeof(void*)); - - if (!count) - return; - - for (i = count - 1; i > 0; --i) - items[i]->next = items[i - 1]; - - items[i]->next = pool->free_list; - pool->free_list = items[count - 1]; -} - uint32_t git_pool__open_pages(git_pool *pool) { uint32_t ct = 0; git_pool_page *scan; - for (scan = pool->open; scan != NULL; scan = scan->next) ct++; - return ct; -} - -uint32_t git_pool__full_pages(git_pool *pool) -{ - uint32_t ct = 0; - git_pool_page *scan; - for (scan = pool->full; scan != NULL; scan = scan->next) ct++; + for (scan = pool->pages; scan != NULL; scan = scan->next) ct++; return ct; } bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) { git_pool_page *scan; - for (scan = pool->open; scan != NULL; scan = scan->next) - if ((void *)scan->data <= ptr && - (void *)(((char *)scan->data) + scan->size) > ptr) - return true; - for (scan = pool->full; scan != NULL; scan = scan->next) + for (scan = pool->pages; scan != NULL; scan = scan->next) if ((void *)scan->data <= ptr && (void *)(((char *)scan->data) + scan->size) > ptr) return true; return false; } - -uint32_t git_pool__system_page_size(void) -{ - static uint32_t size = 0; - - if (!size) { - size_t page_size; - if (git__page_size(&page_size) < 0) - page_size = 4096; - size = page_size - 2 * sizeof(void *); /* allow space for malloc overhead */ - } - - return size; -} - -uint32_t git_pool__suggest_items_per_page(uint32_t item_size) -{ - uint32_t page_bytes = - git_pool__system_page_size() - sizeof(git_pool_page); - return page_bytes / item_size; -} - diff --git a/src/pool.h b/src/pool.h index b0007f315..d16bd349a 100644 --- a/src/pool.h +++ b/src/pool.h @@ -28,37 +28,28 @@ typedef struct git_pool_page git_pool_page; * For examples of how to set up a `git_pool` see `git_pool_init`. */ typedef struct { - git_pool_page *open; /* pages with space left */ - git_pool_page *full; /* pages with no space left */ - void *free_list; /* optional: list of freed blocks */ + git_pool_page *pages; /* allocated pages */ uint32_t item_size; /* size of single alloc unit in bytes */ uint32_t page_size; /* size of page in bytes */ - uint32_t items; - unsigned has_string_alloc : 1; /* was the strdup function used */ - unsigned has_multi_item_alloc : 1; /* was items ever > 1 in malloc */ - unsigned has_large_page_alloc : 1; /* are any pages > page_size */ } git_pool; -#define GIT_POOL_INIT_STRINGPOOL { 0, 0, 0, 1, 4000, 0, 0, 0, 0 } - /** * Initialize a pool. * * To allocation strings, use like this: * - * git_pool_init(&string_pool, 1, 0); + * git_pool_init(&string_pool, 1); * my_string = git_pool_strdup(&string_pool, your_string); * * To allocate items of fixed size, use like this: * - * git_pool_init(&pool, sizeof(item), 0); + * git_pool_init(&pool, sizeof(item)); * my_item = git_pool_malloc(&pool, 1); * * Of course, you can use this in other ways, but those are the * two most common patterns. */ -extern int git_pool_init( - git_pool *pool, uint32_t item_size, uint32_t items_per_page); +extern void git_pool_init(git_pool *pool, uint32_t item_size); /** * Free all items in pool @@ -74,17 +65,7 @@ extern void git_pool_swap(git_pool *a, git_pool *b); * Allocate space for one or more items from a pool. */ extern void *git_pool_malloc(git_pool *pool, uint32_t items); - -/** - * Allocate space and zero it out. - */ -GIT_INLINE(void *) git_pool_mallocz(git_pool *pool, uint32_t items) -{ - void *ptr = git_pool_malloc(pool, items); - if (ptr) - memset(ptr, 0, (size_t)items * (size_t)pool->item_size); - return ptr; -} +extern void *git_pool_mallocz(git_pool *pool, uint32_t items); /** * Allocate space and duplicate string data into it. @@ -114,35 +95,10 @@ extern char *git_pool_strdup_safe(git_pool *pool, const char *str); */ extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b); -/** - * Push a block back onto the free list for the pool. - * - * This is allowed only if the item_size is >= sizeof(void*). - * - * In some cases, it is helpful to "release" an allocated block - * for reuse. Pools don't support a general purpose free, but - * they will keep a simple free blocks linked list provided the - * native block size is large enough to hold a void pointer - */ -extern void git_pool_free(git_pool *pool, void *ptr); - -/** - * Push an array of pool allocated blocks efficiently onto the free list. - * - * This has the same constraints as `git_pool_free()` above. - */ -extern void git_pool_free_array(git_pool *pool, size_t count, void **ptrs); - /* * Misc utilities */ - extern uint32_t git_pool__open_pages(git_pool *pool); - -extern uint32_t git_pool__full_pages(git_pool *pool); - extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr); -extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size); - #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 921f7862b..85b5034d6 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -626,8 +626,9 @@ static int refdb_fs_backend__iterator( iter = git__calloc(1, sizeof(refdb_fs_iter)); GITERR_CHECK_ALLOC(iter); - if (git_pool_init(&iter->pool, 1, 0) < 0 || - git_vector_init(&iter->loose, 8, NULL) < 0) + git_pool_init(&iter->pool, 1); + + if (git_vector_init(&iter->loose, 8, NULL) < 0) goto fail; if (glob != NULL && @@ -732,8 +733,11 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char * error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE); + if (error == GIT_EDIRECTORY) + giterr_set(GITERR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name); + git_buf_free(&ref_path); - return error; + return error; } static int loose_commit(git_filebuf *file, const git_reference *ref) @@ -1463,7 +1467,7 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) entry = git__calloc(1, sizeof(git_reflog_entry)); GITERR_CHECK_ALLOC(entry); - entry->committer = git__malloc(sizeof(git_signature)); + entry->committer = git__calloc(1, sizeof(git_signature)); GITERR_CHECK_ALLOC(entry->committer); if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0) @@ -1784,10 +1788,17 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co /* If the new branch matches part of the namespace of a previously deleted branch, * there maybe an obsolete/unused directory (or directory hierarchy) in the way. */ - if (git_path_isdir(git_buf_cstr(&path)) && - (git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) { - error = -1; - goto cleanup; + if (git_path_isdir(git_buf_cstr(&path))) { + if ((git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) + error = -1; + else if (git_path_isdir(git_buf_cstr(&path))) { + giterr_set(GITERR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder", + ref->name); + error = GIT_EDIRECTORY; + } + + if (error != 0) + goto cleanup; } error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE); diff --git a/src/repository.c b/src/repository.c index 77145cfc8..c61d0e4f0 100644 --- a/src/repository.c +++ b/src/repository.c @@ -585,7 +585,8 @@ static int load_config( git_repository *repo, const char *global_config_path, const char *xdg_config_path, - const char *system_config_path) + const char *system_config_path, + const char *programdata_path) { int error; git_buf config_path = GIT_BUF_INIT; @@ -626,6 +627,12 @@ static int load_config( error != GIT_ENOTFOUND) goto on_error; + if (programdata_path != NULL && + (error = git_config_add_file_ondisk( + cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, 0)) < 0 && + error != GIT_ENOTFOUND) + goto on_error; + giterr_clear(); /* clear any lingering ENOTFOUND errors */ *out = cfg; @@ -651,11 +658,13 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) git_buf global_buf = GIT_BUF_INIT; git_buf xdg_buf = GIT_BUF_INIT; git_buf system_buf = GIT_BUF_INIT; + git_buf programdata_buf = GIT_BUF_INIT; git_config *config; git_config_find_global(&global_buf); git_config_find_xdg(&xdg_buf); git_config_find_system(&system_buf); + git_config_find_programdata(&programdata_buf); /* If there is no global file, open a backend for it anyway */ if (git_buf_len(&global_buf) == 0) @@ -665,7 +674,8 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) &config, repo, path_unless_empty(&global_buf), path_unless_empty(&xdg_buf), - path_unless_empty(&system_buf)); + path_unless_empty(&system_buf), + path_unless_empty(&programdata_buf)); if (!error) { GIT_REFCOUNT_OWN(config, repo); @@ -679,6 +689,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) git_buf_free(&global_buf); git_buf_free(&xdg_buf); git_buf_free(&system_buf); + git_buf_free(&programdata_buf); } *out = repo->_config; diff --git a/src/revwalk.c b/src/revwalk.c index dcdd97915..89279ed1f 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -535,12 +535,10 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) walk->commits = git_oidmap_alloc(); GITERR_CHECK_ALLOC(walk->commits); - if (git_pqueue_init( - &walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0 || - git_pool_init(&walk->commit_pool, 1, - git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0) + if (git_pqueue_init(&walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0) return -1; + git_pool_init(&walk->commit_pool, COMMIT_ALLOC); walk->get_next = &revwalk_next_unsorted; walk->enqueue = &revwalk_enqueue_unsorted; diff --git a/src/settings.c b/src/settings.c index 22e55953a..91bdfd520 100644 --- a/src/settings.c +++ b/src/settings.c @@ -60,6 +60,13 @@ static int config_level_to_sysdir(int config_level) return val; } +extern char *git__user_agent; + +const char *git_libgit2__user_agent() +{ + return git__user_agent; +} + int git_libgit2_opts(int key, ...) { int error = 0; @@ -156,6 +163,15 @@ int git_libgit2_opts(int key, ...) error = -1; #endif break; + case GIT_OPT_SET_USER_AGENT: + git__free(git__user_agent); + git__user_agent = git__strdup(va_arg(ap, const char *)); + if (!git__user_agent) { + giterr_set_oom(); + error = -1; + } + + break; } va_end(ap); diff --git a/src/signature.c b/src/signature.c index 818cd300e..109476efe 100644 --- a/src/signature.c +++ b/src/signature.c @@ -34,13 +34,27 @@ static bool contains_angle_brackets(const char *input) return strchr(input, '<') != NULL || strchr(input, '>') != NULL; } +static bool is_crud(unsigned char c) +{ + return c <= 32 || + c == '.' || + c == ',' || + c == ':' || + c == ';' || + c == '<' || + c == '>' || + c == '"' || + c == '\\' || + c == '\''; +} + static char *extract_trimmed(const char *ptr, size_t len) { - while (len && git__isspace(ptr[0])) { + while (len && is_crud((unsigned char)ptr[0])) { ptr++; len--; } - while (len && git__isspace(ptr[len - 1])) { + while (len && is_crud((unsigned char)ptr[len - 1])) { len--; } diff --git a/src/sortedcache.c b/src/sortedcache.c index 115175724..5c2a167a7 100644 --- a/src/sortedcache.c +++ b/src/sortedcache.c @@ -20,8 +20,9 @@ int git_sortedcache_new( sc = git__calloc(1, alloclen); GITERR_CHECK_ALLOC(sc); - if (git_pool_init(&sc->pool, 1, 0) < 0 || - git_vector_init(&sc->items, 4, item_cmp) < 0 || + git_pool_init(&sc->pool, 1); + + if (git_vector_init(&sc->items, 4, item_cmp) < 0 || git_strmap_alloc(&sc->map) < 0) goto fail; diff --git a/src/stream.h b/src/stream.h index 43fcc3045..4692c7115 100644 --- a/src/stream.h +++ b/src/stream.h @@ -62,6 +62,9 @@ GIT_INLINE(int) git_stream_close(git_stream *st) GIT_INLINE(void) git_stream_free(git_stream *st) { + if (!st) + return; + st->free(st); } diff --git a/src/submodule.c b/src/submodule.c index 3fd338843..1148f8790 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1423,7 +1423,6 @@ static int submodule_update_head(git_submodule *submodule) return 0; } - int git_submodule_reload(git_submodule *sm, int force) { int error = 0; @@ -1433,35 +1432,30 @@ int git_submodule_reload(git_submodule *sm, int force) assert(sm); - /* refresh index data */ - if ((error = submodule_update_index(sm)) < 0) - return error; - - /* refresh HEAD tree data */ - if ((error = submodule_update_head(sm)) < 0) - return error; + if (!git_repository_is_bare(sm->repo)) { + /* refresh config data */ + mods = gitmodules_snapshot(sm->repo); + if (mods != NULL) { + error = submodule_read_config(sm, mods); + git_config_free(mods); - /* done if bare */ - if (git_repository_is_bare(sm->repo)) - return error; + if (error < 0) + return error; + } - /* refresh config data */ - mods = gitmodules_snapshot(sm->repo); - if (mods != NULL) { - error = submodule_read_config(sm, mods); - git_config_free(mods); + /* refresh wd data */ + sm->flags &= + ~(GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS__WD_OID_VALID | + GIT_SUBMODULE_STATUS__WD_FLAGS); - if (error < 0) { - return error; - } + error = submodule_load_from_wd_lite(sm); } - /* refresh wd data */ - sm->flags &= - ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID | - GIT_SUBMODULE_STATUS__WD_FLAGS); + if (error == 0 && (error = submodule_update_index(sm)) == 0) + error = submodule_update_head(sm); - return submodule_load_from_wd_lite(sm); + return error; } static void submodule_copy_oid_maybe( diff --git a/src/sysdir.c b/src/sysdir.c index 2795de491..bf53d830f 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -15,6 +15,16 @@ #include "win32/findfile.h" #endif +static int git_sysdir_guess_programdata_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_programdata_dirs(out); +#else + git_buf_clear(out); + return 0; +#endif +} + static int git_sysdir_guess_system_dirs(git_buf *out) { #ifdef GIT_WIN32 @@ -76,12 +86,13 @@ static int git_sysdir_guess_template_dirs(git_buf *out) typedef int (*git_sysdir_guess_cb)(git_buf *out); static git_buf git_sysdir__dirs[GIT_SYSDIR__MAX] = - { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; + { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; static git_sysdir_guess_cb git_sysdir__dir_guess[GIT_SYSDIR__MAX] = { git_sysdir_guess_system_dirs, git_sysdir_guess_global_dirs, git_sysdir_guess_xdg_dirs, + git_sysdir_guess_programdata_dirs, git_sysdir_guess_template_dirs, }; @@ -258,6 +269,12 @@ int git_sysdir_find_xdg_file(git_buf *path, const char *filename) path, filename, GIT_SYSDIR_XDG, "global/xdg"); } +int git_sysdir_find_programdata_file(git_buf *path, const char *filename) +{ + return git_sysdir_find_in_dirlist( + path, filename, GIT_SYSDIR_PROGRAMDATA, "ProgramData"); +} + int git_sysdir_find_template_dir(git_buf *path) { return git_sysdir_find_in_dirlist( diff --git a/src/sysdir.h b/src/sysdir.h index f1bbf0bae..12874fc85 100644 --- a/src/sysdir.h +++ b/src/sysdir.h @@ -39,6 +39,15 @@ extern int git_sysdir_find_xdg_file(git_buf *path, const char *filename); extern int git_sysdir_find_system_file(git_buf *path, const char *filename); /** + * Find a "ProgramData" file (i.e. one in %PROGRAMDATA%) + * + * @param path buffer to write the full path into + * @param filename name of file to find in the ProgramData directory + * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error + */ +extern int git_sysdir_find_programdata_file(git_buf *path, const char *filename); + +/** * Find template directory. * * @param path buffer to write the full path into @@ -50,8 +59,9 @@ typedef enum { GIT_SYSDIR_SYSTEM = 0, GIT_SYSDIR_GLOBAL = 1, GIT_SYSDIR_XDG = 2, - GIT_SYSDIR_TEMPLATE = 3, - GIT_SYSDIR__MAX = 4, + GIT_SYSDIR_PROGRAMDATA = 3, + GIT_SYSDIR_TEMPLATE = 4, + GIT_SYSDIR__MAX = 5, } git_sysdir_t; /** diff --git a/src/tls_stream.c b/src/tls_stream.c index 39a8ce343..83e2d064a 100644 --- a/src/tls_stream.c +++ b/src/tls_stream.c @@ -11,8 +11,21 @@ #include "openssl_stream.h" #include "stransport_stream.h" +static git_stream_cb tls_ctor; + +int git_stream_register_tls(git_stream_cb ctor) +{ + tls_ctor = ctor; + + return 0; +} + int git_tls_stream_new(git_stream **out, const char *host, const char *port) { + + if (tls_ctor) + return tls_ctor(out, host, port); + #ifdef GIT_SECURE_TRANSPORT return git_stransport_stream_new(out, host, port); #elif defined(GIT_OPENSSL) diff --git a/src/transaction.c b/src/transaction.c index 92e134e5b..2c8a1e8bd 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -77,8 +77,7 @@ int git_transaction_new(git_transaction **out, git_repository *repo) assert(out && repo); - if ((error = git_pool_init(&pool, 1, 0)) < 0) - return error; + git_pool_init(&pool, 1); tx = git_pool_mallocz(&pool, sizeof(git_transaction)); if (!tx) { diff --git a/src/transports/git.c b/src/transports/git.c index 52de92d09..6c6acf9c5 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -130,11 +130,14 @@ static int git_proto_stream_write( static void git_proto_stream_free(git_smart_subtransport_stream *stream) { - git_proto_stream *s = (git_proto_stream *)stream; - git_subtransport *t = OWNING_SUBTRANSPORT(s); - int ret; + git_proto_stream *s; + git_subtransport *t; + + if (!stream) + return; - GIT_UNUSED(ret); + s = (git_proto_stream *)stream; + t = OWNING_SUBTRANSPORT(s); t->current_stream = NULL; diff --git a/src/transports/http.c b/src/transports/http.c index e5f2b9f28..88b124bf7 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -10,6 +10,7 @@ #include "http_parser.h" #include "buffer.h" #include "netops.h" +#include "global.h" #include "remote.h" #include "smart.h" #include "auth.h" @@ -186,6 +187,16 @@ static int apply_credentials(git_buf *buf, http_subtransport *t) return context->next_token(buf, context, cred); } +static const char *user_agent(void) +{ + const char *custom = git_libgit2__user_agent(); + + if (custom) + return custom; + + return "libgit2 " LIBGIT2_VERSION; +} + static int gen_request( git_buf *buf, http_stream *s, @@ -197,7 +208,7 @@ static int gen_request( git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url); - git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n"); + git_buf_printf(buf, "User-Agent: git/1.0 (%s)\r\n", user_agent()); git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host); if (s->chunked || content_length > 0) { diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index b364e906e..77d939bd3 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -15,6 +15,7 @@ #include "smart.h" #include "remote.h" #include "repository.h" +#include "global.h" #include <wincrypt.h> #include <winhttp.h> @@ -567,12 +568,28 @@ static int winhttp_close_connection(winhttp_subtransport *t) return ret; } +static int user_agent(git_buf *ua) +{ + const char *custom = git_libgit2__user_agent(); + + git_buf_clear(ua); + git_buf_PUTS(ua, "git/1.0 ("); + + if (custom) + git_buf_puts(ua, custom); + else + git_buf_PUTS(ua, "libgit2 " LIBGIT2_VERSION); + + return git_buf_putc(ua, ')'); +} + static int winhttp_connect( winhttp_subtransport *t) { - wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")"; wchar_t *wide_host; int32_t port; + wchar_t *wide_ua; + git_buf ua = GIT_BUF_INIT; int error = -1; int default_timeout = TIMEOUT_INFINITE; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; @@ -590,9 +607,23 @@ static int winhttp_connect( return -1; } + if ((error = user_agent(&ua)) < 0) { + git__free(wide_host); + return error; + } + + if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) { + giterr_set(GITERR_OS, "Unable to convert host to wide characters"); + git__free(wide_host); + git_buf_free(&ua); + return -1; + } + + git_buf_free(&ua); + /* Establish session */ t->session = WinHttpOpen( - ua, + wide_ua, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, @@ -628,6 +659,7 @@ on_error: winhttp_close_connection(t); git__free(wide_host); + git__free(wide_ua); return error; } diff --git a/src/win32/findfile.c b/src/win32/findfile.c index de27dd060..58c22279e 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -215,3 +215,13 @@ int git_win32__find_xdg_dirs(git_buf *out) return win32_find_existing_dirs(out, global_tmpls); } + +int git_win32__find_programdata_dirs(git_buf *out) +{ + static const wchar_t *programdata_tmpls[2] = { + L"%PROGRAMDATA%\\Git", + NULL, + }; + + return win32_find_existing_dirs(out, programdata_tmpls); +} diff --git a/src/win32/findfile.h b/src/win32/findfile.h index a50319b9a..3d5fff439 100644 --- a/src/win32/findfile.h +++ b/src/win32/findfile.h @@ -11,6 +11,7 @@ extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath); extern int git_win32__find_global_dirs(git_buf *out); extern int git_win32__find_xdg_dirs(git_buf *out); +extern int git_win32__find_programdata_dirs(git_buf *out); #endif diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h index e2f1e892b..f08f72e16 100644 --- a/src/xdiff/xdiff.h +++ b/src/xdiff/xdiff.h @@ -20,6 +20,8 @@ * */ +#include "../util.h" + #if !defined(XDIFF_H) #define XDIFF_H @@ -106,9 +108,9 @@ typedef struct s_bdiffparam { } bdiffparam_t; -#define xdl_malloc(x) malloc(x) -#define xdl_free(ptr) free(ptr) -#define xdl_realloc(ptr,x) realloc(ptr,x) +#define xdl_malloc(x) git__malloc(x) +#define xdl_free(ptr) git__free(ptr) +#define xdl_realloc(ptr,x) git__realloc(ptr,x) void *xdl_mmfile_first(mmfile_t *mmf, long *size); long xdl_mmfile_size(mmfile_t *mmf); diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c index 0620e5fff..f4d01b48c 100644 --- a/src/xdiff/xdiffi.c +++ b/src/xdiff/xdiffi.c @@ -21,7 +21,8 @@ */ #include "xinclude.h" - +#include "common.h" +#include "integer.h" #define XDL_MAX_COST_MIN 256 @@ -323,7 +324,7 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *xe) { - long ndiags; + size_t ndiags, allocsize; long *kvd, *kvdf, *kvdb; xdalgoenv_t xenv; diffdata_t dd1, dd2; @@ -343,9 +344,12 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, * Allocate and setup K vectors to be used by the differential algorithm. * One is to store the forward path and one to store the backward path. */ - ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; - if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) { + GITERR_CHECK_ALLOC_ADD3(&ndiags, xe->xdf1.nreff, xe->xdf2.nreff, 3); + GITERR_CHECK_ALLOC_MULTIPLY(&allocsize, ndiags, 2); + GITERR_CHECK_ALLOC_ADD(&allocsize, allocsize, 2); + GITERR_CHECK_ALLOC_MULTIPLY(&allocsize, allocsize, sizeof(long)); + if (!(kvd = (long *) xdl_malloc(allocsize))) { xdl_free_env(xe); return -1; } diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c index 500d8112a..0c2edb89c 100644 --- a/src/xdiff/xhistogram.c +++ b/src/xdiff/xhistogram.c @@ -44,6 +44,7 @@ #include "xinclude.h" #include "xtypes.h" #include "xdiff.h" +#include "common.h" #define MAX_PTR UINT_MAX #define MAX_CNT UINT_MAX @@ -271,7 +272,7 @@ static int histogram_diff( { struct histindex index; struct region lcs; - unsigned int sz; + size_t sz; int result = -1; if (count1 <= 0 && count2 <= 0) @@ -302,7 +303,8 @@ static int histogram_diff( index.table_bits = xdl_hashbits(count1); sz = index.records_size = 1 << index.table_bits; - sz *= sizeof(struct record *); + GITERR_CHECK_ALLOC_MULTIPLY(&sz, sz, sizeof(struct record *)); + if (!(index.records = (struct record **) xdl_malloc(sz))) goto cleanup; memset(index.records, 0, sz); diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c index b11e59817..7b7e0e2d3 100644 --- a/src/xdiff/xmerge.c +++ b/src/xdiff/xmerge.c @@ -21,6 +21,7 @@ */ #include "xinclude.h" +#include "common.h" typedef struct s_xdmerge { struct s_xdmerge *next; @@ -109,59 +110,74 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, return 0; } -static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest) { xrecord_t **recs; - int size = 0; + size_t size = 0; + + *out = 0; recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i; if (count < 1) return 0; - for (i = 0; i < count; size += recs[i++]->size) + for (i = 0; i < count; ) { if (dest) memcpy(dest + size, recs[i]->ptr, recs[i]->size); + + GITERR_CHECK_ALLOC_ADD(&size, size, recs[i++]->size); + } + if (add_nl) { i = recs[count - 1]->size; if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') { if (dest) dest[size] = '\n'; - size++; + + GITERR_CHECK_ALLOC_ADD(&size, size, 1); } } - return size; + + *out = size; + return 0; } -static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest) { - return xdl_recs_copy_0(0, xe, i, count, add_nl, dest); + return xdl_recs_copy_0(out, 0, xe, i, count, add_nl, dest); } -static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest) { - return xdl_recs_copy_0(1, xe, i, count, add_nl, dest); + return xdl_recs_copy_0(out, 1, xe, i, count, add_nl, dest); } -static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, +static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1, xdfenv_t *xe2, const char *name2, const char *name3, - int size, int i, int style, + size_t size, int i, int style, xdmerge_t *m, char *dest, int marker_size) { int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0); int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0); int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0); + size_t copied; + + *out = 0; if (marker_size <= 0) marker_size = DEFAULT_CONFLICT_MARKER_SIZE; /* Before conflicting part */ - size += xdl_recs_copy(xe1, i, m->i1 - i, 0, - dest ? dest + size : NULL); + if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, + dest ? dest + size : NULL) < 0) + return -1; + + GITERR_CHECK_ALLOC_ADD(&size, size, copied); if (!dest) { - size += marker_size + 1 + marker1_size; + GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker1_size); } else { memset(dest + size, '<', marker_size); size += marker_size; @@ -174,13 +190,16 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, } /* Postimage from side #1 */ - size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, - dest ? dest + size : NULL); + if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, 1, + dest ? dest + size : NULL) < 0) + return -1; + + GITERR_CHECK_ALLOC_ADD(&size, size, copied); if (style == XDL_MERGE_DIFF3) { /* Shared preimage */ if (!dest) { - size += marker_size + 1 + marker3_size; + GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker3_size); } else { memset(dest + size, '|', marker_size); size += marker_size; @@ -191,12 +210,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, } dest[size++] = '\n'; } - size += xdl_orig_copy(xe1, m->i0, m->chg0, 1, - dest ? dest + size : NULL); + + if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, 1, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); } if (!dest) { - size += marker_size + 1; + GITERR_CHECK_ALLOC_ADD3(&size, size, marker_size, 1); } else { memset(dest + size, '=', marker_size); size += marker_size; @@ -204,10 +226,14 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, } /* Postimage from side #2 */ - size += xdl_recs_copy(xe2, m->i2, m->chg2, 1, - dest ? dest + size : NULL); + + if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 1, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + if (!dest) { - size += marker_size + 1 + marker2_size; + GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker2_size); } else { memset(dest + size, '>', marker_size); size += marker_size; @@ -218,46 +244,69 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, } dest[size++] = '\n'; } - return size; + + *out = size; + return 0; } -static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, +static int xdl_fill_merge_buffer(size_t *out, + xdfenv_t *xe1, const char *name1, xdfenv_t *xe2, const char *name2, const char *ancestor_name, int favor, xdmerge_t *m, char *dest, int style, int marker_size) { - int size, i; + size_t size, copied; + int i; + + *out = 0; for (size = i = 0; m; m = m->next) { if (favor && !m->mode) m->mode = favor; - if (m->mode == 0) - size = fill_conflict_hunk(xe1, name1, xe2, name2, + if (m->mode == 0) { + if (fill_conflict_hunk(&size, xe1, name1, xe2, name2, ancestor_name, size, i, style, m, dest, - marker_size); + marker_size) < 0) + return -1; + } else if (m->mode & 3) { /* Before conflicting part */ - size += xdl_recs_copy(xe1, i, m->i1 - i, 0, - dest ? dest + size : NULL); + if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + /* Postimage from side #1 */ - if (m->mode & 1) - size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2), - dest ? dest + size : NULL); + if (m->mode & 1) { + if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, (m->mode & 2), + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + } + /* Postimage from side #2 */ - if (m->mode & 2) - size += xdl_recs_copy(xe2, m->i2, m->chg2, 0, - dest ? dest + size : NULL); + if (m->mode & 2) { + if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + } } else continue; i = m->i1 + m->chg1; } - size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0, - dest ? dest + size : NULL); - return size; + + if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + + *out = size; + return 0; } /* @@ -551,19 +600,24 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, /* output */ if (result) { int marker_size = xmp->marker_size; - int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2, + size_t size; + + if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2, ancestor_name, favor, changes, NULL, style, - marker_size); + marker_size) < 0) + return -1; + result->ptr = xdl_malloc(size); if (!result->ptr) { xdl_cleanup_merge(changes); return -1; } result->size = size; - xdl_fill_merge_buffer(xe1, name1, xe2, name2, + if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2, ancestor_name, favor, changes, - result->ptr, style, marker_size); + result->ptr, style, marker_size) < 0) + return -1; } return xdl_cleanup_merge(changes); } diff --git a/tests/commit/commit.c b/tests/commit/commit.c index f5461cfd3..81aaf80d3 100644 --- a/tests/commit/commit.c +++ b/tests/commit/commit.c @@ -70,12 +70,16 @@ void test_commit_commit__summary(void) assert_commit_summary("Trimmed leading&trailing newlines", "\n\nTrimmed leading&trailing newlines\n\n"); assert_commit_summary("First paragraph only", "\nFirst paragraph only\n\n(There are more!)"); assert_commit_summary("First paragraph with unwrapped trailing\tlines", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)"); - assert_commit_summary("\tLeading \ttabs", "\tLeading\n\ttabs\n\nis preserved"); - assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved"); + assert_commit_summary("\tLeading tabs", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */ + assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */ assert_commit_summary("Trailing tabs\tare removed", "Trailing tabs\tare removed\t\t"); assert_commit_summary("Trailing spaces are removed", "Trailing spaces are removed "); assert_commit_summary("Trailing tabs", "Trailing tabs\t\n\nare removed"); assert_commit_summary("Trailing spaces", "Trailing spaces \n\nare removed"); + assert_commit_summary("Newlines are replaced by spaces", "Newlines\nare\nreplaced by spaces\n"); + assert_commit_summary(" Spaces after newlines are collapsed", "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */ + assert_commit_summary(" Spaces before newlines are collapsed", " \nSpaces before newlines \nare \ncollapsed \n"); + assert_commit_summary(" Spaces around newlines are collapsed", " \n Spaces around newlines \n are \n collapsed \n "); assert_commit_summary("", ""); assert_commit_summary("", " "); assert_commit_summary("", "\n"); diff --git a/tests/commit/signature.c b/tests/commit/signature.c index 41a74b999..0070320ae 100644 --- a/tests/commit/signature.c +++ b/tests/commit/signature.c @@ -35,6 +35,13 @@ void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void) assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " \t nulltoken \n", " \n emeric.fermas@gmail.com \n"); } +void test_commit_signature__leading_and_trailing_crud_is_trimmed(void) +{ + assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", "\"nulltoken\"", "\"emeric.fermas@gmail.com\""); + assert_name_and_email("nulltoken w", "emeric.fermas@gmail.com", "nulltoken w.", "emeric.fermas@gmail.com"); + assert_name_and_email("nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com", "nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com"); +} + void test_commit_signature__angle_brackets_in_names_are_not_supported(void) { cl_git_fail(try_build_signature("<Phil Haack", "phil@haack", 1234567890, 60)); diff --git a/tests/commit/write.c b/tests/commit/write.c index ee9eb8237..176965cbd 100644 --- a/tests/commit/write.c +++ b/tests/commit/write.c @@ -8,7 +8,7 @@ static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; static const char *root_commit_message = "This is a root commit\n\ This is a root commit and should be the only one in this branch\n"; static const char *root_reflog_message = "commit (initial): This is a root commit \ - This is a root commit and should be the only one in this branch"; +This is a root commit and should be the only one in this branch"; static char *head_old; static git_reference *head, *branch; static git_commit *commit; diff --git a/tests/config/global.c b/tests/config/global.c index b5e83fec0..1336ef6e7 100644 --- a/tests/config/global.c +++ b/tests/config/global.c @@ -65,3 +65,43 @@ void test_config_global__open_xdg(void) git_config_free(xdg); git_config_free(cfg); } + +void test_config_global__open_programdata(void) +{ + char *programdata; + git_config *cfg; + git_repository *repo; + git_buf config_path = GIT_BUF_INIT; + git_buf var_contents = GIT_BUF_INIT; + + if (!cl_getenv("GITTEST_INVASIVE_FS_STRUCTURE")) + cl_skip(); + + programdata = cl_getenv("PROGRAMDATA"); + cl_git_pass(git_buf_printf(&config_path, "%s/Git", programdata)); + cl_git_pass(p_mkdir(config_path.ptr, 0777)); + cl_git_pass(git_buf_puts(&config_path, "/config")); + + cl_git_pass(git_config_open_ondisk(&cfg, config_path.ptr)); + cl_git_pass(git_config_set_string(cfg, "programdata.var", "even higher level")); + + git_buf_free(&config_path); + git_config_free(cfg); + + git_config_open_default(&cfg); + cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var")); + cl_assert_equal_s("even higher level", var_contents.ptr); + + git_config_free(cfg); + git_buf_free(&var_contents); + + cl_git_pass(git_repository_init(&repo, "./foo.git", true)); + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var")); + cl_assert_equal_s("even higher level", var_contents.ptr); + + git_config_free(cfg); + git_buf_free(&var_contents); + git_repository_free(repo); + cl_fixture_cleanup("./foo.git"); +} diff --git a/tests/config/stress.c b/tests/config/stress.c index 503f44f03..6e960425c 100644 --- a/tests/config/stress.c +++ b/tests/config/stress.c @@ -107,3 +107,23 @@ void test_config_stress__complex(void) git_config_free(config); } + +void test_config_stress__quick_write(void) +{ + git_config *config_w, *config_r; + const char *path = "./config-quick-write"; + const char *key = "quick.write"; + int32_t i; + + /* Create an external writer for one instance with the other one */ + cl_git_pass(git_config_open_ondisk(&config_w, path)); + cl_git_pass(git_config_open_ondisk(&config_r, path)); + + /* Write and read in the same second (repeat to increase the chance of it happening) */ + for (i = 0; i < 10; i++) { + int32_t val; + cl_git_pass(git_config_set_int32(config_w, key, i)); + cl_git_pass(git_config_get_int32(&val, config_r, key)); + cl_assert_equal_i(i, val); + } +} diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c index 915e3cc34..93aaed759 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -230,3 +230,12 @@ void test_core_filebuf__hidden_file(void) git_filebuf_cleanup(&file); #endif } + +void test_core_filebuf__detects_directory(void) +{ + git_filebuf file = GIT_FILEBUF_INIT, fail = GIT_FILEBUF_INIT; + + cl_must_pass(p_mkdir("foo", 0777)); + cl_git_fail_with(GIT_EDIRECTORY, git_filebuf_open(&file, "foo", 0, 0666)); + cl_must_pass(p_rmdir("foo")); +} diff --git a/tests/core/pool.c b/tests/core/pool.c index a7ec8801b..f90adfbc3 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -8,7 +8,7 @@ void test_core_pool__0(void) git_pool p; void *ptr; - cl_git_pass(git_pool_init(&p, 1, 4000)); + git_pool_init(&p, 1); for (i = 1; i < 10000; i *= 2) { ptr = git_pool_malloc(&p, i); @@ -17,13 +17,6 @@ void test_core_pool__0(void) cl_assert(!git_pool__ptr_in_pool(&p, &i)); } - /* 1+2+4+8+16+32+64+128+256+512+1024 -> original block */ - /* 2048 -> 1 block */ - /* 4096 -> 1 block */ - /* 8192 -> 1 block */ - - cl_assert(git_pool__open_pages(&p) + git_pool__full_pages(&p) == 4); - git_pool_clear(&p); } @@ -32,26 +25,24 @@ void test_core_pool__1(void) int i; git_pool p; - cl_git_pass(git_pool_init(&p, 1, 4000)); + git_pool_init(&p, 1); + p.page_size = 4000; for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); /* with fixed page size, allocation must end up with these values */ - cl_assert_equal_i(1, git_pool__open_pages(&p)); - cl_assert_equal_i(507, git_pool__full_pages(&p)); - + cl_assert_equal_i(590, git_pool__open_pages(&p)); git_pool_clear(&p); - cl_git_pass(git_pool_init(&p, 1, 4120)); + git_pool_init(&p, 1); + p.page_size = 4120; for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); /* with fixed page size, allocation must end up with these values */ - cl_assert_equal_i(1, git_pool__open_pages(&p)); - cl_assert_equal_i(492, git_pool__full_pages(&p)); - + cl_assert_equal_i(573, git_pool__open_pages(&p)); git_pool_clear(&p); } @@ -66,7 +57,8 @@ void test_core_pool__2(void) memset(oid_hex, '0', sizeof(oid_hex)); - cl_git_pass(git_pool_init(&p, sizeof(git_oid), 100)); + git_pool_init(&p, sizeof(git_oid)); + p.page_size = 4000; for (i = 1000; i < 10000; i++) { oid = git_pool_malloc(&p, 1); @@ -78,59 +70,7 @@ void test_core_pool__2(void) } /* with fixed page size, allocation must end up with these values */ - cl_assert(git_pool__open_pages(&p) == 0); - cl_assert(git_pool__full_pages(&p) == 90); - - git_pool_clear(&p); -} - -void test_core_pool__free_list(void) -{ - int i; - git_pool p; - void *ptr, *ptrs[50]; - - cl_git_pass(git_pool_init(&p, 100, 100)); - - for (i = 0; i < 10; ++i) { - ptr = git_pool_malloc(&p, 1); - cl_assert(ptr != NULL); - } - cl_assert_equal_i(10, (int)p.items); - - for (i = 0; i < 50; ++i) { - ptrs[i] = git_pool_malloc(&p, 1); - cl_assert(ptrs[i] != NULL); - } - cl_assert_equal_i(60, (int)p.items); - - git_pool_free(&p, ptr); - cl_assert_equal_i(60, (int)p.items); - - git_pool_free_array(&p, 50, ptrs); - cl_assert_equal_i(60, (int)p.items); - - for (i = 0; i < 50; ++i) { - ptrs[i] = git_pool_malloc(&p, 1); - cl_assert(ptrs[i] != NULL); - } - cl_assert_equal_i(60, (int)p.items); - - for (i = 0; i < 111; ++i) { - ptr = git_pool_malloc(&p, 1); - cl_assert(ptr != NULL); - } - cl_assert_equal_i(170, (int)p.items); - - git_pool_free_array(&p, 50, ptrs); - cl_assert_equal_i(170, (int)p.items); - - for (i = 0; i < 50; ++i) { - ptrs[i] = git_pool_malloc(&p, 1); - cl_assert(ptrs[i] != NULL); - } - cl_assert_equal_i(170, (int)p.items); - + cl_assert_equal_i(sizeof(void *) == 8 ? 55 : 45, git_pool__open_pages(&p)); git_pool_clear(&p); } @@ -138,7 +78,7 @@ void test_core_pool__strndup_limit(void) { git_pool p; - cl_git_pass(git_pool_init(&p, 1, 100)); + git_pool_init(&p, 1); /* ensure 64 bit doesn't overflow */ cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL); git_pool_clear(&p); diff --git a/tests/core/stream.c b/tests/core/stream.c new file mode 100644 index 000000000..ace6a05da --- /dev/null +++ b/tests/core/stream.c @@ -0,0 +1,47 @@ +#include "clar_libgit2.h" +#include "git2/sys/stream.h" +#include "tls_stream.h" +#include "stream.h" + +static git_stream test_stream; +static int ctor_called; + +static int test_ctor(git_stream **out, const char *host, const char *port) +{ + GIT_UNUSED(host); + GIT_UNUSED(port); + + ctor_called = 1; + *out = &test_stream; + + return 0; +} + +void test_core_stream__register_tls(void) +{ + git_stream *stream; + int error; + + ctor_called = 0; + cl_git_pass(git_stream_register_tls(test_ctor)); + cl_git_pass(git_tls_stream_new(&stream, "localhost", "443")); + cl_assert_equal_i(1, ctor_called); + cl_assert_equal_p(&test_stream, stream); + + ctor_called = 0; + stream = NULL; + cl_git_pass(git_stream_register_tls(NULL)); + error = git_tls_stream_new(&stream, "localhost", "443"); + + /* We don't have arbitrary TLS stream support on Windows */ +#if GIT_WIN32 + cl_git_fail_with(-1, error); +#else + cl_git_pass(error); +#endif + + cl_assert_equal_i(0, ctor_called); + cl_assert(&test_stream != stream); + + git_stream_free(stream); +} diff --git a/tests/core/useragent.c b/tests/core/useragent.c new file mode 100644 index 000000000..6d06693a8 --- /dev/null +++ b/tests/core/useragent.c @@ -0,0 +1,11 @@ +#include "clar_libgit2.h" +#include "global.h" + +void test_core_useragent__get(void) +{ + const char *custom_name = "super duper git"; + + cl_assert_equal_p(NULL, git_libgit2__user_agent()); + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name)); + cl_assert_equal_s(custom_name, git_libgit2__user_agent()); +} diff --git a/tests/diff/notify.c b/tests/diff/notify.c index 6ef4af573..74abbc93b 100644 --- a/tests/diff/notify.c +++ b/tests/diff/notify.c @@ -55,7 +55,7 @@ static void test_notify( opts.pathspec.strings = searched_pathspecs; opts.pathspec.count = pathspecs_count; - opts.notify_payload = expected_matched_pathspecs; + opts.payload = expected_matched_pathspecs; memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); @@ -228,3 +228,30 @@ void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void) git_diff_free(diff); } + +static int progress_abort_diff( + const git_diff *diff_so_far, + const char *old_path, + const char *new_path, + void *payload) +{ + GIT_UNUSED(old_path); + GIT_UNUSED(new_path); + GIT_UNUSED(payload); + + return -42; +} + +void test_diff_notify__progress_cb_can_abort_diff(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + + g_repo = cl_git_sandbox_init("status"); + + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; + opts.progress_cb = progress_abort_diff; + + cl_git_fail_with( + git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42); +} diff --git a/tests/diff/tree.c b/tests/diff/tree.c index 2bc9e6a55..e4b2a8bbe 100644 --- a/tests/diff/tree.c +++ b/tests/diff/tree.c @@ -90,7 +90,7 @@ void test_diff_tree__0(void) #define DIFF_OPTS(FLAGS, CTXT) \ {GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_UNSPECIFIED, \ - {NULL,0}, NULL, NULL, (CTXT), 1} + {NULL,0}, NULL, NULL, NULL, (CTXT), 1} void test_diff_tree__options(void) { diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 89f9483a6..4c782339d 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -2080,7 +2080,7 @@ void test_diff_workdir__to_index_pathlist(void) cl_git_pass(git_repository_index(&index, g_repo)); opts.flags = GIT_DIFF_INCLUDE_IGNORED; - opts.pathspec.strings = pathlist.contents; + opts.pathspec.strings = (char **)pathlist.contents; opts.pathspec.count = pathlist.length; cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts)); @@ -2097,3 +2097,64 @@ void test_diff_workdir__to_index_pathlist(void) git_vector_free(&pathlist); } +void test_diff_workdir__symlink_changed_on_non_symlink_platform(void) +{ + git_tree *tree; + git_diff *diff; + diff_expects exp = {0}; + const git_diff_delta *delta; + const char *commit = "7fccd7"; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_vector pathlist = GIT_VECTOR_INIT; + int symlinks; + + g_repo = cl_git_sandbox_init("unsymlinked.git"); + + cl_git_pass(git_repository__cvar(&symlinks, g_repo, GIT_CVAR_SYMLINKS)); + + if (symlinks) + cl_skip(); + + cl_git_pass(git_vector_insert(&pathlist, "include/Nu/Nu.h")); + + opts.pathspec.strings = (char **)pathlist.contents; + opts.pathspec.count = pathlist.length; + + cl_must_pass(p_mkdir("symlink", 0777)); + cl_git_pass(git_repository_set_workdir(g_repo, "symlink", false)); + + cl_assert((tree = resolve_commit_oid_to_tree(g_repo, commit)) != NULL); + + /* first, do the diff with the original contents */ + + cl_git_pass(git_futils_mkpath2file("symlink/include/Nu/Nu.h", 0755)); + cl_git_mkfile("symlink/include/Nu/Nu.h", "../../objc/Nu.h"); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + + /* now update the contents and expect a difference, but that the file + * mode has persisted as a symbolic link. + */ + + cl_git_rewritefile("symlink/include/Nu/Nu.h", "awesome content\n"); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts)); + + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + + cl_assert_equal_i(1, git_diff_num_deltas(diff)); + delta = git_diff_get_delta(diff, 0); + cl_assert_equal_i(GIT_FILEMODE_LINK, delta->old_file.mode); + cl_assert_equal_i(GIT_FILEMODE_LINK, delta->new_file.mode); + + git_diff_free(diff); + + cl_git_pass(git_futils_rmdir_r("symlink", NULL, GIT_RMDIR_REMOVE_FILES)); + + git_tree_free(tree); + git_vector_free(&pathlist); +} diff --git a/tests/index/bypath.c b/tests/index/bypath.c index b152b0917..88a76178a 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -240,3 +240,120 @@ void test_index_bypath__add_honors_existing_case_4(void) cl_assert_equal_s("just_a_dir/a/b/Z/y/X/foo.txt", entry->path); } +void test_index_bypath__add_honors_mode(void) +{ + const git_index_entry *entry; + git_index_entry new_entry; + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + + memcpy(&new_entry, entry, sizeof(git_index_entry)); + new_entry.path = "README.txt"; + new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE; + + cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE)); + + cl_git_pass(git_index_add(g_idx, &new_entry)); + cl_git_pass(git_index_write(g_idx)); + + cl_git_rewritefile("submod2/README.txt", "Modified but still executable"); + + cl_git_pass(git_index_add_bypath(g_idx, "README.txt")); + cl_git_pass(git_index_write(g_idx)); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode); +} + +void test_index_bypath__add_honors_conflict_mode(void) +{ + const git_index_entry *entry; + git_index_entry new_entry; + int stage = 0; + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + + memcpy(&new_entry, entry, sizeof(git_index_entry)); + new_entry.path = "README.txt"; + new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE; + + cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE)); + + cl_git_pass(git_index_remove_bypath(g_idx, "README.txt")); + + for (stage = 1; stage <= 3; stage++) { + new_entry.flags = stage << GIT_IDXENTRY_STAGESHIFT; + cl_git_pass(git_index_add(g_idx, &new_entry)); + } + + cl_git_pass(git_index_write(g_idx)); + + cl_git_rewritefile("submod2/README.txt", "Modified but still executable"); + + cl_git_pass(git_index_add_bypath(g_idx, "README.txt")); + cl_git_pass(git_index_write(g_idx)); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode); +} + +void test_index_bypath__add_honors_conflict_case(void) +{ + const git_index_entry *entry; + git_index_entry new_entry; + int stage = 0; + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + + memcpy(&new_entry, entry, sizeof(git_index_entry)); + new_entry.path = "README.txt"; + new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE; + + cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE)); + + cl_git_pass(git_index_remove_bypath(g_idx, "README.txt")); + + for (stage = 1; stage <= 3; stage++) { + new_entry.flags = stage << GIT_IDXENTRY_STAGESHIFT; + cl_git_pass(git_index_add(g_idx, &new_entry)); + } + + cl_git_pass(git_index_write(g_idx)); + + cl_git_rewritefile("submod2/README.txt", "Modified but still executable"); + + cl_git_pass(git_index_add_bypath(g_idx, "README.txt")); + cl_git_pass(git_index_write(g_idx)); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode); +} + +void test_index_bypath__add_honors_symlink(void) +{ + const git_index_entry *entry; + git_index_entry new_entry; + int symlinks; + + cl_git_pass(git_repository__cvar(&symlinks, g_repo, GIT_CVAR_SYMLINKS)); + + if (symlinks) + cl_skip(); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + + memcpy(&new_entry, entry, sizeof(git_index_entry)); + new_entry.path = "README.txt"; + new_entry.mode = GIT_FILEMODE_LINK; + + cl_git_pass(git_index_add(g_idx, &new_entry)); + cl_git_pass(git_index_write(g_idx)); + + cl_git_rewritefile("submod2/README.txt", "Modified but still a (fake) symlink"); + + cl_git_pass(git_index_add_bypath(g_idx, "README.txt")); + cl_git_pass(git_index_write(g_idx)); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + cl_assert_equal_i(GIT_FILEMODE_LINK, entry->mode); +} diff --git a/tests/index/conflicts.c b/tests/index/conflicts.c index b7a2456eb..8e94cd441 100644 --- a/tests/index/conflicts.c +++ b/tests/index/conflicts.c @@ -342,3 +342,94 @@ void test_index_conflicts__partial(void) cl_assert(conflict_entry[1] == NULL); cl_assert(conflict_entry[2] == NULL); } + +void test_index_conflicts__case_matters(void) +{ + const git_index_entry *conflict_entry[3]; + git_oid oid; + const char *upper_case = "DIFFERS-IN-CASE.TXT"; + const char *mixed_case = "Differs-In-Case.txt"; + const char *correct_case; + bool ignorecase = cl_repo_get_bool(repo, "core.ignorecase"); + + git_index_entry ancestor_entry, our_entry, their_entry; + + memset(&ancestor_entry, 0x0, sizeof(git_index_entry)); + memset(&our_entry, 0x0, sizeof(git_index_entry)); + memset(&their_entry, 0x0, sizeof(git_index_entry)); + + ancestor_entry.path = upper_case; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR); + git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID); + ancestor_entry.mode = GIT_FILEMODE_BLOB; + + our_entry.path = upper_case; + GIT_IDXENTRY_STAGE_SET(&our_entry, GIT_INDEX_STAGE_OURS); + git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID); + our_entry.mode = GIT_FILEMODE_BLOB; + + their_entry.path = upper_case; + GIT_IDXENTRY_STAGE_SET(&their_entry, GIT_INDEX_STAGE_THEIRS); + git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID); + their_entry.mode = GIT_FILEMODE_BLOB; + + cl_git_pass(git_index_conflict_add(repo_index, + &ancestor_entry, &our_entry, &their_entry)); + + ancestor_entry.path = mixed_case; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR); + git_oid_fromstr(&ancestor_entry.id, CONFLICTS_TWO_ANCESTOR_OID); + ancestor_entry.mode = GIT_FILEMODE_BLOB; + + our_entry.path = mixed_case; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR); + git_oid_fromstr(&our_entry.id, CONFLICTS_TWO_OUR_OID); + ancestor_entry.mode = GIT_FILEMODE_BLOB; + + their_entry.path = mixed_case; + GIT_IDXENTRY_STAGE_SET(&their_entry, GIT_INDEX_STAGE_THEIRS); + git_oid_fromstr(&their_entry.id, CONFLICTS_TWO_THEIR_OID); + their_entry.mode = GIT_FILEMODE_BLOB; + + cl_git_pass(git_index_conflict_add(repo_index, + &ancestor_entry, &our_entry, &their_entry)); + + cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], + &conflict_entry[2], repo_index, upper_case)); + + /* + * We inserted with mixed case last, so on a case-insensitive + * fs we should get the mixed case. + */ + if (ignorecase) + correct_case = mixed_case; + else + correct_case = upper_case; + + cl_assert_equal_s(correct_case, conflict_entry[0]->path); + git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_ANCESTOR_OID : CONFLICTS_ONE_ANCESTOR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[0]->id); + + cl_assert_equal_s(correct_case, conflict_entry[1]->path); + git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_OUR_OID : CONFLICTS_ONE_OUR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[1]->id); + + cl_assert_equal_s(correct_case, conflict_entry[2]->path); + git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_THEIR_OID : CONFLICTS_ONE_THEIR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[2]->id); + + cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], + &conflict_entry[2], repo_index, mixed_case)); + + cl_assert_equal_s(mixed_case, conflict_entry[0]->path); + git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[0]->id); + + cl_assert_equal_s(mixed_case, conflict_entry[1]->path); + git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[1]->id); + + cl_assert_equal_s(mixed_case, conflict_entry[2]->path); + git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[2]->id); +} diff --git a/tests/index/nsec.c b/tests/index/nsec.c new file mode 100644 index 000000000..5004339f0 --- /dev/null +++ b/tests/index/nsec.c @@ -0,0 +1,78 @@ +#include "clar_libgit2.h" +#include "index.h" +#include "git2/sys/index.h" +#include "git2/repository.h" +#include "../reset/reset_helpers.h" + +static git_repository *repo; +static git_index *repo_index; + +#define TEST_REPO_PATH "nsecs" + +// Fixture setup and teardown +void test_index_nsec__initialize(void) +{ + repo = cl_git_sandbox_init("nsecs"); + git_repository_index(&repo_index, repo); +} + +void test_index_nsec__cleanup(void) +{ + git_index_free(repo_index); + repo_index = NULL; + + cl_git_sandbox_cleanup(); +} + +static bool has_nsecs(void) +{ + const git_index_entry *entry; + size_t i; + bool has_nsecs = false; + + for (i = 0; i < git_index_entrycount(repo_index); i++) { + entry = git_index_get_byindex(repo_index, i); + + if (entry->ctime.nanoseconds || entry->mtime.nanoseconds) { + has_nsecs = true; + break; + } + } + + return has_nsecs; +} + +void test_index_nsec__has_nanos(void) +{ + cl_assert_equal_b(true, has_nsecs()); +} + +void test_index_nsec__staging_maintains_other_nanos(void) +{ + const git_index_entry *entry; + + cl_git_rewritefile("nsecs/a.txt", "This is file A"); + cl_git_pass(git_index_add_bypath(repo_index, "a.txt")); + cl_git_pass(git_index_write(repo_index)); + + cl_git_pass(git_index_write(repo_index)); + + git_index_read(repo_index, 1); + cl_assert_equal_b(true, has_nsecs()); + + cl_assert((entry = git_index_get_bypath(repo_index, "a.txt", 0))); + cl_assert_equal_i(0, entry->ctime.nanoseconds); + cl_assert_equal_i(0, entry->mtime.nanoseconds); +} + +void test_index_nsec__status_doesnt_clear_nsecs(void) +{ + git_status_list *statuslist; + + cl_git_pass(git_status_list_new(&statuslist, repo, NULL)); + + git_index_read(repo_index, 1); + cl_assert_equal_b(true, has_nsecs()); + + git_status_list_free(statuslist); +} diff --git a/tests/merge/files.c b/tests/merge/files.c index 2fd90d066..2d55df2b2 100644 --- a/tests/merge/files.c +++ b/tests/merge/files.c @@ -6,6 +6,7 @@ #include "merge_helpers.h" #include "refs.h" #include "fileops.h" +#include "diff_xdiff.h" #define TEST_REPO_PATH "merge-resolve" #define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" @@ -288,3 +289,90 @@ void test_merge_files__doesnt_add_newline(void) git_merge_file_result_free(&result); } +void test_merge_files__skips_large_files(void) +{ + git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT, + theirs = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; + + ours.size = GIT_XDIFF_MAX_SIZE + 1; + ours.path = "testfile.txt"; + ours.mode = 0100755; + + theirs.size = GIT_XDIFF_MAX_SIZE + 1; + theirs.path = "testfile.txt"; + theirs.mode = 0100755; + + cl_git_pass(git_merge_file(&result, NULL, &ours, &theirs, &opts)); + + cl_assert_equal_i(0, result.automergeable); + + git_merge_file_result_free(&result); +} + +void test_merge_files__skips_binaries(void) +{ + git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT, + ours = GIT_MERGE_FILE_INPUT_INIT, + theirs = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_result result = {0}; + + ancestor.ptr = "ance\0stor\0"; + ancestor.size = 10; + ancestor.path = "ancestor.txt"; + ancestor.mode = 0100755; + + ours.ptr = "foo\0bar\0"; + ours.size = 8; + ours.path = "ours.txt"; + ours.mode = 0100755; + + theirs.ptr = "bar\0foo\0"; + theirs.size = 8; + theirs.path = "theirs.txt"; + theirs.mode = 0100644; + + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, NULL)); + + cl_assert_equal_i(0, result.automergeable); + + git_merge_file_result_free(&result); +} + +void test_merge_files__handles_binaries_when_favored(void) +{ + git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT, + ours = GIT_MERGE_FILE_INPUT_INIT, + theirs = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; + + ancestor.ptr = "ance\0stor\0"; + ancestor.size = 10; + ancestor.path = "ancestor.txt"; + ancestor.mode = 0100755; + + ours.ptr = "foo\0bar\0"; + ours.size = 8; + ours.path = "ours.txt"; + ours.mode = 0100755; + + theirs.ptr = "bar\0foo\0"; + theirs.size = 8; + theirs.path = "theirs.txt"; + theirs.mode = 0100644; + + opts.favor = GIT_MERGE_FILE_FAVOR_OURS; + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts)); + + cl_assert_equal_i(1, result.automergeable); + + cl_assert_equal_s("ours.txt", result.path); + cl_assert_equal_i(0100755, result.mode); + + cl_assert_equal_i(ours.size, result.len); + cl_assert(memcmp(result.ptr, ours.ptr, ours.size) == 0); + + git_merge_file_result_free(&result); +} diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c index f81471424..986a365db 100644 --- a/tests/merge/merge_helpers.c +++ b/tests/merge/merge_helpers.c @@ -40,7 +40,7 @@ int merge_trees_from_branches( cl_git_pass(git_commit_tree(&our_tree, our_commit)); cl_git_pass(git_commit_tree(&their_tree, their_commit)); - cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts)); + error = git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts); git_buf_free(&branch_buf); git_tree_free(our_tree); @@ -50,7 +50,7 @@ int merge_trees_from_branches( git_commit_free(their_commit); git_commit_free(ancestor_commit); - return 0; + return error; } int merge_commits_from_branches( @@ -61,6 +61,7 @@ int merge_commits_from_branches( git_commit *our_commit, *their_commit; git_oid our_oid, their_oid; git_buf branch_buf = GIT_BUF_INIT; + int error; git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name); cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); @@ -71,13 +72,13 @@ int merge_commits_from_branches( cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr)); cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid)); - cl_git_pass(git_merge_commits(index, repo, our_commit, their_commit, opts)); + error = git_merge_commits(index, repo, our_commit, their_commit, opts); git_buf_free(&branch_buf); git_commit_free(our_commit); git_commit_free(their_commit); - return 0; + return error; } int merge_branches(git_repository *repo, diff --git a/tests/merge/trees/commits.c b/tests/merge/trees/commits.c index c4e470997..2e3c4578b 100644 --- a/tests/merge/trees/commits.c +++ b/tests/merge/trees/commits.c @@ -94,7 +94,6 @@ void test_merge_trees_commits__no_ancestor(void) git_index_free(index); } - void test_merge_trees_commits__df_conflict(void) { git_index *index; @@ -129,3 +128,20 @@ void test_merge_trees_commits__df_conflict(void) git_index_free(index); } + +void test_merge_trees_commits__fail_on_conflict(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + opts.tree_flags |= GIT_MERGE_TREE_FAIL_ON_CONFLICT; + + cl_git_fail_with(GIT_EMERGECONFLICT, + merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts)); + + cl_git_fail_with(GIT_EMERGECONFLICT, + merge_commits_from_branches(&index, repo, "master", "unrelated", &opts)); + cl_git_fail_with(GIT_EMERGECONFLICT, + merge_commits_from_branches(&index, repo, "master", "branch", &opts)); +} + diff --git a/tests/odb/sorting.c b/tests/odb/sorting.c index d24c49c69..6af8b0d1b 100644 --- a/tests/odb/sorting.c +++ b/tests/odb/sorting.c @@ -57,14 +57,14 @@ void test_odb_sorting__basic_backends_sorting(void) void test_odb_sorting__alternate_backends_sorting(void) { - cl_git_pass(git_odb_add_backend(_odb, new_backend(0), 5)); - cl_git_pass(git_odb_add_backend(_odb, new_backend(2), 3)); - cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 4)); - cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 1)); - cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 5)); - cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 3)); - cl_git_pass(git_odb_add_alternate(_odb, new_backend(5), 4)); - cl_git_pass(git_odb_add_alternate(_odb, new_backend(7), 1)); + cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 5)); + cl_git_pass(git_odb_add_backend(_odb, new_backend(5), 3)); + cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 4)); + cl_git_pass(git_odb_add_backend(_odb, new_backend(7), 1)); + cl_git_pass(git_odb_add_alternate(_odb, new_backend(0), 5)); + cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 3)); + cl_git_pass(git_odb_add_alternate(_odb, new_backend(2), 4)); + cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 1)); check_backend_sorting(_odb); } diff --git a/tests/online/badssl.c b/tests/online/badssl.c index 850468320..12badbda3 100644 --- a/tests/online/badssl.c +++ b/tests/online/badssl.c @@ -5,23 +5,34 @@ static git_repository *g_repo; #if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) +static bool g_has_ssl = true; +#else +static bool g_has_ssl = false; +#endif void test_online_badssl__expired(void) { + if (!g_has_ssl) + cl_skip(); + cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", NULL)); } void test_online_badssl__wrong_host(void) { + if (!g_has_ssl) + cl_skip(); + cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", NULL)); } void test_online_badssl__self_signed(void) { + if (!g_has_ssl) + cl_skip(); + cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL)); } - -#endif diff --git a/tests/refs/create.c b/tests/refs/create.c index 192551dbd..48194ae3b 100644 --- a/tests/refs/create.c +++ b/tests/refs/create.c @@ -151,6 +151,23 @@ void test_refs_create__propagate_eexists(void) cl_assert(error == GIT_EEXISTS); } +void test_refs_create__existing_dir_propagates_edirectory(void) +{ + git_reference *new_reference, *fail_reference; + git_oid id; + const char *dir_head = "refs/heads/new-dir/new-head", + *fail_head = "refs/heads/new-dir"; + + git_oid_fromstr(&id, current_master_tip); + + /* Create and write the new object id reference */ + cl_git_pass(git_reference_create(&new_reference, g_repo, dir_head, &id, 1, NULL)); + cl_git_fail_with(GIT_EDIRECTORY, + git_reference_create(&fail_reference, g_repo, fail_head, &id, false, NULL)); + + git_reference_free(new_reference); +} + static void test_invalid_name(const char *name) { git_reference *new_reference; diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 56ec422c3..fdb15502c 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -125,6 +125,77 @@ void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void) git_buf_free(&master_log_path); } +void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void) +{ + git_reference *master; + git_buf master_log_path = GIT_BUF_INIT; + + git_buf_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_buf_joinpath(&master_log_path, git_buf_cstr(&master_log_path), "refs/heads/master"); + + cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&master_log_path))); + + cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master")); + cl_git_pass(git_reference_delete(master)); + git_reference_free(master); + + cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&master_log_path))); + git_buf_free(&master_log_path); +} + +void test_refs_reflog_reflog__removes_empty_reflog_dir(void) +{ + git_reference *ref; + git_buf log_path = GIT_BUF_INIT; + git_oid id; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL)); + + git_buf_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_buf_joinpath(&log_path, git_buf_cstr(&log_path), "refs/heads/new-dir/new-head"); + + cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path))); + + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + /* new ref creation should succeed since new-dir is empty */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL)); + git_reference_free(ref); + + git_buf_free(&log_path); +} + +void test_refs_reflog_reflog__fails_gracefully_on_nonempty_reflog_dir(void) +{ + git_reference *ref; + git_buf log_path = GIT_BUF_INIT; + git_oid id; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL)); + git_reference_free(ref); + + git_buf_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_buf_joinpath(&log_path, git_buf_cstr(&log_path), "refs/heads/new-dir/new-head"); + + cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path))); + + /* delete the ref manually, leave the reflog */ + cl_must_pass(p_unlink("testrepo.git/refs/heads/new-dir/new-head")); + + /* new ref creation should fail since new-dir contains reflogs still */ + git_oid_fromstr(&id, current_master_tip); + cl_git_fail_with(GIT_EDIRECTORY, git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL)); + git_reference_free(ref); + + git_buf_free(&log_path); +} + static void assert_has_reflog(bool expected_result, const char *name) { cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name)); @@ -154,6 +225,49 @@ void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_re git_buf_free(&subtrees_log_path); } +void test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_returns_error(void) +{ + git_reflog *reflog; + const git_error *error; + const char *refname = "refs/heads/newline"; + const char *refmessage = + "Reflog*message with a newline and enough content after it to pass the GIT_REFLOG_SIZE_MIN check inside reflog_parse."; + git_reference *ref; + git_oid id; + git_buf logpath = GIT_BUF_INIT, logcontents = GIT_BUF_INIT; + char *star; + + git_oid_fromstr(&id, current_master_tip); + + /* create a new branch */ + cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, refmessage)); + + /* corrupt the branch reflog by introducing a newline inside the reflog message (we replace '*' with '\n') */ + git_buf_join_n(&logpath, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname); + cl_git_pass(git_futils_readbuffer(&logcontents, git_buf_cstr(&logpath))); + cl_assert((star = strchr(git_buf_cstr(&logcontents), '*')) != NULL); + *star = '\n'; + cl_git_rewritefile(git_buf_cstr(&logpath), git_buf_cstr(&logcontents)); + + /* confirm that the file was rewritten successfully and now contains a '\n' in the expected location */ + cl_git_pass(git_futils_readbuffer(&logcontents, git_buf_cstr(&logpath))); + cl_assert(strstr(git_buf_cstr(&logcontents), "Reflog\nmessage") != NULL); + + /* clear the error state so we can capture the error generated by git_reflog_read */ + giterr_clear(); + + cl_git_fail(git_reflog_read(&reflog, g_repo, refname)); + + error = giterr_last(); + + cl_assert(error != NULL); + cl_assert_equal_s("Unable to parse OID - contains invalid characters", error->message); + + git_reference_free(ref); + git_buf_free(&logpath); + git_buf_free(&logcontents); +} + void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void) { git_reference *master, *new_master; diff --git a/tests/resources/nsecs/.gitted/HEAD b/tests/resources/nsecs/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests/resources/nsecs/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests/resources/nsecs/.gitted/config b/tests/resources/nsecs/.gitted/config new file mode 100644 index 000000000..78387c50b --- /dev/null +++ b/tests/resources/nsecs/.gitted/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + symlinks = false + ignorecase = true + hideDotFiles = dotGitOnly diff --git a/tests/resources/nsecs/.gitted/index b/tests/resources/nsecs/.gitted/index Binary files differnew file mode 100644 index 000000000..9233f1b11 --- /dev/null +++ b/tests/resources/nsecs/.gitted/index diff --git a/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e b/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e new file mode 100644 index 000000000..a813b7424 --- /dev/null +++ b/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e @@ -0,0 +1,2 @@ +xA +0D]J4DMu-/"FHFf
PaʠZrnX*4kUixK-#%y Z20;Џ;
ŰJZ7FRBy?g?<^@]f˔GvܵNUOKv
\ No newline at end of file diff --git a/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac b/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac Binary files differnew file mode 100644 index 000000000..74bb7d3fe --- /dev/null +++ b/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac diff --git a/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4 b/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4 Binary files differnew file mode 100644 index 000000000..7bf3a956c --- /dev/null +++ b/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4 diff --git a/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745 b/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745 Binary files differnew file mode 100644 index 000000000..dcf4c8ccb --- /dev/null +++ b/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745 diff --git a/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4 b/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4 Binary files differnew file mode 100644 index 000000000..df45d3314 --- /dev/null +++ b/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4 diff --git a/tests/resources/nsecs/.gitted/refs/heads/master b/tests/resources/nsecs/.gitted/refs/heads/master new file mode 100644 index 000000000..3dda65b65 --- /dev/null +++ b/tests/resources/nsecs/.gitted/refs/heads/master @@ -0,0 +1 @@ +031986a8372d1442cfe9e3b54906a9aadc524a7e diff --git a/tests/resources/nsecs/a.txt b/tests/resources/nsecs/a.txt new file mode 100644 index 000000000..be4c1ee68 --- /dev/null +++ b/tests/resources/nsecs/a.txt @@ -0,0 +1 @@ +File A
diff --git a/tests/resources/nsecs/b.txt b/tests/resources/nsecs/b.txt new file mode 100644 index 000000000..19a0af40f --- /dev/null +++ b/tests/resources/nsecs/b.txt @@ -0,0 +1 @@ +File B
diff --git a/tests/resources/nsecs/c.txt b/tests/resources/nsecs/c.txt new file mode 100644 index 000000000..31d776008 --- /dev/null +++ b/tests/resources/nsecs/c.txt @@ -0,0 +1 @@ +File C
diff --git a/tests/resources/redundant.git/HEAD b/tests/resources/redundant.git/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests/resources/redundant.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests/resources/redundant.git/config b/tests/resources/redundant.git/config new file mode 100755 index 000000000..2f8958058 --- /dev/null +++ b/tests/resources/redundant.git/config @@ -0,0 +1,5 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true + logallrefupdates = true diff --git a/tests/resources/redundant.git/objects/info/packs b/tests/resources/redundant.git/objects/info/packs new file mode 100644 index 000000000..fbf960f10 --- /dev/null +++ b/tests/resources/redundant.git/objects/info/packs @@ -0,0 +1,2 @@ +P pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack + diff --git a/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx Binary files differnew file mode 100644 index 000000000..d8e099a98 --- /dev/null +++ b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx diff --git a/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack Binary files differnew file mode 100644 index 000000000..02ea49f4b --- /dev/null +++ b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack diff --git a/tests/resources/redundant.git/packed-refs b/tests/resources/redundant.git/packed-refs new file mode 100644 index 000000000..e8bf04d65 --- /dev/null +++ b/tests/resources/redundant.git/packed-refs @@ -0,0 +1,3 @@ +# pack-refs with: peeled fully-peeled +e18fa2788e9c4e12d83150808a31dfbfb1ae364f refs/heads/master +91f4b95df4a59504a9813ba66912562931d990e3 refs/heads/ref2/ref28 diff --git a/tests/resources/redundant.git/refs/.gitkeep b/tests/resources/redundant.git/refs/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/resources/redundant.git/refs/.gitkeep diff --git a/tests/resources/submodule_with_path/.gitmodules b/tests/resources/submodule_with_path/.gitmodules new file mode 100644 index 000000000..ba34c47dc --- /dev/null +++ b/tests/resources/submodule_with_path/.gitmodules @@ -0,0 +1,3 @@ +[submodule "testrepo"] + path = lib/testrepo + url = ../testrepo.git diff --git a/tests/resources/submodule_with_path/.gitted/HEAD b/tests/resources/submodule_with_path/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests/resources/submodule_with_path/.gitted/config b/tests/resources/submodule_with_path/.gitted/config new file mode 100644 index 000000000..78387c50b --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + symlinks = false + ignorecase = true + hideDotFiles = dotGitOnly diff --git a/tests/resources/submodule_with_path/.gitted/index b/tests/resources/submodule_with_path/.gitted/index Binary files differnew file mode 100644 index 000000000..a740b4b91 --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/index diff --git a/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693 b/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693 Binary files differnew file mode 100644 index 000000000..d9b4313e1 --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693 diff --git a/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26 b/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26 Binary files differnew file mode 100644 index 000000000..366c161c1 --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26 diff --git a/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921 b/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921 Binary files differnew file mode 100644 index 000000000..7c1af6645 --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921 diff --git a/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3 b/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3 Binary files differnew file mode 100644 index 000000000..447558259 --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3 diff --git a/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7 b/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7 Binary files differnew file mode 100644 index 000000000..3069f151f --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7 diff --git a/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 b/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 Binary files differnew file mode 100644 index 000000000..9f664569c --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 diff --git a/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b b/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b Binary files differnew file mode 100644 index 000000000..56a844953 --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b diff --git a/tests/resources/submodule_with_path/.gitted/refs/heads/master b/tests/resources/submodule_with_path/.gitted/refs/heads/master new file mode 100644 index 000000000..4b5a5a21d --- /dev/null +++ b/tests/resources/submodule_with_path/.gitted/refs/heads/master @@ -0,0 +1 @@ +89ca686bb21bfb75dda99a02313831a0c418f921 diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c index 7e50452c9..5ed7da4eb 100644 --- a/tests/revwalk/basic.c +++ b/tests/revwalk/basic.c @@ -437,3 +437,39 @@ void test_revwalk_basic__mimic_git_rev_list(void) cl_git_fail_with(git_revwalk_next(&oid, _walk), GIT_ITEROVER); } + +void test_revwalk_basic__big_timestamp(void) +{ + git_reference *head; + git_commit *tip; + git_signature *sig; + git_tree *tree; + git_oid id; + int error; + + revwalk_basic_setup_walk("testrepo.git"); + + cl_git_pass(git_repository_head(&head, _repo)); + cl_git_pass(git_reference_peel((git_object **) &tip, head, GIT_OBJ_COMMIT)); + + /* Commit with a far-ahead timestamp, we should be able to parse it in the revwalk */ + cl_git_pass(git_signature_new(&sig, "Joe", "joe@example.com", 2399662595, 0)); + cl_git_pass(git_commit_tree(&tree, tip)); + + cl_git_pass(git_commit_create(&id, _repo, "HEAD", sig, sig, NULL, "some message", tree, 1, + (const git_commit **)&tip)); + + cl_git_pass(git_revwalk_push_head(_walk)); + + while ((error = git_revwalk_next(&id, _walk)) == 0) { + /* nothing */ + } + + cl_assert_equal_i(GIT_ITEROVER, error); + + git_tree_free(tree); + git_commit_free(tip); + git_reference_free(head); + git_signature_free(sig); + +} diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c index aafe97d71..ee078b3e7 100644 --- a/tests/revwalk/mergebase.c +++ b/tests/revwalk/mergebase.c @@ -492,3 +492,23 @@ void test_revwalk_mergebase__octopus_merge_branch(void) * * a */ + +void test_revwalk_mergebase__remove_redundant(void) +{ + git_repository *repo; + git_oid one, two, base; + git_oidarray result = {NULL, 0}; + + cl_git_pass(git_repository_open(&repo, cl_fixture("redundant.git"))); + + cl_git_pass(git_oid_fromstr(&one, "d89137c93ba1ee749214ff4ce52ae9137bc833f9")); + cl_git_pass(git_oid_fromstr(&two, "91f4b95df4a59504a9813ba66912562931d990e3")); + cl_git_pass(git_oid_fromstr(&base, "6cb1f2352d974e1c5a776093017e8772416ac97a")); + + cl_git_pass(git_merge_bases(&result, repo, &one, &two)); + cl_assert_equal_i(1, result.count); + cl_assert_equal_oid(&base, &result.ids[0]); + + git_oidarray_free(&result); + git_repository_free(repo); +} diff --git a/tests/revwalk/signatureparsing.c b/tests/revwalk/signatureparsing.c index 5c7d8813d..b312bad09 100644 --- a/tests/revwalk/signatureparsing.c +++ b/tests/revwalk/signatureparsing.c @@ -38,7 +38,7 @@ void test_revwalk_signatureparsing__do_not_choke_when_name_contains_angle_bracke signature = git_commit_committer(commit); cl_assert_equal_s("foo@example.com", signature->email); - cl_assert_equal_s("<Yu V. Bin Haacked>", signature->name); + cl_assert_equal_s("Yu V. Bin Haacked", signature->name); cl_assert_equal_i(1323847743, (int)signature->when.time); cl_assert_equal_i(60, signature->when.offset); diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c index cde69d92d..4ff4b4da7 100644 --- a/tests/submodule/submodule_helpers.c +++ b/tests/submodule/submodule_helpers.c @@ -156,6 +156,21 @@ git_repository *setup_fixture_submodule_simple(void) return repo; } +git_repository *setup_fixture_submodule_with_path(void) +{ + git_repository *repo = cl_git_sandbox_init("submodule_with_path"); + + cl_fixture_sandbox("testrepo.git"); + p_mkdir("submodule_with_path/lib", 0777); + p_mkdir("submodule_with_path/lib/testrepo", 0777); + + cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git"); + + cl_git_pass(git_repository_reinit_filesystem(repo, 1)); + + return repo; +} + void assert__submodule_exists( git_repository *repo, const char *name, const char *msg, const char *file, int line) diff --git a/tests/submodule/submodule_helpers.h b/tests/submodule/submodule_helpers.h index 1191ab35b..42b14a7bc 100644 --- a/tests/submodule/submodule_helpers.h +++ b/tests/submodule/submodule_helpers.h @@ -5,6 +5,7 @@ extern git_repository *setup_fixture_submodules(void); extern git_repository *setup_fixture_submod2(void); extern git_repository *setup_fixture_submodule_simple(void); extern git_repository *setup_fixture_super(void); +extern git_repository *setup_fixture_submodule_with_path(void); extern unsigned int get_submodule_status(git_repository *, const char *); diff --git a/tests/submodule/update.c b/tests/submodule/update.c index 40d24d0a7..cbd519d81 100644 --- a/tests/submodule/update.c +++ b/tests/submodule/update.c @@ -131,6 +131,53 @@ void test_submodule_update__update_submodule(void) git_submodule_free(sm); } +void test_submodule_update__update_submodule_with_path(void) +{ + git_submodule *sm; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + unsigned int submodule_status = 0; + struct update_submodule_cb_payload update_payload = { 0 }; + + g_repo = setup_fixture_submodule_with_path(); + + update_options.checkout_opts.progress_cb = checkout_progress_cb; + update_options.checkout_opts.progress_payload = &update_payload; + + update_options.fetch_opts.callbacks.update_tips = update_tips; + update_options.fetch_opts.callbacks.payload = &update_payload; + + /* get the submodule */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + /* verify the initial state of the submodule */ + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED); + + /* initialize and update the submodule */ + cl_git_pass(git_submodule_init(sm, 0)); + cl_git_pass(git_submodule_update(sm, 0, &update_options)); + + /* verify state */ + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_IN_WD); + + cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + + /* verify that the expected callbacks have been called. */ + cl_assert_equal_i(1, update_payload.checkout_progress_called); + cl_assert_equal_i(1, update_payload.update_tips_called); + + git_submodule_free(sm); +} + void test_submodule_update__update_and_init_submodule(void) { git_submodule *sm; @@ -390,3 +437,4 @@ void test_submodule_update__can_force_update(void) git_object_free(branch_commit); git_reference_free(branch_reference); } + |