diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2015-11-05 10:49:39 -0500 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2015-11-05 10:49:39 -0500 |
commit | 41dc9f067d822c91c70fd9e241c4a89da7c897ba (patch) | |
tree | 22346591a32d0641f78f85935542a46496dad340 | |
parent | fe965028885fbd8c62dce08e3a86cd3cb3e3b320 (diff) | |
parent | 5b6b774533fe0973a652be296b774bcddd96e5fd (diff) | |
download | libgit2-41dc9f067d822c91c70fd9e241c4a89da7c897ba.tar.gz |
Merge pull request #3501 from libgit2/cmn/for-v23
Backports for v0.23
57 files changed, 1035 insertions, 286 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 73c963016..714e188e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ CMAKE_POLICY(SET CMP0015 NEW) SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") INCLUDE(CheckLibraryExists) +INCLUDE(CheckFunctionExists) INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgConfig) @@ -95,6 +96,23 @@ SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.") SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.") SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.") +# Set a couple variables to be substituted inside the .pc file. +# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue +# or relative paths is both valid and supported by cmake. +SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX}) + +IF(IS_ABSOLUTE ${LIB_INSTALL_DIR}) + SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR}) +ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR}) + SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}") +ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR}) + +IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR}) +ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}") +ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + FUNCTION(TARGET_OS_LIBRARIES target) IF(WIN32) TARGET_LINK_LIBRARIES(${target} ws2_32) @@ -440,6 +458,21 @@ ELSE () ENDIF () ENDIF() +CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS) +IF (HAVE_FUTIMENS) + ADD_DEFINITIONS(-DHAVE_FUTIMENS) +ENDIF () + +CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R) +IF (HAVE_QSORT_R) + ADD_DEFINITIONS(-DHAVE_QSORT_R) +ENDIF () + +CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S) +IF (HAVE_QSORT_S) + ADD_DEFINITIONS(-DHAVE_QSORT_S) +ENDIF () + IF( NOT CMAKE_CONFIGURATION_TYPES ) # Build Debug by default IF (NOT CMAKE_BUILD_TYPE) @@ -546,7 +579,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") 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/include/git2/common.h b/include/git2/common.h index d84a76512..577906115 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 \ diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index 867fbcbce..e6ee3c654 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/libgit2.pc.in b/libgit2.pc.in index 3d825a49f..880266a30 100644 --- a/libgit2.pc.in +++ b/libgit2.pc.in @@ -1,5 +1,6 @@ -libdir=@CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@ -includedir=@CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@ +prefix=@PKGCONFIG_PREFIX@ +libdir=@PKGCONFIG_LIBDIR@ +includedir=@PKGCONFIG_INCLUDEDIR@ Name: libgit2 Description: The git library, take 2 diff --git a/src/checkout.c b/src/checkout.c index e7699d95f..12e308257 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -244,6 +244,12 @@ static int checkout_action_common( if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL) *action |= CHECKOUT_ACTION__REMOVE; + /* if the file is on disk and doesn't match our mode, force update */ + if (wd && + GIT_PERMS_IS_EXEC(wd->mode) != + GIT_PERMS_IS_EXEC(delta->new_file.mode)) + *action |= CHECKOUT_ACTION__REMOVE; + notify = GIT_CHECKOUT_NOTIFY_UPDATED; } @@ -1501,15 +1507,6 @@ static int blob_content_to_file( if (error < 0) return error; - if (GIT_PERMS_IS_EXEC(mode)) { - data->perfdata.chmod_calls++; - - if ((error = p_chmod(path, mode)) < 0) { - giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path); - return error; - } - } - if (st) { data->perfdata.stat_calls++; diff --git a/src/commit_list.c b/src/commit_list.c index 3054c18dd..53612d514 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -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/diff.c b/src/diff.c index c1adcc662..9cde03e17 100644 --- a/src/diff.c +++ b/src/diff.c @@ -471,8 +471,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) { diff --git a/src/diff_print.c b/src/diff_print.c index d406a441a..bc2d6fab0 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -358,6 +358,7 @@ static int format_binary( scan += chunk_len; pi->line.num_lines++; } + git_buf_putc(pi->buf, '\n'); return 0; } @@ -416,7 +417,6 @@ static int diff_print_patch_file_binary( if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data, binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 || - (error = git_buf_putc(pi->buf, '\n')) < 0 || (error = format_binary(pi, binary->old_file.type, binary->old_file.data, binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) { diff --git a/src/filebuf.c b/src/filebuf.c index 838f4b4d2..2bbc210ba 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -191,6 +191,81 @@ static int write_deflate(git_filebuf *file, void *source, size_t len) return 0; } +#define MAX_SYMLINK_DEPTH 5 + +static int resolve_symlink(git_buf *out, const char *path) +{ + int i, error, root; + ssize_t ret; + struct stat st; + git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT; + + if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 || + (error = git_buf_puts(&curpath, path)) < 0) + return error; + + for (i = 0; i < MAX_SYMLINK_DEPTH; i++) { + error = p_lstat(curpath.ptr, &st); + if (error < 0 && errno == ENOENT) { + error = git_buf_puts(out, curpath.ptr); + goto cleanup; + } + + if (error < 0) { + giterr_set(GITERR_OS, "failed to stat '%s'", curpath.ptr); + error = -1; + goto cleanup; + } + + if (!S_ISLNK(st.st_mode)) { + error = git_buf_puts(out, curpath.ptr); + goto cleanup; + } + + ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX); + if (ret < 0) { + giterr_set(GITERR_OS, "failed to read symlink '%s'", curpath.ptr); + error = -1; + goto cleanup; + } + + if (ret == GIT_PATH_MAX) { + giterr_set(GITERR_INVALID, "symlink target too long"); + error = -1; + goto cleanup; + } + + /* readlink(2) won't NUL-terminate for us */ + target.ptr[ret] = '\0'; + target.size = ret; + + root = git_path_root(target.ptr); + if (root >= 0) { + if ((error = git_buf_puts(&curpath, target.ptr)) < 0) + goto cleanup; + } else { + git_buf dir = GIT_BUF_INIT; + + if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0) + goto cleanup; + + git_buf_swap(&curpath, &dir); + git_buf_free(&dir); + + if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0) + goto cleanup; + } + } + + giterr_set(GITERR_INVALID, "maximum symlink depth reached"); + error = -1; + +cleanup: + git_buf_free(&curpath); + git_buf_free(&target); + return error; +} + int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode) { int compression, error = -1; @@ -265,11 +340,14 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode file->path_lock = git_buf_detach(&tmp_path); GITERR_CHECK_ALLOC(file->path_lock); } else { - path_len = strlen(path); + git_buf resolved_path = GIT_BUF_INIT; + + if ((error = resolve_symlink(&resolved_path, path)) < 0) + goto cleanup; /* Save the original path of the file */ - file->path_original = git__strdup(path); - GITERR_CHECK_ALLOC(file->path_original); + path_len = resolved_path.size; + file->path_original = git_buf_detach(&resolved_path); /* create the locking path by appending ".lock" to the original */ GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH); diff --git a/src/index.c b/src/index.c index 73f0b3d26..cb5902ea9 100644 --- a/src/index.c +++ b/src/index.c @@ -1790,27 +1790,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, @@ -1825,7 +1822,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; @@ -1845,7 +1842,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; @@ -1857,8 +1854,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); } @@ -1867,7 +1864,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); diff --git a/src/merge.c b/src/merge.c index 13b524b81..9799f935b 100644 --- a/src/merge.c +++ b/src/merge.c @@ -302,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; @@ -376,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; + + 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__free(tmp); - tmp = next; + git_vector_free(&redundant); } *out = result; @@ -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) @@ -621,23 +625,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); } @@ -645,43 +644,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) @@ -698,13 +699,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; @@ -784,36 +825,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) @@ -823,42 +866,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; @@ -879,7 +928,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; @@ -888,6 +937,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_mempack.c b/src/odb_mempack.c index 34355270f..25f30590c 100644 --- a/src/odb_mempack.c +++ b/src/odb_mempack.c @@ -154,12 +154,19 @@ void git_mempack_reset(git_odb_backend *_backend) }); git_array_clear(db->commits); + + git_oidmap_free(db->objects); + db->objects = git_oidmap_alloc(); } static void impl__free(git_odb_backend *_backend) { - git_mempack_reset(_backend); - git__free(_backend); + struct memory_packer_db *db = (struct memory_packer_db *)_backend; + + git_mempack_reset(db); + git_oidmap_free(db->objects); + + git__free(db); } int git_mempack_new(git_odb_backend **out) 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/openssl_stream.c b/src/openssl_stream.c index 4df7c6b7c..16ee78341 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -302,6 +302,7 @@ cert_fail_name: typedef struct { git_stream parent; git_stream *io; + bool connected; char *host; SSL *ssl; git_cert_x509 cert_info; @@ -318,6 +319,8 @@ int openssl_connect(git_stream *stream) if ((ret = git_stream_connect(st->io)) < 0) return ret; + st->connected = true; + bio = BIO_new(&git_stream_bio_method); GITERR_CHECK_ALLOC(bio); bio->ptr = st->io; @@ -405,9 +408,11 @@ int openssl_close(git_stream *stream) openssl_stream *st = (openssl_stream *) stream; int ret; - if ((ret = ssl_teardown(st->ssl)) < 0) + if (st->connected && (ret = ssl_teardown(st->ssl)) < 0) return -1; + st->connected = false; + return git_stream_close(st->io); } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index e1a77f3ff..792e4bb0a 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1453,7 +1453,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) diff --git a/src/repository.c b/src/repository.c index 08f4baa20..3476ccadc 100644 --- a/src/repository.c +++ b/src/repository.c @@ -908,12 +908,28 @@ bool git_repository__reserved_names( buf->size = git_repository__reserved_names_win32[i].size; } - /* Try to add any repo-specific reserved names */ + /* Try to add any repo-specific reserved names - the gitlink file + * within a submodule or the repository (if the repository directory + * is beneath the workdir). These are typically `.git`, but should + * be protected in case they are not. Note, repo and workdir paths + * are always prettified to end in `/`, so a prefixcmp is safe. + */ if (!repo->is_bare) { - const char *reserved_path = repo->path_gitlink ? - repo->path_gitlink : repo->path_repository; + int (*prefixcmp)(const char *, const char *); + int error, ignorecase; - if (reserved_names_add8dot3(repo, reserved_path) < 0) + error = git_repository__cvar( + &ignorecase, repo, GIT_CVAR_IGNORECASE); + prefixcmp = (error || ignorecase) ? git__prefixcmp_icase : + git__prefixcmp; + + if (repo->path_gitlink && + reserved_names_add8dot3(repo, repo->path_gitlink) < 0) + goto on_error; + + if (repo->path_repository && + prefixcmp(repo->path_repository, repo->workdir) == 0 && + reserved_names_add8dot3(repo, repo->path_repository) < 0) goto on_error; } } 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/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/ssh.c b/src/transports/ssh.c index e3792ebb3..250e588e7 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -756,8 +756,10 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use list = libssh2_userauth_list(session, username, strlen(username)); /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */ - if (list == NULL && !libssh2_userauth_authenticated(session)) + if (list == NULL && !libssh2_userauth_authenticated(session)) { + ssh_error(session, "Failed to retrieve list of SSH authentication methods"); return -1; + } ptr = list; while (ptr) { diff --git a/src/unix/posix.h b/src/unix/posix.h index 777350990..6633689bc 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -22,7 +22,6 @@ typedef int GIT_SOCKET; #define p_stat(p,b) stat(p, b) #define p_utimes(f, t) utimes(f, t) -#define p_futimes(f, t) futimes(f, t) #define p_readlink(a, b, c) readlink(a, b, c) #define p_symlink(o,n) symlink(o, n) @@ -53,4 +52,18 @@ extern char *p_realpath(const char *, char *); #define p_localtime_r(c, r) localtime_r(c, r) #define p_gmtime_r(c, r) gmtime_r(c, r) +#ifdef HAVE_FUTIMENS +GIT_INLINE(int) p_futimes(int f, const struct timeval t[2]) +{ + struct timespec s[2]; + s[0].tv_sec = t[0].tv_sec; + s[0].tv_nsec = t[0].tv_usec * 1000; + s[1].tv_sec = t[1].tv_sec; + s[1].tv_nsec = t[1].tv_usec * 1000; + return futimens(f, s); +} +#else +# define p_futimes futimes +#endif + #endif diff --git a/src/util.c b/src/util.c index c62826420..49d491dd3 100644 --- a/src/util.c +++ b/src/util.c @@ -607,7 +607,7 @@ size_t git__unescape(char *str) return (pos - str); } -#if defined(GIT_WIN32) || defined(BSD) +#if defined(HAVE_QSORT_S) || (defined(HAVE_QSORT_R) && defined(BSD)) typedef struct { git__sort_r_cmp cmp; void *payload; @@ -624,21 +624,16 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp( void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { -#if defined(__MINGW32__) || defined(AMIGA) || \ - defined(__OpenBSD__) || defined(__NetBSD__) || \ - defined(__gnu_hurd__) || defined(__ANDROID_API__) || \ - defined(__sun) || defined(__CYGWIN__) || \ - (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) || \ - (defined(_MSC_VER) && _MSC_VER < 1500) - git__insertsort_r(els, nel, elsize, NULL, cmp, payload); -#elif defined(GIT_WIN32) - git__qsort_r_glue glue = { cmp, payload }; - qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue); -#elif defined(BSD) +#if defined(HAVE_QSORT_R) && defined(BSD) git__qsort_r_glue glue = { cmp, payload }; qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp); -#else +#elif defined(HAVE_QSORT_R) && defined(__GLIBC__) qsort_r(els, nel, elsize, cmp, payload); +#elif defined(HAVE_QSORT_S) + git__qsort_r_glue glue = { cmp, payload }; + qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue); +#else + git__insertsort_r(els, nel, elsize, NULL, cmp, payload); #endif } diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c index 118e8bcc5..40b95c33b 100644 --- a/src/win32/path_w32.c +++ b/src/win32/path_w32.c @@ -198,13 +198,13 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src) /* See if this is an absolute path (beginning with a drive letter) */ if (path__is_absolute(src)) { if (git__utf8_to_16(dest, MAX_PATH, src) < 0) - return -1; + goto on_error; } /* File-prefixed NT-style paths beginning with \\?\ */ else if (path__is_nt_namespace(src)) { /* Skip the NT prefix, the destination already contains it */ if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0) - return -1; + goto on_error; } /* UNC paths */ else if (path__is_unc(src)) { @@ -213,36 +213,43 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src) /* Skip the leading "\\" */ if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0) - return -1; + goto on_error; } /* Absolute paths omitting the drive letter */ else if (src[0] == '\\' || src[0] == '/') { if (path__cwd(dest, MAX_PATH) < 0) - return -1; + goto on_error; if (!path__is_absolute(dest)) { errno = ENOENT; - return -1; + goto on_error; } /* Skip the drive letter specification ("C:") */ if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0) - return -1; + goto on_error; } /* Relative paths */ else { int cwd_len; if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0) - return -1; + goto on_error; dest[cwd_len++] = L'\\'; if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0) - return -1; + goto on_error; } return git_win32_path_canonicalize(out); + +on_error: + /* set windows error code so we can use its error message */ + if (errno == ENAMETOOLONG) + SetLastError(ERROR_FILENAME_EXCED_RANGE); + + return -1; } int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src) diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 504562b0e..e7aa6fc7c 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -146,12 +146,19 @@ static int lstat_w( return git_win32__file_attribute_to_stat(buf, &fdata, path); } - errno = ENOENT; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + errno = EACCES; + break; + default: + errno = ENOENT; + break; + } /* To match POSIX behavior, set ENOTDIR when any of the folders in the * file path is a regular file, otherwise set ENOENT. */ - if (posix_enotdir) { + if (errno == ENOENT && posix_enotdir) { size_t path_len = wcslen(path); /* scan up path until we find an existing item */ diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h index db5d59884..f08f72e16 100644 --- a/src/xdiff/xdiff.h +++ b/src/xdiff/xdiff.h @@ -20,7 +20,7 @@ * */ -#include "util.h" +#include "../util.h" #if !defined(XDIFF_H) #define XDIFF_H diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index be4019822..5680b86df 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -946,7 +946,7 @@ void test_checkout_tree__filemode_preserved_in_index(void) cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0)); - cl_assert_equal_i(0100755, entry->mode); + cl_assert(GIT_PERMS_IS_EXEC(entry->mode)); git_commit_free(commit); @@ -957,7 +957,7 @@ void test_checkout_tree__filemode_preserved_in_index(void) cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0)); - cl_assert_equal_i(0100644, entry->mode); + cl_assert(!GIT_PERMS_IS_EXEC(entry->mode)); git_commit_free(commit); @@ -968,7 +968,18 @@ void test_checkout_tree__filemode_preserved_in_index(void) cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0)); - cl_assert_equal_i(0100755, entry->mode); + cl_assert(GIT_PERMS_IS_EXEC(entry->mode)); + + git_commit_free(commit); + + + /* Finally, check out the text file again and check that the exec bit is cleared */ + cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0)); + cl_assert(!GIT_PERMS_IS_EXEC(entry->mode)); git_commit_free(commit); @@ -976,6 +987,73 @@ void test_checkout_tree__filemode_preserved_in_index(void) git_index_free(index); } +mode_t read_filemode(const char *path) +{ + git_buf fullpath = GIT_BUF_INIT; + struct stat st; + mode_t result; + + git_buf_joinpath(&fullpath, "testrepo", path); + cl_must_pass(p_stat(fullpath.ptr, &st)); + + result = GIT_PERMS_IS_EXEC(st.st_mode) ? + GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB; + + git_buf_free(&fullpath); + + return result; +} + +void test_checkout_tree__filemode_preserved_in_workdir(void) +{ +#ifndef GIT_WIN32 + git_oid executable_oid; + git_commit *commit; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + /* test a freshly added executable */ + cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert(GIT_PERMS_IS_EXEC(read_filemode("executable.txt"))); + + git_commit_free(commit); + + + /* Now start with a commit which has a text file */ + cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt"))); + + git_commit_free(commit); + + + /* And then check out to a commit which converts the text file to an executable */ + cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert(GIT_PERMS_IS_EXEC(read_filemode("a/b.txt"))); + + git_commit_free(commit); + + + /* Finally, check out the text file again and check that the exec bit is cleared */ + cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt"))); + + git_commit_free(commit); +#endif +} + void test_checkout_tree__removes_conflicts(void) { git_oid commit_id; 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/core/filebuf.c b/tests/core/filebuf.c index 3f7dc8569..39d98ff7e 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -151,3 +151,56 @@ void test_core_filebuf__rename_error(void) cl_assert_equal_i(false, git_path_exists(test_lock)); } + +void test_core_filebuf__symlink_follow(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + const char *dir = "linkdir", *source = "linkdir/link"; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(p_mkdir(dir, 0777)); + cl_git_pass(p_symlink("target", source)); + + cl_git_pass(git_filebuf_open(&file, source, 0, 0666)); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_path_exists("linkdir/target.lock")); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert_equal_i(true, git_path_exists("linkdir/target")); + + git_filebuf_cleanup(&file); + + /* The second time around, the target file does exist */ + cl_git_pass(git_filebuf_open(&file, source, 0, 0666)); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_path_exists("linkdir/target.lock")); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert_equal_i(true, git_path_exists("linkdir/target")); + + git_filebuf_cleanup(&file); + cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_core_filebuf__symlink_depth(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + const char *dir = "linkdir", *source = "linkdir/link"; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(p_mkdir(dir, 0777)); + /* Endless loop */ + cl_git_pass(p_symlink("link", source)); + + cl_git_fail(git_filebuf_open(&file, source, 0, 0666)); + + cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES)); +} diff --git a/tests/diff/binary.c b/tests/diff/binary.c index 5298e9ebb..173a5994e 100644 --- a/tests/diff/binary.c +++ b/tests/diff/binary.c @@ -96,7 +96,8 @@ void test_diff_binary__add(void) "Kc${Nk-~s>u4FC%O\n" "\n" \ "literal 0\n" \ - "Hc$@<O00001\n"; + "Hc$@<O00001\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -136,7 +137,8 @@ void test_diff_binary__modify(void) "Mc${NkU}WL~000&M4gdfE\n" \ "\n" \ "literal 3\n" \ - "Kc${Nk-~s>u4FC%O\n"; + "Kc${Nk-~s>u4FC%O\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY; @@ -177,7 +179,8 @@ void test_diff_binary__delete(void) "Hc$@<O00001\n" \ "\n" \ "literal 3\n" \ - "Kc${Nk-~s>u4FC%O\n"; + "Kc${Nk-~s>u4FC%O\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -208,7 +211,8 @@ void test_diff_binary__delta(void) "delta 198\n" \ "zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pr*5}Br~;mqJ$<Jl;sX*mF<MGCYv&*L7AHu\n" \ "zGA1*^gt?gYVN82wTbPO_W)+x<&1+cP;HrPHR>PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \ - "JfH567LIF3FM2!Fd\n"; + "JfH567LIF3FM2!Fd\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -249,7 +253,8 @@ void test_diff_binary__delta_append(void) "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ "\n" \ "delta 7\n" \ - "Oc%18D`@*{63ljhg(E~C7\n"; + "Oc%18D`@*{63ljhg(E~C7\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -314,7 +319,8 @@ void test_diff_binary__index_to_workdir(void) "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ "\n" \ "delta 7\n" \ - "Oc%18D`@*{63ljhg(E~C7\n"; + "Oc%18D`@*{63ljhg(E~C7\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -379,7 +385,8 @@ void test_diff_binary__print_patch_from_diff(void) "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ "\n" \ "delta 7\n" \ - "Oc%18D`@*{63ljhg(E~C7\n"; + "Oc%18D`@*{63ljhg(E~C7\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; 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/odb/sorting.c b/tests/odb/sorting.c index 147a160c8..22d057b3b 100644 --- a/tests/odb/sorting.c +++ b/tests/odb/sorting.c @@ -56,14 +56,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/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 56ec422c3..3fbf412e4 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -154,6 +154,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/revwalk/basic.c b/tests/revwalk/basic.c index 7e50452c9..d8236ce72 100644 --- a/tests/revwalk/basic.c +++ b/tests/revwalk/basic.c @@ -437,3 +437,38 @@ 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, &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..d486bbaf3 100644 --- a/tests/revwalk/mergebase.c +++ b/tests/revwalk/mergebase.c @@ -492,3 +492,22 @@ 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_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/win32/longpath.c b/tests/win32/longpath.c new file mode 100644 index 000000000..6de7d389a --- /dev/null +++ b/tests/win32/longpath.c @@ -0,0 +1,60 @@ +#include "clar_libgit2.h" + +#include "git2/clone.h" +#include "clone.h" +#include "buffer.h" +#include "fileops.h" + +static git_buf path = GIT_BUF_INIT; + +void test_win32_longpath__initialize(void) +{ +#ifdef GIT_WIN32 + const char *base = clar_sandbox_path(); + size_t base_len = strlen(base); + size_t remain = MAX_PATH - base_len; + size_t i; + + git_buf_clear(&path); + git_buf_puts(&path, base); + git_buf_putc(&path, '/'); + + cl_assert(remain < (MAX_PATH - 5)); + + for (i = 0; i < (remain - 5); i++) + git_buf_putc(&path, 'a'); +#endif +} + +void test_win32_longpath__cleanup(void) +{ + git_buf_free(&path); +} + +#ifdef GIT_WIN32 +void assert_name_too_long(void) +{ + const git_error *err; + size_t expected_len, actual_len; + const char *expected_msg; + + err = giterr_last(); + actual_len = strlen(err->message); + + expected_msg = git_win32_get_error_message(ERROR_FILENAME_EXCED_RANGE); + expected_len = strlen(expected_msg); + + /* check the suffix */ + cl_assert_equal_s(expected_msg, err->message + (actual_len - expected_len)); +} +#endif + +void test_win32_longpath__errmsg_on_checkout(void) +{ +#ifdef GIT_WIN32 + git_repository *repo; + + cl_git_fail(git_clone(&repo, cl_fixture("testrepo.git"), path.ptr, NULL)); + assert_name_too_long(); +#endif +} |