diff --git a/CMakeLists.txt b/CMakeLists.txt index c79b2637c..0f9cff575 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) OPTION( VALGRIND "Configure build for valgrind" OFF ) -OPTION( CURL "User curl for HTTP if available" ON) +OPTION( CURL "Use curl for HTTP if available" ON) OPTION( DEBUG_POOL "Enable debug pool allocator" OFF ) IF(DEBUG_POOL) @@ -151,6 +151,10 @@ FUNCTION(TARGET_OS_LIBRARIES target) TARGET_LINK_LIBRARIES(${target} socket nsl) LIST(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl") SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE) + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Haiku") + TARGET_LINK_LIBRARIES(${target} network) + LIST(APPEND LIBGIT2_PC_LIBS "-lnetwork") + SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE) ENDIF() CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT) IF(NEED_LIBRT) @@ -161,6 +165,8 @@ FUNCTION(TARGET_OS_LIBRARIES target) IF(THREADSAFE) TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT}) + LIST(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT}) + SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE) ENDIF() ENDFUNCTION() @@ -280,6 +286,7 @@ ELSE () IF (CURL_FOUND) ADD_DEFINITIONS(-DGIT_CURL) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIRS}) + LINK_DIRECTORIES(${CURL_LIBRARY_DIRS}) LINK_LIBRARIES(${CURL_LIBRARIES}) LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS}) ENDIF() @@ -467,19 +474,21 @@ ELSE () SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") ENDIF () - IF (MINGW) # MinGW always does PIC and complains if we tell it to + IF (MINGW OR MSYS) # MinGW and MSYS always do PIC and complain if we tell them to STRING(REGEX REPLACE "-fPIC" "" CMAKE_SHARED_LIBRARY_C_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}") - # MinGW >= 3.14 uses the C99-style stdio functions - # automatically, but forks like mingw-w64 still want - # us to define this in order to use them - ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1) - ELSEIF (BUILD_SHARED_LIBS) ADD_C_FLAG_IF_SUPPORTED(-fvisibility=hidden) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") ENDIF () + IF (MINGW) + # MinGW >= 3.14 uses the C99-style stdio functions + # automatically, but forks like mingw-w64 still want + # us to define this in order to use them + ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1) + ENDIF () + ADD_C_FLAG_IF_SUPPORTED(-Wdocumentation) ADD_C_FLAG_IF_SUPPORTED(-Wno-missing-field-initializers) ADD_C_FLAG_IF_SUPPORTED(-Wstrict-aliasing=2) diff --git a/README.md b/README.md index 8ea787b3e..b69a18a38 100644 --- a/README.md +++ b/README.md @@ -15,18 +15,30 @@ with any kind of software without having to release its source code. Additionally, the example code has been released to the public domain (see the [separate license](examples/COPYING) for more information). -* Website: [libgit2.github.com](http://libgit2.github.com) -* StackOverflow Tag: [libgit2](http://stackoverflow.com/questions/tagged/libgit2) -* Issues: [GitHub Issues](https://github.com/libgit2/libgit2/issues) (Right here!) -* API documentation: -* IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net. -* Mailing list: The libgit2 mailing list was - traditionally hosted in Librelist but has been deprecated. We encourage you to - [use StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) instead for any questions regarding - the library, or [open an issue](https://github.com/libgit2/libgit2/issues) - on GitHub for bug reports. The mailing list archives are still available at - . +Getting Help +============ +**Join us on Slack** + +Visit [slack.libgit2.org](http://slack.libgit2.org/) to sign up, then join +us in `#libgit2`. If you prefer IRC, you can also point your client to our +slack channel once you've registered. + +**Getting Help** + +If you have questions about the library, please be sure to check out the +[API documentation](http://libgit2.github.com/libgit2/). If you still have +questions, reach out to us on Slack or post a question on +[StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) (with the `libgit2` tag). + +**Reporting Bugs** + +Please open a [GitHub Issue](https://github.com/libgit2/libgit2/issues) and +include as much information as possible. If possible, provide sample code +that illustrates the problem you're seeing. If you're seeing a bug only +on a specific repository, please provide a link to it if possible. + +We ask that you not open a GitHub Issue for help, only for bug reports. What It Can Do ============== @@ -235,16 +247,22 @@ we can add it to the list. How Can I Contribute? ================================== -Check the [contribution guidelines](CONTRIBUTING.md) to understand our -workflow, the libgit2 [coding conventions](CONVENTIONS.md), and our list of -[good starting projects](PROJECTS.md). +We welcome new contributors! We have a number of issues marked as +["up for grabs"](https://github.com/libgit2/libgit2/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grabs%22) +and +["easy fix"](https://github.com/libgit2/libgit2/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3A%22easy+fix%22) +that are good places to jump in and get started. There's much more detailed +information in our list of [outstanding projects](PROJECTS.md). + +Please be sure to check the [contribution guidelines](CONTRIBUTING.md) to +understand our workflow, and the libgit2 [coding conventions](CONVENTIONS.md). License ================================== `libgit2` is under GPL2 **with linking exception**. This means you can link to and use the library from any program, proprietary or open source; paid or -gratis. However, you cannot modify libgit2 and distribute it without -supplying the source. +gratis. However, if you modify libgit2 itself, you must distribute the +source to your modified version of libgit2. See the [COPYING file](COPYING) for the full license text. diff --git a/docs/checkout-internals.md b/docs/checkout-internals.md index 6147ffdd8..e0b2583b5 100644 --- a/docs/checkout-internals.md +++ b/docs/checkout-internals.md @@ -66,6 +66,8 @@ Key - Bi - ignored blob (WD only) - T1,T2,T3 - trees with different SHAs, - Ti - ignored tree (WD only) +- S1,S2 - submodules with different SHAs +- Sd - dirty submodule (WD only) - x - nothing Diff with 2 non-workdir iterators @@ -162,6 +164,27 @@ Checkout From 3 Iterators (2 not workdir, 1 workdir) | 35+ | T1 | T2 | x | update locally deleted tree (SAFE+MISSING) | | 36* | T1 | T2 | B1/Bi | update to tree with typechanged tree->blob conflict (F-1) | | 37 | T1 | T2 | T1/T2/T3 | update to existing tree (MAYBE SAFE) | +| 38+ | x | S1 | x | add submodule (SAFE) | +| 39 | x | S1 | S1/Sd | independently added submodule (SUBMODULE) | +| 40* | x | S1 | B1 | add submodule with blob confilct (FORCEABLE) | +| 41* | x | S1 | T1 | add submodule with tree conflict (FORCEABLE) | +| 42 | S1 | x | S1/Sd | deleted submodule (SUBMODULE) | +| 43 | S1 | x | x | independently deleted submodule (SUBMODULE) | +| 44 | S1 | x | B1 | independently deleted submodule with added blob (SAFE+MISSING) | +| 45 | S1 | x | T1 | independently deleted submodule with added tree (SAFE+MISSING) | +| 46 | S1 | S1 | x | locally deleted submodule (SUBMODULE) | +| 47+ | S1 | S2 | x | update locally deleted submodule (SAFE) | +| 48 | S1 | S1 | S2 | locally updated submodule commit (SUBMODULE) | +| 49 | S1 | S2 | S1 | updated submodule commit (SUBMODULE) | +| 50+ | S1 | B1 | x | add blob with locally deleted submodule (SAFE+MISSING) | +| 51* | S1 | B1 | S1 | typechange submodule->blob (SAFE) | +| 52* | S1 | B1 | Sd | typechange dirty submodule->blob (SAFE!?!?) | +| 53+ | S1 | T1 | x | add tree with locally deleted submodule (SAFE+MISSING) | +| 54* | S1 | T1 | S1/Sd | typechange submodule->tree (MAYBE SAFE) | +| 55+ | B1 | S1 | x | add submodule with locally deleted blob (SAFE+MISSING) | +| 56* | B1 | S1 | B1 | typechange blob->submodule (SAFE) | +| 57+ | T1 | S1 | x | add submodule with locally deleted tree (SAFE+MISSING) | +| 58* | T1 | S1 | T1 | typechange tree->submodule (SAFE) | The number is followed by ' ' if no change is needed or '+' if the case @@ -176,6 +199,8 @@ There are four tiers of safe cases: content, which is unknown at this point * FORCEABLE == conflict unless FORCE is given * DIRTY == no conflict but change is not applied unless FORCE +* SUBMODULE == no conflict and no change is applied unless a deleted + submodule dir is empty Some slightly unusual circumstances: @@ -198,7 +223,9 @@ Some slightly unusual circumstances: cases, if baseline == target, we don't touch the workdir (it is either already right or is "dirty"). However, since this case also implies that a ?/B1/x case will exist as well, it can be skipped. +* 41 - It's not clear how core git distinguishes this case from 39 (mode?). +* 52 - Core git makes destructive changes without any warning when the + submodule is dirty and the type changes to a blob. Cases 3, 17, 24, 26, and 29 are all considered conflicts even though none of them will require making any updates to the working directory. - diff --git a/include/git2/checkout.h b/include/git2/checkout.h index 6cf9ed8bd..4a9dbb021 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -313,6 +313,13 @@ GIT_EXTERN(int) git_checkout_init_options( * Updates files in the index and the working tree to match the content of * the commit pointed at by HEAD. * + * Note that this is _not_ the correct mechanism used to switch branches; + * do not change your `HEAD` and then call this method, that would leave + * you with checkout conflicts since your working directory would then + * appear to be dirty. Instead, checkout the target of the branch and + * then update `HEAD` using `git_repository_set_head` to point to the + * branch you checked out. + * * @param repo repository to check out (must be non-bare) * @param opts specifies checkout options (may be NULL) * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non diff --git a/include/git2/version.h b/include/git2/version.h index 66a6623cd..93b369462 100644 --- a/include/git2/version.h +++ b/include/git2/version.h @@ -7,10 +7,10 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "0.24.0" +#define LIBGIT2_VERSION "0.24.2" #define LIBGIT2_VER_MAJOR 0 #define LIBGIT2_VER_MINOR 24 -#define LIBGIT2_VER_REVISION 0 +#define LIBGIT2_VER_REVISION 2 #define LIBGIT2_VER_PATCH 0 #define LIBGIT2_SOVERSION 24 diff --git a/script/cibuild.sh b/script/cibuild.sh index 00cde0ada..5707f01cb 100755 --- a/script/cibuild.sh +++ b/script/cibuild.sh @@ -6,6 +6,10 @@ then exit $?; fi +if [ "$TRAVIS_OS_NAME" = "osx" ]; then + export PKG_CONFIG_PATH=$(ls -d /usr/local/Cellar/{curl,zlib}/*/lib/pkgconfig | paste -s -d':' -) +fi + mkdir _build cd _build # shellcheck disable=SC2086 diff --git a/script/install-deps-osx.sh b/script/install-deps-osx.sh index 5510379d4..4b8393b19 100755 --- a/script/install-deps-osx.sh +++ b/script/install-deps-osx.sh @@ -3,4 +3,6 @@ set -x brew update +brew install homebrew/dupes/zlib +brew install curl brew install libssh2 diff --git a/src/array.h b/src/array.h index 490e6be20..78d321e82 100644 --- a/src/array.h +++ b/src/array.h @@ -96,7 +96,7 @@ GIT_INLINE(int) git_array__search( { size_t lim; unsigned char *part, *array = array_ptr, *base = array_ptr; - int cmp; + int cmp = -1; for (lim = array_len; lim != 0; lim >>= 1) { part = base + (lim >> 1) * item_size; diff --git a/src/blame_git.c b/src/blame_git.c index 700207edb..96785c75b 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -37,25 +37,27 @@ static void origin_decref(git_blame__origin *o) static int make_origin(git_blame__origin **out, git_commit *commit, const char *path) { git_blame__origin *o; + git_object *blob; size_t path_len = strlen(path), alloc_len; int error = 0; + if ((error = git_object_lookup_bypath(&blob, (git_object*)commit, + path, GIT_OBJ_BLOB)) < 0) + return error; + GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*o), path_len); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1); o = git__calloc(1, alloc_len); GITERR_CHECK_ALLOC(o); o->commit = commit; + o->blob = (git_blob *) blob; o->refcnt = 1; strcpy(o->path, path); - if (!(error = git_object_lookup_bypath((git_object**)&o->blob, (git_object*)commit, - path, GIT_OBJ_BLOB))) { - *out = o; - } else { - origin_decref(o); - } - return error; + *out = o; + + return 0; } /* Locate an existing origin or create a new one. */ @@ -529,8 +531,16 @@ static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) goto finish; porigin = find_origin(blame, p, origin); - if (!porigin) + if (!porigin) { + /* + * We only have to decrement the parent's + * reference count when no porigin has + * been created, as otherwise the commit + * is assigned to the created object. + */ + git_commit_free(p); continue; + } if (porigin->blob && origin->blob && !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) { pass_whole_blame(blame, origin, porigin); diff --git a/src/checkout.c b/src/checkout.c index deeee62e0..1bea00da8 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -464,7 +464,8 @@ static int checkout_action_with_wd( *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); break; case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */ - if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) + if (wd->mode != GIT_FILEMODE_COMMIT && + checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); else *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); @@ -1342,9 +1343,11 @@ fail: static bool should_remove_existing(checkout_data *data) { - int ignorecase = 0; + int ignorecase; - git_repository__cvar(&ignorecase, data->repo, GIT_CVAR_IGNORECASE); + if (git_repository__cvar(&ignorecase, data->repo, GIT_CVAR_IGNORECASE) < 0) { + ignorecase = 0; + } return (ignorecase && (data->strategy & GIT_CHECKOUT_DONT_REMOVE_EXISTING) == 0); @@ -2405,8 +2408,13 @@ static int checkout_data_init( if (!data->opts.baseline && !data->opts.baseline_index) { data->opts_free_baseline = true; + error = 0; - error = checkout_lookup_head_tree(&data->opts.baseline, repo); + /* if we don't have an index, this is an initial checkout and + * should be against an empty baseline + */ + if (data->index->on_disk) + error = checkout_lookup_head_tree(&data->opts.baseline, repo); if (error == GIT_EUNBORNBRANCH) { error = 0; @@ -2691,7 +2699,7 @@ int git_checkout_tree( if ((error = git_repository_index(&index, repo)) < 0) return error; - if ((opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) { + if (opts && (opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) { iter_opts.pathlist.count = opts->paths.count; iter_opts.pathlist.strings = opts->paths.strings; } diff --git a/src/common.h b/src/common.h index 9abd605cb..51fb9186e 100644 --- a/src/common.h +++ b/src/common.h @@ -45,7 +45,7 @@ # include "win32/error.h" # include "win32/version.h" # ifdef GIT_THREADS -# include "win32/pthread.h" +# include "win32/thread.h" # endif # if defined(GIT_MSVC_CRTDBG) # include "win32/w32_stack.h" diff --git a/src/delta-apply.c b/src/delta-apply.c index 89745faa0..02ec7b75e 100644 --- a/src/delta-apply.c +++ b/src/delta-apply.c @@ -49,6 +49,37 @@ int git__delta_read_header( return 0; } +#define DELTA_HEADER_BUFFER_LEN 16 +int git__delta_read_header_fromstream(size_t *base_sz, size_t *res_sz, git_packfile_stream *stream) +{ + static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN; + unsigned char buffer[DELTA_HEADER_BUFFER_LEN]; + const unsigned char *delta, *delta_end; + size_t len; + ssize_t read; + + len = read = 0; + while (len < buffer_len) { + read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len); + + if (read == 0) + break; + + if (read == GIT_EBUFS) + continue; + + len += read; + } + + delta = buffer; + delta_end = delta + len; + if ((hdr_sz(base_sz, &delta, delta_end) < 0) || + (hdr_sz(res_sz, &delta, delta_end) < 0)) + return -1; + + return 0; +} + int git__delta_apply( git_rawobj *out, const unsigned char *base, @@ -90,13 +121,13 @@ int git__delta_apply( size_t off = 0, len = 0; if (cmd & 0x01) off = *delta++; - if (cmd & 0x02) off |= *delta++ << 8; - if (cmd & 0x04) off |= *delta++ << 16; - if (cmd & 0x08) off |= *delta++ << 24; + if (cmd & 0x02) off |= *delta++ << 8UL; + if (cmd & 0x04) off |= *delta++ << 16UL; + if (cmd & 0x08) off |= *delta++ << 24UL; if (cmd & 0x10) len = *delta++; - if (cmd & 0x20) len |= *delta++ << 8; - if (cmd & 0x40) len |= *delta++ << 16; + if (cmd & 0x20) len |= *delta++ << 8UL; + if (cmd & 0x40) len |= *delta++ << 16UL; if (!len) len = 0x10000; if (base_len < off + len || res_sz < len) diff --git a/src/delta-apply.h b/src/delta-apply.h index d7d99d04c..eeeb78682 100644 --- a/src/delta-apply.h +++ b/src/delta-apply.h @@ -8,6 +8,7 @@ #define INCLUDE_delta_apply_h__ #include "odb.h" +#include "pack.h" /** * Apply a git binary delta to recover the original content. @@ -47,4 +48,15 @@ extern int git__delta_read_header( size_t *base_sz, size_t *res_sz); +/** + * Read the header of a git binary delta + * + * This variant reads just enough from the packfile stream to read the + * delta header. + */ +extern int git__delta_read_header_fromstream( + size_t *base_sz, + size_t *res_sz, + git_packfile_stream *stream); + #endif diff --git a/src/filebuf.c b/src/filebuf.c index 101d5082a..507f6a942 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -70,7 +70,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) git_file source; char buffer[FILEIO_BUFSIZE]; ssize_t read_bytes; - int error; + int error = 0; source = p_open(file->path_original, O_RDONLY); if (source < 0) { diff --git a/src/global.c b/src/global.c index adf353d35..198bc1e80 100644 --- a/src/global.c +++ b/src/global.c @@ -59,8 +59,9 @@ static int init_common(void) if ((ret = git_hash_global_init()) == 0 && (ret = git_sysdir_global_init()) == 0 && (ret = git_filter_global_init()) == 0 && - (ret = git_transport_ssh_global_init()) == 0) - ret = git_openssl_stream_global_init(); + (ret = git_transport_ssh_global_init()) == 0 && + (ret = git_openssl_stream_global_init()) == 0) + ret = git_mwindow_global_init(); GIT_MEMORY_BARRIER; @@ -85,11 +86,6 @@ static void shutdown_common(void) git__free(git__user_agent); git__free(git__ssl_ciphers); - -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_cleanup(); - git_win32__stack_cleanup(); -#endif } /** @@ -137,7 +133,7 @@ static int synchronized_threads_init(void) _tls_index = TlsAlloc(); - win32_pthread_initialize(); + git_threads_init(); if (git_mutex_init(&git__mwindow_mutex)) return -1; @@ -181,6 +177,11 @@ int git_libgit2_shutdown(void) TlsFree(_tls_index); git_mutex_free(&git__mwindow_mutex); + +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_cleanup(); + git_win32__stack_cleanup(); +#endif } /* Exit the lock */ @@ -226,6 +227,9 @@ void git__free_tls_data(void) BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved) { + GIT_UNUSED(hInstDll); + GIT_UNUSED(lpvReserved); + /* This is how Windows lets us know our thread is being shut down */ if (fdwReason == DLL_THREAD_DETACH) { git__free_tls_data(); diff --git a/src/ignore.c b/src/ignore.c index ac2af4f58..dcbd5c1ca 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -11,35 +11,64 @@ #define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n" /** - * A negative ignore pattern can match a positive one without - * wildcards if its pattern equals the tail of the positive - * pattern. Thus + * A negative ignore pattern can negate a positive one without + * wildcards if it is a basename only and equals the basename of + * the positive pattern. Thus * * foo/bar * !bar * - * would result in foo/bar being unignored again. + * would result in foo/bar being unignored again while + * + * moo/foo/bar + * !foo/bar + * + * would do nothing. The reverse also holds true: a positive + * basename pattern can be negated by unignoring the basename in + * subdirectories. Thus + * + * bar + * !foo/bar + * + * would result in foo/bar being unignored again. As with the + * first case, + * + * foo/bar + * !moo/foo/bar + * + * would do nothing, again. */ static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg) { + git_attr_fnmatch *longer, *shorter; char *p; if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0 && (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) { - /* - * no chance of matching if rule is shorter than - * the negated one - */ - if (rule->length < neg->length) + + /* If lengths match we need to have an exact match */ + if (rule->length == neg->length) { + return strcmp(rule->pattern, neg->pattern) == 0; + } else if (rule->length < neg->length) { + shorter = rule; + longer = neg; + } else { + shorter = neg; + longer = rule; + } + + /* Otherwise, we need to check if the shorter + * rule is a basename only (that is, it contains + * no path separator) and, if so, if it + * matches the tail of the longer rule */ + p = longer->pattern + longer->length - shorter->length; + + if (p[-1] != '/') + return false; + if (memchr(shorter->pattern, '/', shorter->length) != NULL) return false; - /* - * shift pattern so its tail aligns with the - * negated pattern - */ - p = rule->pattern + rule->length - neg->length; - if (strcmp(p, neg->pattern) == 0) - return true; + return memcmp(p, shorter->pattern, shorter->length) == 0; } return false; diff --git a/src/index.c b/src/index.c index 63e47965a..32f585faf 100644 --- a/src/index.c +++ b/src/index.c @@ -505,10 +505,11 @@ static int index_remove_entry(git_index *index, size_t pos) int error = 0; git_index_entry *entry = git_vector_get(&index->entries, pos); - if (entry != NULL) + if (entry != NULL) { git_tree_cache_invalidate_path(index->tree, entry->path); + DELETE_IN_MAP(index, entry); + } - DELETE_IN_MAP(index, entry); error = git_vector_remove(&index->entries, pos); if (!error) { @@ -2968,6 +2969,8 @@ int git_index_read_index( *remove_entry = NULL; int diff; + error = 0; + if (old_entry && new_entry) diff = git_index_entry_cmp(old_entry, new_entry); else if (!old_entry && new_entry) @@ -2985,7 +2988,8 @@ int git_index_read_index( /* 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)) { + if (git_oid_equal(&old_entry->id, &new_entry->id) && + old_entry->mode == new_entry->mode) { add_entry = (git_index_entry *)old_entry; } else { dup_entry = (git_index_entry *)new_entry; @@ -2996,8 +3000,17 @@ int git_index_read_index( if (dup_entry) { if ((error = index_entry_dup_nocache(&add_entry, index, dup_entry)) < 0) goto done; + + index_entry_adjust_namemask(add_entry, + ((struct entry_internal *)add_entry)->pathlen); } + /* invalidate this path in the tree cache if this is new (to + * invalidate the parent trees) + */ + if (dup_entry && !remove_entry && index->tree) + git_tree_cache_invalidate_path(index->tree, dup_entry->path); + if (add_entry) { if ((error = git_vector_insert(&new_entries, add_entry)) == 0) INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error); @@ -3008,7 +3021,7 @@ int git_index_read_index( if (error < 0) { giterr_set(GITERR_INDEX, "failed to insert entry"); - return error; + goto done; } if (diff <= 0) { diff --git a/src/merge.c b/src/merge.c index d2f92ccce..174cbfb58 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2730,6 +2730,7 @@ static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_inde opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH; opts.pathspec.count = merged_paths->length; opts.pathspec.strings = (char **)merged_paths->contents; + opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL; if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, NULL, &opts)) < 0) goto done; diff --git a/src/mwindow.c b/src/mwindow.c index d3e9be78b..8a5b5caee 100644 --- a/src/mwindow.c +++ b/src/mwindow.c @@ -33,20 +33,7 @@ static git_mwindow_ctl mem_ctl; /* Global list of mwindow files, to open packs once across repos */ git_strmap *git__pack_cache = NULL; -/** - * Run under mwindow lock - */ -int git_mwindow_files_init(void) -{ - if (git__pack_cache) - return 0; - - git__on_shutdown(git_mwindow_files_free); - - return git_strmap_alloc(&git__pack_cache); -} - -void git_mwindow_files_free(void) +static void git_mwindow_files_free(void) { git_strmap *tmp = git__pack_cache; @@ -54,6 +41,14 @@ void git_mwindow_files_free(void) git_strmap_free(tmp); } +int git_mwindow_global_init(void) +{ + assert(!git__pack_cache); + + git__on_shutdown(git_mwindow_files_free); + return git_strmap_alloc(&git__pack_cache); +} + int git_mwindow_get_pack(struct git_pack_file **out, const char *path) { int error; @@ -69,12 +64,6 @@ int git_mwindow_get_pack(struct git_pack_file **out, const char *path) return -1; } - if (git_mwindow_files_init() < 0) { - git_mutex_unlock(&git__mwindow_mutex); - git__free(packname); - return -1; - } - pos = git_strmap_lookup_index(git__pack_cache, packname); git__free(packname); diff --git a/src/mwindow.h b/src/mwindow.h index 63418e458..bdde9e0a4 100644 --- a/src/mwindow.h +++ b/src/mwindow.h @@ -43,8 +43,7 @@ int git_mwindow_file_register(git_mwindow_file *mwf); void git_mwindow_file_deregister(git_mwindow_file *mwf); void git_mwindow_close(git_mwindow **w_cursor); -int git_mwindow_files_init(void); -void git_mwindow_files_free(void); +extern int git_mwindow_global_init(void); struct git_pack_file; /* just declaration to avoid cyclical includes */ int git_mwindow_get_pack(struct git_pack_file **out, const char *path); diff --git a/src/odb.c b/src/odb.c index cb0f70623..1ecfe937f 100644 --- a/src/odb.c +++ b/src/odb.c @@ -803,19 +803,12 @@ int git_odb__read_header_or_object( return 0; } -static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b, - 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }}; static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }}; static int hardcoded_objects(git_rawobj *raw, const git_oid *id) { - if (!git_oid_cmp(id, &empty_blob)) { - raw->type = GIT_OBJ_BLOB; - raw->len = 0; - raw->data = git__calloc(1, sizeof(uint8_t)); - return 0; - } else if (!git_oid_cmp(id, &empty_tree)) { + if (!git_oid_cmp(id, &empty_tree)) { raw->type = GIT_OBJ_TREE; raw->len = 0; raw->data = git__calloc(1, sizeof(uint8_t)); @@ -1229,7 +1222,7 @@ int git_odb__error_notfound( { if (oid != NULL) { char oid_str[GIT_OID_HEXSZ + 1]; - git_oid_tostr(oid_str, oid_len, oid); + git_oid_tostr(oid_str, oid_len+1, oid); giterr_set(GITERR_ODB, "Object not found - %s (%.*s)", message, oid_len, oid_str); } else diff --git a/src/odb_loose.c b/src/odb_loose.c index 9d9bffd21..3c33160d0 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -91,7 +91,7 @@ static int object_mkdir(const git_buf *name, const loose_backend *be) static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj) { - unsigned char c; + unsigned long c; unsigned char *data = (unsigned char *)obj->ptr; size_t shift, size, used = 0; diff --git a/src/odb_pack.c b/src/odb_pack.c index 5a57864ad..0764207e5 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -591,9 +591,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) struct pack_backend *backend = NULL; git_buf path = GIT_BUF_INIT; - if (git_mwindow_files_init() < 0) - return -1; - if (pack_backend__alloc(&backend, 8) < 0) return -1; diff --git a/src/openssl_stream.c b/src/openssl_stream.c index a65f5586e..9d97bae00 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -522,8 +522,9 @@ ssize_t openssl_read(git_stream *stream, void *data, size_t len) openssl_stream *st = (openssl_stream *) stream; int ret; - if ((ret = SSL_read(st->ssl, data, len)) <= 0) - ssl_set_error(st->ssl, ret); + if ((ret = SSL_read(st->ssl, data, len)) <= 0) { + return ssl_set_error(st->ssl, ret); + } return ret; } diff --git a/src/pack-objects.c b/src/pack-objects.c index 11e13f7d4..29231e028 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -1186,7 +1186,7 @@ static int ll_find_deltas(git_packbuilder *pb, git_pobject **list, git_mutex_init(&p[i].mutex); git_cond_init(&p[i].cond); - ret = git_thread_create(&p[i].thread, NULL, + ret = git_thread_create(&p[i].thread, threaded_find_deltas, &p[i]); if (ret) { giterr_set(GITERR_THREAD, "unable to create thread"); diff --git a/src/pack.c b/src/pack.c index e7003e66d..6a700e29f 100644 --- a/src/pack.c +++ b/src/pack.c @@ -499,15 +499,14 @@ int git_packfile_resolve_header( if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) { size_t base_size; - git_rawobj delta; + git_packfile_stream stream; + base_offset = get_delta_base(p, &w_curs, &curpos, type, offset); git_mwindow_close(&w_curs); - error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, size, type); - git_mwindow_close(&w_curs); - if (error < 0) + if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0) return error; - error = git__delta_read_header(delta.data, delta.len, &base_size, size_p); - git__free(delta.data); + error = git__delta_read_header_fromstream(&base_size, size_p, &stream); + git_packfile_stream_free(&stream); if (error < 0) return error; } else diff --git a/src/refspec.c b/src/refspec.c index debde8692..d200e5609 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -53,8 +53,10 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) if (rhs) { size_t rlen = strlen(++rhs); - is_glob = (1 <= rlen && strchr(rhs, '*')); - refspec->dst = git__strndup(rhs, rlen); + if (rlen || !is_fetch) { + is_glob = (1 <= rlen && strchr(rhs, '*')); + refspec->dst = git__strndup(rhs, rlen); + } } llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs)); diff --git a/src/remote.c b/src/remote.c index 8b7203ee2..15a00dd28 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1414,7 +1414,11 @@ static int update_tips_for_spec( /* In autotag mode, don't overwrite any locally-existing tags */ error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, log_message); - if (error < 0 && error != GIT_EEXISTS) + + if (error == GIT_EEXISTS) + continue; + + if (error < 0) goto on_error; git_reference_free(ref); @@ -2224,15 +2228,21 @@ static int remove_branch_config_related_entries( if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0) break; - if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) - break; + if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) { + if (error != GIT_ENOTFOUND) + break; + giterr_clear(); + } git_buf_clear(&buf); if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0) break; - if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) - break; + if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) { + if (error != GIT_ENOTFOUND) + break; + giterr_clear(); + } } if (error == GIT_ITEROVER) diff --git a/src/repository.c b/src/repository.c index 8a6fef0f6..c19790a8c 100644 --- a/src/repository.c +++ b/src/repository.c @@ -264,7 +264,7 @@ cleanup: * the stack could remove directories name limits, but at the cost of doing * repeated malloc/frees inside the loop below, so let's not do it now. */ -static int find_ceiling_dir_offset( +static size_t find_ceiling_dir_offset( const char *path, const char *ceiling_directories) { @@ -278,7 +278,7 @@ static int find_ceiling_dir_offset( min_len = (size_t)(git_path_root(path) + 1); if (ceiling_directories == NULL || min_len == 0) - return (int)min_len; + return min_len; for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) { for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++); @@ -305,7 +305,7 @@ static int find_ceiling_dir_offset( } } - return (int)(max_len <= min_len ? min_len : max_len); + return (max_len <= min_len ? min_len : max_len); } /* @@ -359,21 +359,36 @@ static int find_repo( git_buf path = GIT_BUF_INIT; struct stat st; dev_t initial_device = 0; - bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0); - int ceiling_offset; + int min_iterations; + bool in_dot_git; + size_t ceiling_offset = 0; git_buf_free(repo_path); if ((error = git_path_prettify(&path, start_path, NULL)) < 0) return error; - ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs); + /* in_dot_git toggles each loop: + * /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a + * With GIT_REPOSITORY_OPEN_BARE, we assume we started with /a/b/c.git + * and don't append .git the first time through. + * min_iterations indicates the number of iterations left before going + * further counts as a search. */ + if (flags & GIT_REPOSITORY_OPEN_BARE) { + in_dot_git = true; + min_iterations = 1; + } else { + in_dot_git = false; + min_iterations = 2; + } - if (!try_with_dot_git && - (error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) - return error; + while (!error && (min_iterations || !(path.ptr[ceiling_offset] == 0 || + (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))) { + if (!in_dot_git) + if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) + break; + in_dot_git = !in_dot_git; - while (!error && !git_buf_len(repo_path)) { if (p_stat(path.ptr, &st) == 0) { /* check that we have not crossed device boundaries */ if (initial_device == 0) @@ -414,17 +429,10 @@ static int find_repo( break; } - if (try_with_dot_git) { - /* if we tried original dir with and without .git AND either hit - * directory ceiling or NO_SEARCH was requested, then be done. - */ - if (path.ptr[ceiling_offset] == '\0' || - (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0) - break; - /* otherwise look first for .git item */ - error = git_buf_joinpath(&path, path.ptr, DOT_GIT); - } - try_with_dot_git = !try_with_dot_git; + /* Once we've checked the directory (and .git if applicable), + * find the ceiling for a search. */ + if (min_iterations && (--min_iterations == 0)) + ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs); } if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { diff --git a/src/stransport_stream.c b/src/stransport_stream.c index 33b6c5c38..9d87d6029 100644 --- a/src/stransport_stream.c +++ b/src/stransport_stream.c @@ -16,7 +16,7 @@ #include "socket_stream.h" #include "curl_stream.h" -int stransport_error(OSStatus ret) +static int stransport_error(OSStatus ret) { CFStringRef message; @@ -33,6 +33,7 @@ int stransport_error(OSStatus ret) CFRelease(message); #else giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret); + GIT_UNUSED(message); #endif return -1; @@ -46,7 +47,7 @@ typedef struct { git_cert_x509 cert_info; } stransport_stream; -int stransport_connect(git_stream *stream) +static int stransport_connect(git_stream *stream) { stransport_stream *st = (stransport_stream *) stream; int error; @@ -66,6 +67,9 @@ int stransport_connect(git_stream *stream) if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr) goto on_error; + if (!trust) + return GIT_ECERTIFICATE; + if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr) goto on_error; @@ -89,7 +93,7 @@ on_error: return stransport_error(ret); } -int stransport_certificate(git_cert **out, git_stream *stream) +static int stransport_certificate(git_cert **out, git_stream *stream) { stransport_stream *st = (stransport_stream *) stream; SecTrustRef trust = NULL; @@ -116,7 +120,7 @@ int stransport_certificate(git_cert **out, git_stream *stream) return 0; } -int stransport_set_proxy(git_stream *stream, const char *proxy) +static int stransport_set_proxy(git_stream *stream, const char *proxy) { stransport_stream *st = (stransport_stream *) stream; @@ -146,7 +150,7 @@ static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len) return noErr; } -ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags) +static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags) { stransport_stream *st = (stransport_stream *) stream; size_t data_len, processed; @@ -195,7 +199,7 @@ static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len) return error; } -ssize_t stransport_read(git_stream *stream, void *data, size_t len) +static ssize_t stransport_read(git_stream *stream, void *data, size_t len) { stransport_stream *st = (stransport_stream *) stream; size_t processed; @@ -207,7 +211,7 @@ ssize_t stransport_read(git_stream *stream, void *data, size_t len) return processed; } -int stransport_close(git_stream *stream) +static int stransport_close(git_stream *stream) { stransport_stream *st = (stransport_stream *) stream; OSStatus ret; @@ -219,7 +223,7 @@ int stransport_close(git_stream *stream) return git_stream_close(st->io); } -void stransport_free(git_stream *stream) +static void stransport_free(git_stream *stream) { stransport_stream *st = (stransport_stream *) stream; @@ -255,6 +259,7 @@ int git_stransport_stream_new(git_stream **out, const char *host, const char *po st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); if (!st->ctx) { giterr_set(GITERR_NET, "failed to create SSL context"); + git__free(st); return -1; } @@ -264,7 +269,8 @@ int git_stransport_stream_new(git_stream **out, const char *host, const char *po (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr || (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr || (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) { - git_stream_free((git_stream *)st); + CFRelease(st->ctx); + git__free(st); return stransport_error(ret); } diff --git a/src/sysdir.c b/src/sysdir.c index bf53d830f..29e53e239 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -83,45 +83,43 @@ static int git_sysdir_guess_template_dirs(git_buf *out) #endif } -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 }; - -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, +struct git_sysdir__dir { + git_buf buf; + int (*guess)(git_buf *out); }; -static int git_sysdir__dirs_shutdown_set = 0; +static struct git_sysdir__dir git_sysdir__dirs[] = { + { GIT_BUF_INIT, git_sysdir_guess_system_dirs }, + { GIT_BUF_INIT, git_sysdir_guess_global_dirs }, + { GIT_BUF_INIT, git_sysdir_guess_xdg_dirs }, + { GIT_BUF_INIT, git_sysdir_guess_programdata_dirs }, + { GIT_BUF_INIT, git_sysdir_guess_template_dirs }, +}; + +static void git_sysdir_global_shutdown(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(git_sysdir__dirs); ++i) + git_buf_free(&git_sysdir__dirs[i].buf); +} int git_sysdir_global_init(void) { - git_sysdir_t i; - const git_buf *path; + size_t i; int error = 0; - for (i = 0; !error && i < GIT_SYSDIR__MAX; i++) - error = git_sysdir_get(&path, i); + for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); i++) + error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); + + git__on_shutdown(git_sysdir_global_shutdown); return error; } -void git_sysdir_global_shutdown(void) -{ - int i; - for (i = 0; i < GIT_SYSDIR__MAX; ++i) - git_buf_free(&git_sysdir__dirs[i]); - - git_sysdir__dirs_shutdown_set = 0; -} - static int git_sysdir_check_selector(git_sysdir_t which) { - if (which < GIT_SYSDIR__MAX) + if (which < ARRAY_SIZE(git_sysdir__dirs)) return 0; giterr_set(GITERR_INVALID, "config directory selector out of range"); @@ -137,18 +135,7 @@ int git_sysdir_get(const git_buf **out, git_sysdir_t which) GITERR_CHECK_ERROR(git_sysdir_check_selector(which)); - if (!git_buf_len(&git_sysdir__dirs[which])) { - /* prepare shutdown if we're going to need it */ - if (!git_sysdir__dirs_shutdown_set) { - git__on_shutdown(git_sysdir_global_shutdown); - git_sysdir__dirs_shutdown_set = 1; - } - - GITERR_CHECK_ERROR( - git_sysdir__dir_guess[which](&git_sysdir__dirs[which])); - } - - *out = &git_sysdir__dirs[which]; + *out = &git_sysdir__dirs[which].buf; return 0; } @@ -183,31 +170,38 @@ int git_sysdir_set(git_sysdir_t which, const char *search_path) if (search_path != NULL) expand_path = strstr(search_path, PATH_MAGIC); - /* init with default if not yet done and needed (ignoring error) */ - if ((!search_path || expand_path) && - !git_buf_len(&git_sysdir__dirs[which])) - git_sysdir__dir_guess[which](&git_sysdir__dirs[which]); + /* reset the default if this path has been cleared */ + if (!search_path || expand_path) + git_sysdir__dirs[which].guess(&git_sysdir__dirs[which].buf); /* if $PATH is not referenced, then just set the path */ - if (!expand_path) - return git_buf_sets(&git_sysdir__dirs[which], search_path); + if (!expand_path) { + if (search_path) + git_buf_sets(&git_sysdir__dirs[which].buf, search_path); + + goto done; + } /* otherwise set to join(before $PATH, old value, after $PATH) */ if (expand_path > search_path) git_buf_set(&merge, search_path, expand_path - search_path); - if (git_buf_len(&git_sysdir__dirs[which])) + if (git_buf_len(&git_sysdir__dirs[which].buf)) git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, - merge.ptr, git_sysdir__dirs[which].ptr); + merge.ptr, git_sysdir__dirs[which].buf.ptr); expand_path += strlen(PATH_MAGIC); if (*expand_path) git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path); - git_buf_swap(&git_sysdir__dirs[which], &merge); + git_buf_swap(&git_sysdir__dirs[which].buf, &merge); git_buf_free(&merge); - return git_buf_oom(&git_sysdir__dirs[which]) ? -1 : 0; +done: + if (git_buf_oom(&git_sysdir__dirs[which].buf)) + return -1; + + return 0; } static int git_sysdir_find_in_dirlist( diff --git a/src/sysdir.h b/src/sysdir.h index 12874fc85..11878981c 100644 --- a/src/sysdir.h +++ b/src/sysdir.h @@ -103,9 +103,4 @@ extern int git_sysdir_get_str(char *out, size_t outlen, git_sysdir_t which); */ extern int git_sysdir_set(git_sysdir_t which, const char *paths); -/** - * Free the configuration file search paths. - */ -extern void git_sysdir_global_shutdown(void); - #endif /* INCLUDE_sysdir_h__ */ diff --git a/src/tag.c b/src/tag.c index c4bce1f22..fe840fe82 100644 --- a/src/tag.c +++ b/src/tag.c @@ -137,8 +137,14 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) tag->message = NULL; if (buffer < buffer_end) { - if( *buffer != '\n' ) - return tag_error("No new line before message"); + /* If we're not at the end of the header, search for it */ + if( *buffer != '\n' ) { + search = strstr(buffer, "\n\n"); + if (search) + buffer = search + 1; + else + return tag_error("tag contains no message"); + } text_len = buffer_end - ++buffer; diff --git a/src/thread-utils.h b/src/thread-utils.h index 14c8a41ff..f0161989f 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -40,58 +40,12 @@ typedef git_atomic git_atomic_ssize; #ifdef GIT_THREADS -#if !defined(GIT_WIN32) - -typedef struct { - pthread_t thread; -} git_thread; - -#define git_thread_create(git_thread_ptr, attr, start_routine, arg) \ - pthread_create(&(git_thread_ptr)->thread, attr, start_routine, arg) -#define git_thread_join(git_thread_ptr, status) \ - pthread_join((git_thread_ptr)->thread, status) - +#ifdef GIT_WIN32 +# include "win32/thread.h" +#else +# include "unix/pthread.h" #endif -/* Pthreads Mutex */ -#define git_mutex pthread_mutex_t -#define git_mutex_init(a) pthread_mutex_init(a, NULL) -#define git_mutex_lock(a) pthread_mutex_lock(a) -#define git_mutex_unlock(a) pthread_mutex_unlock(a) -#define git_mutex_free(a) pthread_mutex_destroy(a) - -/* Pthreads condition vars */ -#define git_cond pthread_cond_t -#define git_cond_init(c) pthread_cond_init(c, NULL) -#define git_cond_free(c) pthread_cond_destroy(c) -#define git_cond_wait(c, l) pthread_cond_wait(c, l) -#define git_cond_signal(c) pthread_cond_signal(c) -#define git_cond_broadcast(c) pthread_cond_broadcast(c) - -/* Pthread (-ish) rwlock - * - * This differs from normal pthreads rwlocks in two ways: - * 1. Separate APIs for releasing read locks and write locks (as - * opposed to the pure POSIX API which only has one unlock fn) - * 2. You should not use recursive read locks (i.e. grabbing a read - * lock in a thread that already holds a read lock) because the - * Windows implementation doesn't support it - */ -#define git_rwlock pthread_rwlock_t -#define git_rwlock_init(a) pthread_rwlock_init(a, NULL) -#define git_rwlock_rdlock(a) pthread_rwlock_rdlock(a) -#define git_rwlock_rdunlock(a) pthread_rwlock_rdunlock(a) -#define git_rwlock_wrlock(a) pthread_rwlock_wrlock(a) -#define git_rwlock_wrunlock(a) pthread_rwlock_wrunlock(a) -#define git_rwlock_free(a) pthread_rwlock_destroy(a) -#define GIT_RWLOCK_STATIC_INIT PTHREAD_RWLOCK_INITIALIZER - -#ifndef GIT_WIN32 -#define pthread_rwlock_rdunlock pthread_rwlock_unlock -#define pthread_rwlock_wrunlock pthread_rwlock_unlock -#endif - - GIT_INLINE(void) git_atomic_set(git_atomic *a, int val) { #if defined(GIT_WIN32) @@ -178,7 +132,7 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) #else #define git_thread unsigned int -#define git_thread_create(thread, attr, start_routine, arg) 0 +#define git_thread_create(thread, start_routine, arg) 0 #define git_thread_join(id, status) (void)0 /* Pthreads Mutex */ diff --git a/src/transports/http.c b/src/transports/http.c index 88b124bf7..f0efd956a 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -114,7 +114,7 @@ static bool challenge_match(git_http_auth_scheme *scheme, void *data) size_t scheme_len; scheme_len = strlen(scheme_name); - return (strncmp(challenge, scheme_name, scheme_len) == 0 && + return (strncasecmp(challenge, scheme_name, scheme_len) == 0 && (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' ')); } @@ -569,6 +569,7 @@ static int http_connect(http_subtransport *t) git_stream_close(t->io); git_stream_free(t->io); t->io = NULL; + t->connected = 0; } if (t->connection_data.use_ssl) { diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index 2ea57bb64..2297cc94f 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -433,6 +433,7 @@ int git_pkt_parse_line( * line? */ if (len == PKT_LEN_SIZE) { + *head = NULL; *out = line; return 0; } diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 02e1ecf74..3448fa7fb 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -759,6 +759,14 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, line_len -= (line_end - line); line = line_end; + /* When a valid packet with no content has been + * read, git_pkt_parse_line does not report an + * error, but the pkt pointer has not been set. + * Handle this by skipping over empty packets. + */ + if (pkt == NULL) + continue; + error = add_push_report_pkt(push, pkt); git_pkt_free(pkt); @@ -813,6 +821,9 @@ static int parse_report(transport_smart *transport, git_push *push) error = 0; + if (pkt == NULL) + continue; + switch (pkt->type) { case GIT_PKT_DATA: /* This is a sideband packet which contains other packets */ diff --git a/src/tree.c b/src/tree.c index 6ce460c6d..3874e45f4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -45,7 +45,7 @@ GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode) if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_COMMIT) return GIT_FILEMODE_COMMIT; - /* 12XXXX means commit */ + /* 12XXXX means symlink */ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_LINK) return GIT_FILEMODE_LINK; diff --git a/src/unix/pthread.h b/src/unix/pthread.h new file mode 100644 index 000000000..0f3f17927 --- /dev/null +++ b/src/unix/pthread.h @@ -0,0 +1,54 @@ +/* + * 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_unix_pthread_h__ +#define INCLUDE_unix_pthread_h__ + +typedef struct { + pthread_t thread; +} git_thread; + +#define git_threads_init() (void)0 +#define git_thread_create(git_thread_ptr, start_routine, arg) \ + pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg) +#define git_thread_join(git_thread_ptr, status) \ + pthread_join((git_thread_ptr)->thread, status) + +/* Git Mutex */ +#define git_mutex pthread_mutex_t +#define git_mutex_init(a) pthread_mutex_init(a, NULL) +#define git_mutex_lock(a) pthread_mutex_lock(a) +#define git_mutex_unlock(a) pthread_mutex_unlock(a) +#define git_mutex_free(a) pthread_mutex_destroy(a) + +/* Git condition vars */ +#define git_cond pthread_cond_t +#define git_cond_init(c) pthread_cond_init(c, NULL) +#define git_cond_free(c) pthread_cond_destroy(c) +#define git_cond_wait(c, l) pthread_cond_wait(c, l) +#define git_cond_signal(c) pthread_cond_signal(c) +#define git_cond_broadcast(c) pthread_cond_broadcast(c) + +/* Pthread (-ish) rwlock + * + * This differs from normal pthreads rwlocks in two ways: + * 1. Separate APIs for releasing read locks and write locks (as + * opposed to the pure POSIX API which only has one unlock fn) + * 2. You should not use recursive read locks (i.e. grabbing a read + * lock in a thread that already holds a read lock) because the + * Windows implementation doesn't support it + */ +#define git_rwlock pthread_rwlock_t +#define git_rwlock_init(a) pthread_rwlock_init(a, NULL) +#define git_rwlock_rdlock(a) pthread_rwlock_rdlock(a) +#define git_rwlock_rdunlock(a) pthread_rwlock_unlock(a) +#define git_rwlock_wrlock(a) pthread_rwlock_wrlock(a) +#define git_rwlock_wrunlock(a) pthread_rwlock_unlock(a) +#define git_rwlock_free(a) pthread_rwlock_destroy(a) +#define GIT_RWLOCK_STATIC_INIT PTHREAD_RWLOCK_INITIALIZER + +#endif /* INCLUDE_unix_pthread_h__ */ diff --git a/src/util.c b/src/util.c index 9e67f4347..cc4b43241 100644 --- a/src/util.c +++ b/src/util.c @@ -122,8 +122,8 @@ int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int ba v = c - 'A' + 10; if (v >= base) break; - nn = n*base + v; - if (nn < n) + nn = n * base + (neg ? -v : v); + if ((!neg && nn < n) || (neg && nn > n)) ovfl = 1; n = nn; } @@ -142,7 +142,7 @@ Return: return -1; } - *result = neg ? -n : n; + *result = n; return 0; } diff --git a/src/win32/precompiled.h b/src/win32/precompiled.h index 33ce106d3..10ca0b80c 100644 --- a/src/win32/precompiled.h +++ b/src/win32/precompiled.h @@ -16,7 +16,7 @@ #include #include #ifdef GIT_THREADS - #include "win32/pthread.h" + #include "win32/thread.h" #endif #include "git2.h" diff --git a/src/win32/pthread.h b/src/win32/pthread.h deleted file mode 100644 index e4826ca7f..000000000 --- a/src/win32/pthread.h +++ /dev/null @@ -1,92 +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 GIT_PTHREAD_H -#define GIT_PTHREAD_H - -#include "../common.h" - -#if defined (_MSC_VER) -# define GIT_RESTRICT __restrict -#else -# define GIT_RESTRICT __restrict__ -#endif - -typedef struct { - HANDLE thread; - void *(*proc)(void *); - void *param; - void *result; -} git_win32_thread; - -typedef int pthread_mutexattr_t; -typedef int pthread_condattr_t; -typedef int pthread_attr_t; -typedef int pthread_rwlockattr_t; - -typedef CRITICAL_SECTION pthread_mutex_t; -typedef HANDLE pthread_cond_t; - -typedef struct { void *Ptr; } GIT_SRWLOCK; - -typedef struct { - union { - GIT_SRWLOCK srwl; - CRITICAL_SECTION csec; - } native; -} pthread_rwlock_t; - -#define PTHREAD_MUTEX_INITIALIZER {(void*)-1} - -int git_win32__thread_create( - git_win32_thread *GIT_RESTRICT, - const pthread_attr_t *GIT_RESTRICT, - void *(*) (void *), - void *GIT_RESTRICT); - -int git_win32__thread_join( - git_win32_thread *, - void **); - -#ifdef GIT_THREADS - -typedef git_win32_thread git_thread; - -#define git_thread_create(git_thread_ptr, attr, start_routine, arg) \ - git_win32__thread_create(git_thread_ptr, attr, start_routine, arg) -#define git_thread_join(git_thread_ptr, status) \ - git_win32__thread_join(git_thread_ptr, status) - -#endif - -int pthread_mutex_init( - pthread_mutex_t *GIT_RESTRICT mutex, - const pthread_mutexattr_t *GIT_RESTRICT mutexattr); -int pthread_mutex_destroy(pthread_mutex_t *); -int pthread_mutex_lock(pthread_mutex_t *); -int pthread_mutex_unlock(pthread_mutex_t *); - -int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); -int pthread_cond_destroy(pthread_cond_t *); -int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); -int pthread_cond_signal(pthread_cond_t *); -/* pthread_cond_broadcast is not supported on Win32 yet. */ - -int pthread_num_processors_np(void); - -int pthread_rwlock_init( - pthread_rwlock_t *GIT_RESTRICT lock, - const pthread_rwlockattr_t *GIT_RESTRICT attr); -int pthread_rwlock_rdlock(pthread_rwlock_t *); -int pthread_rwlock_rdunlock(pthread_rwlock_t *); -int pthread_rwlock_wrlock(pthread_rwlock_t *); -int pthread_rwlock_wrunlock(pthread_rwlock_t *); -int pthread_rwlock_destroy(pthread_rwlock_t *); - -extern int win32_pthread_initialize(void); - -#endif diff --git a/src/win32/pthread.c b/src/win32/thread.c similarity index 72% rename from src/win32/pthread.c rename to src/win32/thread.c index a1cc18932..80d56ce5d 100644 --- a/src/win32/pthread.c +++ b/src/win32/thread.c @@ -5,18 +5,26 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "pthread.h" +#include "thread.h" #include "../global.h" #define CLEAN_THREAD_EXIT 0x6F012842 +typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *); + +static win32_srwlock_fn win32_srwlock_initialize; +static win32_srwlock_fn win32_srwlock_acquire_shared; +static win32_srwlock_fn win32_srwlock_release_shared; +static win32_srwlock_fn win32_srwlock_acquire_exclusive; +static win32_srwlock_fn win32_srwlock_release_exclusive; + /* The thread procedure stub used to invoke the caller's procedure * and capture the return value for later collection. Windows will * only hold a DWORD, but we need to be able to store an entire * void pointer. This requires the indirection. */ static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter) { - git_win32_thread *thread = lpParameter; + git_thread *thread = lpParameter; thread->result = thread->proc(thread->param); @@ -25,14 +33,31 @@ static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter) return CLEAN_THREAD_EXIT; } -int git_win32__thread_create( - git_win32_thread *GIT_RESTRICT thread, - const pthread_attr_t *GIT_RESTRICT attr, +int git_threads_init(void) +{ + HMODULE hModule = GetModuleHandleW(L"kernel32"); + + if (hModule) { + win32_srwlock_initialize = (win32_srwlock_fn) + GetProcAddress(hModule, "InitializeSRWLock"); + win32_srwlock_acquire_shared = (win32_srwlock_fn) + GetProcAddress(hModule, "AcquireSRWLockShared"); + win32_srwlock_release_shared = (win32_srwlock_fn) + GetProcAddress(hModule, "ReleaseSRWLockShared"); + win32_srwlock_acquire_exclusive = (win32_srwlock_fn) + GetProcAddress(hModule, "AcquireSRWLockExclusive"); + win32_srwlock_release_exclusive = (win32_srwlock_fn) + GetProcAddress(hModule, "ReleaseSRWLockExclusive"); + } + + return 0; +} + +int git_thread_create( + git_thread *GIT_RESTRICT thread, void *(*start_routine)(void*), void *GIT_RESTRICT arg) { - GIT_UNUSED(attr); - thread->result = NULL; thread->param = arg; thread->proc = start_routine; @@ -42,8 +67,8 @@ int git_win32__thread_create( return thread->thread ? 0 : -1; } -int git_win32__thread_join( - git_win32_thread *thread, +int git_thread_join( + git_thread *thread, void **value_ptr) { DWORD exit; @@ -70,39 +95,32 @@ int git_win32__thread_join( return 0; } -int pthread_mutex_init( - pthread_mutex_t *GIT_RESTRICT mutex, - const pthread_mutexattr_t *GIT_RESTRICT mutexattr) +int git_mutex_init(git_mutex *GIT_RESTRICT mutex) { - GIT_UNUSED(mutexattr); InitializeCriticalSection(mutex); return 0; } -int pthread_mutex_destroy(pthread_mutex_t *mutex) +int git_mutex_free(git_mutex *mutex) { DeleteCriticalSection(mutex); return 0; } -int pthread_mutex_lock(pthread_mutex_t *mutex) +int git_mutex_lock(git_mutex *mutex) { EnterCriticalSection(mutex); return 0; } -int pthread_mutex_unlock(pthread_mutex_t *mutex) +int git_mutex_unlock(git_mutex *mutex) { LeaveCriticalSection(mutex); return 0; } -int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) +int git_cond_init(git_cond *cond) { - /* We don't support non-default attributes. */ - if (attr) - return EINVAL; - /* This is an auto-reset event. */ *cond = CreateEventW(NULL, FALSE, FALSE, NULL); assert(*cond); @@ -112,7 +130,7 @@ int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) return *cond ? 0 : ENOMEM; } -int pthread_cond_destroy(pthread_cond_t *cond) +int git_cond_free(git_cond *cond) { BOOL closed; @@ -127,7 +145,7 @@ int pthread_cond_destroy(pthread_cond_t *cond) return 0; } -int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +int git_cond_wait(git_cond *cond, git_mutex *mutex) { int error; DWORD wait_result; @@ -136,7 +154,7 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) return EINVAL; /* The caller must be holding the mutex. */ - error = pthread_mutex_unlock(mutex); + error = git_mutex_unlock(mutex); if (error) return error; @@ -145,10 +163,10 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) assert(WAIT_OBJECT_0 == wait_result); GIT_UNUSED(wait_result); - return pthread_mutex_lock(mutex); + return git_mutex_lock(mutex); } -int pthread_cond_signal(pthread_cond_t *cond) +int git_cond_signal(git_cond *cond) { BOOL signaled; @@ -162,36 +180,8 @@ int pthread_cond_signal(pthread_cond_t *cond) return 0; } -/* pthread_cond_broadcast is not implemented because doing so with just - * Win32 events is quite complicated, and no caller in libgit2 uses it - * yet. - */ -int pthread_num_processors_np(void) +int git_rwlock_init(git_rwlock *GIT_RESTRICT lock) { - DWORD_PTR p, s; - int n = 0; - - if (GetProcessAffinityMask(GetCurrentProcess(), &p, &s)) - for (; p; p >>= 1) - n += p&1; - - return n ? n : 1; -} - -typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *); - -static win32_srwlock_fn win32_srwlock_initialize; -static win32_srwlock_fn win32_srwlock_acquire_shared; -static win32_srwlock_fn win32_srwlock_release_shared; -static win32_srwlock_fn win32_srwlock_acquire_exclusive; -static win32_srwlock_fn win32_srwlock_release_exclusive; - -int pthread_rwlock_init( - pthread_rwlock_t *GIT_RESTRICT lock, - const pthread_rwlockattr_t *GIT_RESTRICT attr) -{ - GIT_UNUSED(attr); - if (win32_srwlock_initialize) win32_srwlock_initialize(&lock->native.srwl); else @@ -200,7 +190,7 @@ int pthread_rwlock_init( return 0; } -int pthread_rwlock_rdlock(pthread_rwlock_t *lock) +int git_rwlock_rdlock(git_rwlock *lock) { if (win32_srwlock_acquire_shared) win32_srwlock_acquire_shared(&lock->native.srwl); @@ -210,7 +200,7 @@ int pthread_rwlock_rdlock(pthread_rwlock_t *lock) return 0; } -int pthread_rwlock_rdunlock(pthread_rwlock_t *lock) +int git_rwlock_rdunlock(git_rwlock *lock) { if (win32_srwlock_release_shared) win32_srwlock_release_shared(&lock->native.srwl); @@ -220,7 +210,7 @@ int pthread_rwlock_rdunlock(pthread_rwlock_t *lock) return 0; } -int pthread_rwlock_wrlock(pthread_rwlock_t *lock) +int git_rwlock_wrlock(git_rwlock *lock) { if (win32_srwlock_acquire_exclusive) win32_srwlock_acquire_exclusive(&lock->native.srwl); @@ -230,7 +220,7 @@ int pthread_rwlock_wrlock(pthread_rwlock_t *lock) return 0; } -int pthread_rwlock_wrunlock(pthread_rwlock_t *lock) +int git_rwlock_wrunlock(git_rwlock *lock) { if (win32_srwlock_release_exclusive) win32_srwlock_release_exclusive(&lock->native.srwl); @@ -240,30 +230,10 @@ int pthread_rwlock_wrunlock(pthread_rwlock_t *lock) return 0; } -int pthread_rwlock_destroy(pthread_rwlock_t *lock) +int git_rwlock_free(git_rwlock *lock) { if (!win32_srwlock_initialize) DeleteCriticalSection(&lock->native.csec); git__memzero(lock, sizeof(*lock)); return 0; } - -int win32_pthread_initialize(void) -{ - HMODULE hModule = GetModuleHandleW(L"kernel32"); - - if (hModule) { - win32_srwlock_initialize = (win32_srwlock_fn) - GetProcAddress(hModule, "InitializeSRWLock"); - win32_srwlock_acquire_shared = (win32_srwlock_fn) - GetProcAddress(hModule, "AcquireSRWLockShared"); - win32_srwlock_release_shared = (win32_srwlock_fn) - GetProcAddress(hModule, "ReleaseSRWLockShared"); - win32_srwlock_acquire_exclusive = (win32_srwlock_fn) - GetProcAddress(hModule, "AcquireSRWLockExclusive"); - win32_srwlock_release_exclusive = (win32_srwlock_fn) - GetProcAddress(hModule, "ReleaseSRWLockExclusive"); - } - - return 0; -} diff --git a/src/win32/thread.h b/src/win32/thread.h new file mode 100644 index 000000000..0d01822a6 --- /dev/null +++ b/src/win32/thread.h @@ -0,0 +1,62 @@ +/* + * 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_win32_thread_h__ +#define INCLUDE_win32_thread_h__ + +#include "../common.h" + +#if defined (_MSC_VER) +# define GIT_RESTRICT __restrict +#else +# define GIT_RESTRICT __restrict__ +#endif + +typedef struct { + HANDLE thread; + void *(*proc)(void *); + void *param; + void *result; +} git_thread; + +typedef CRITICAL_SECTION git_mutex; +typedef HANDLE git_cond; + +typedef struct { void *Ptr; } GIT_SRWLOCK; + +typedef struct { + union { + GIT_SRWLOCK srwl; + CRITICAL_SECTION csec; + } native; +} git_rwlock; + +int git_threads_init(void); + +int git_thread_create(git_thread *GIT_RESTRICT, + void *(*) (void *), + void *GIT_RESTRICT); +int git_thread_join(git_thread *, void **); + +int git_mutex_init(git_mutex *GIT_RESTRICT mutex); +int git_mutex_free(git_mutex *); +int git_mutex_lock(git_mutex *); +int git_mutex_unlock(git_mutex *); + +int git_cond_init(git_cond *); +int git_cond_free(git_cond *); +int git_cond_wait(git_cond *, git_mutex *); +int git_cond_signal(git_cond *); + +int git_rwlock_init(git_rwlock *GIT_RESTRICT lock); +int git_rwlock_rdlock(git_rwlock *); +int git_rwlock_rdunlock(git_rwlock *); +int git_rwlock_wrlock(git_rwlock *); +int git_rwlock_wrunlock(git_rwlock *); +int git_rwlock_free(git_rwlock *); + +#endif /* INCLUDE_win32_thread_h__ */ diff --git a/tests/checkout/index.c b/tests/checkout/index.c index 8af3e5684..ca63dc3f8 100644 --- a/tests/checkout/index.c +++ b/tests/checkout/index.c @@ -294,11 +294,12 @@ void test_checkout_index__options_dir_modes(void) (void)p_umask(um = p_umask(022)); cl_git_pass(p_stat("./testrepo/a", &st)); - cl_assert_equal_i_fmt(st.st_mode, (GIT_FILEMODE_TREE | 0701) & ~um, "%07o"); + /* Haiku & Hurd use other mode bits, so we must mask them out */ + cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), (GIT_FILEMODE_TREE | 0701) & ~um, "%07o"); /* File-mode test, since we're on the 'dir' branch */ cl_git_pass(p_stat("./testrepo/a/b.txt", &st)); - cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o"); + cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o"); git_commit_free(commit); } diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 5680b86df..4a0314a9e 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -1416,3 +1416,70 @@ void test_checkout_tree__safe_proceeds_if_no_index(void) git_object_free(obj); } +static int checkout_conflict_count_cb( + git_checkout_notify_t why, + const char *path, + const git_diff_file *b, + const git_diff_file *t, + const git_diff_file *w, + void *payload) +{ + size_t *n = payload; + + GIT_UNUSED(why); + GIT_UNUSED(path); + GIT_UNUSED(b); + GIT_UNUSED(t); + GIT_UNUSED(w); + + (*n)++; + + return 0; +} + +/* A repo that has a HEAD (even a properly born HEAD that peels to + * a commit) but no index should be treated as if it's an empty baseline + */ +void test_checkout_tree__baseline_is_empty_when_no_index(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_reference *head; + git_object *obj; + git_status_list *status; + size_t conflicts = 0; + + assert_on_branch(g_repo, "master"); + cl_git_pass(git_repository_head(&head, g_repo)); + cl_git_pass(git_reference_peel(&obj, head, GIT_OBJ_COMMIT)); + + cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL)); + + cl_must_pass(p_unlink("testrepo/.git/index")); + + /* for a safe checkout, we should have checkout conflicts with + * the existing untracked files. + */ + opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE; + opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT; + opts.notify_cb = checkout_conflict_count_cb; + opts.notify_payload = &conflicts; + + cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, obj, &opts)); + cl_assert_equal_i(4, conflicts); + + /* but force should succeed and update the index */ + opts.checkout_strategy |= GIT_CHECKOUT_FORCE; + cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); + + cl_git_pass(git_status_list_new(&status, g_repo, NULL)); + cl_assert_equal_i(0, git_status_list_entrycount(status)); + git_status_list_free(status); + + git_object_free(obj); + git_reference_free(head); +} + +void test_checkout_tree__nullopts(void) +{ + cl_git_pass(git_checkout_tree(g_repo, NULL, NULL)); +} diff --git a/tests/checkout/typechange.c b/tests/checkout/typechange.c index b4959a351..8a5110caa 100644 --- a/tests/checkout/typechange.c +++ b/tests/checkout/typechange.c @@ -6,6 +6,36 @@ static git_repository *g_repo = NULL; +/* +From the test repo used for this test: +-------------------------------------- + +This is a test repo for libgit2 where tree entries have type changes + +The key types that could be found in tree entries are: + +1 - GIT_FILEMODE_NEW = 0000000 +2 - GIT_FILEMODE_TREE = 0040000 +3 - GIT_FILEMODE_BLOB = 0100644 +4 - GIT_FILEMODE_BLOB_EXECUTABLE = 0100755 +5 - GIT_FILEMODE_LINK = 0120000 +6 - GIT_FILEMODE_COMMIT = 0160000 + +I will try to have every type of transition somewhere in the history +of this repo. + +Commits +------- +Initial commit - a(1) b(1) c(1) d(1) e(1) +Create content - a(1->2) b(1->3) c(1->4) d(1->5) e(1->6) +Changes #1 - a(2->3) b(3->4) c(4->5) d(5->6) e(6->2) +Changes #2 - a(3->5) b(4->6) c(5->2) d(6->3) e(2->4) +Changes #3 - a(5->3) b(6->4) c(2->5) d(3->6) e(4->2) +Changes #4 - a(3->2) b(4->3) c(5->4) d(6->5) e(2->6) +Changes #5 - a(2->1) b(3->1) c(4->1) d(5->1) e(6->1) + +*/ + static const char *g_typechange_oids[] = { "79b9f23e85f55ea36a472a902e875bc1121a94cb", "9bdb75b73836a99e3dbeea640a81de81031fdc29", @@ -21,6 +51,14 @@ static bool g_typechange_empty[] = { true, false, false, false, false, false, true, true }; +static const int g_typechange_expected_conflicts[] = { + 1, 2, 3, 3, 2, 3, 2 +}; + +static const int g_typechange_expected_untracked[] = { + 6, 4, 3, 2, 3, 2, 5 +}; + void test_checkout_typechange__initialize(void) { g_repo = cl_git_sandbox_init("typechanges"); @@ -112,12 +150,7 @@ void test_checkout_typechange__checkout_typechanges_safe(void) for (i = 0; g_typechange_oids[i] != NULL; ++i) { cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i])); - opts.checkout_strategy = GIT_CHECKOUT_FORCE; - - /* There are bugs in some submodule->tree changes that prevent - * SAFE from passing here, even though the following should work: - */ - /* !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE; */ + opts.checkout_strategy = !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE; cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); @@ -190,6 +223,38 @@ static void force_create_file(const char *file) cl_git_rewritefile(file, "yowza!!"); } +static int make_submodule_dirty(git_submodule *sm, const char *name, void *payload) +{ + git_buf submodulepath = GIT_BUF_INIT; + git_buf dirtypath = GIT_BUF_INIT; + git_repository *submodule_repo; + + GIT_UNUSED(name); + GIT_UNUSED(payload); + + /* remove submodule directory in preparation for init and repo_init */ + cl_git_pass(git_buf_joinpath( + &submodulepath, + git_repository_workdir(g_repo), + git_submodule_path(sm) + )); + git_futils_rmdir_r(git_buf_cstr(&submodulepath), NULL, GIT_RMDIR_REMOVE_FILES); + + /* initialize submodule's repository */ + cl_git_pass(git_submodule_repo_init(&submodule_repo, sm, 0)); + + /* create a file in the submodule workdir to make it dirty */ + cl_git_pass( + git_buf_joinpath(&dirtypath, git_repository_workdir(submodule_repo), "dirty")); + force_create_file(git_buf_cstr(&dirtypath)); + + git_buf_free(&dirtypath); + git_buf_free(&submodulepath); + git_repository_free(submodule_repo); + + return 0; +} + void test_checkout_typechange__checkout_with_conflicts(void) { int i; @@ -211,13 +276,17 @@ void test_checkout_typechange__checkout_with_conflicts(void) git_futils_rmdir_r("typechanges/d", NULL, GIT_RMDIR_REMOVE_FILES); p_mkdir("typechanges/d", 0777); /* intentionally empty dir */ force_create_file("typechanges/untracked"); + cl_git_pass(git_submodule_foreach(g_repo, make_submodule_dirty, NULL)); opts.checkout_strategy = GIT_CHECKOUT_SAFE; memset(&cts, 0, sizeof(cts)); cl_git_fail(git_checkout_tree(g_repo, obj, &opts)); - cl_assert(cts.conflicts > 0); - cl_assert(cts.untracked > 0); + cl_assert_equal_i(cts.conflicts, g_typechange_expected_conflicts[i]); + cl_assert_equal_i(cts.untracked, g_typechange_expected_untracked[i]); + cl_assert_equal_i(cts.dirty, 0); + cl_assert_equal_i(cts.updates, 0); + cl_assert_equal_i(cts.ignored, 0); opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED; diff --git a/tests/core/strtol.c b/tests/core/strtol.c index 8765e042b..0d3b6a5e6 100644 --- a/tests/core/strtol.c +++ b/tests/core/strtol.c @@ -33,5 +33,13 @@ void test_core_strtol__int64(void) cl_assert(i == 2147483657LL); cl_git_pass(git__strtol64(&i, " -2147483657 ", NULL, 10)); cl_assert(i == -2147483657LL); + cl_git_pass(git__strtol64(&i, " 9223372036854775807 ", NULL, 10)); + cl_assert(i == INT64_MAX); + cl_git_pass(git__strtol64(&i, " -9223372036854775808 ", NULL, 10)); + cl_assert(i == INT64_MIN); + cl_git_pass(git__strtol64(&i, " 0x7fffffffffffffff ", NULL, 16)); + cl_assert(i == INT64_MAX); + cl_git_pass(git__strtol64(&i, " -0x8000000000000000 ", NULL, 16)); + cl_assert(i == INT64_MIN); } diff --git a/tests/index/collision.c b/tests/index/collision.c index 19c1548e9..ad5827e0b 100644 --- a/tests/index/collision.c +++ b/tests/index/collision.c @@ -2,105 +2,103 @@ #include "git2/repository.h" #include "git2/index.h" -git_repository *repo = NULL; +static git_repository *g_repo; +static git_odb *g_odb; +static git_index *g_index; +static git_oid g_empty_id; + +void test_index_collision__initialize(void) +{ + g_repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_repository_odb(&g_odb, g_repo)); + cl_git_pass(git_repository_index(&g_index, g_repo)); + + cl_git_pass(git_odb_write(&g_empty_id, g_odb, "", 0, GIT_OBJ_BLOB)); +} void test_index_collision__cleanup(void) { + git_index_free(g_index); + git_odb_free(g_odb); cl_git_sandbox_cleanup(); - repo = NULL; } void test_index_collision__add(void) { - git_index *index; git_index_entry entry; git_oid tree_id; git_tree *tree; - repo = cl_git_sandbox_init("empty_standard_repo"); - cl_git_pass(git_repository_index(&index, repo)); - memset(&entry, 0, sizeof(entry)); entry.ctime.seconds = 12346789; entry.mtime.seconds = 12346789; entry.mode = 0100644; entry.file_size = 0; - git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + git_oid_cpy(&entry.id, &g_empty_id); entry.path = "a/b"; - cl_git_pass(git_index_add(index, &entry)); + cl_git_pass(git_index_add(g_index, &entry)); /* create a tree/blob collision */ entry.path = "a/b/c"; - cl_git_fail(git_index_add(index, &entry)); + cl_git_fail(git_index_add(g_index, &entry)); - cl_git_pass(git_index_write_tree(&tree_id, index)); - cl_git_pass(git_tree_lookup(&tree, repo, &tree_id)); + cl_git_pass(git_index_write_tree(&tree_id, g_index)); + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); git_tree_free(tree); - git_index_free(index); } void test_index_collision__add_with_highstage_1(void) { - git_index *index; git_index_entry entry; - repo = cl_git_sandbox_init("empty_standard_repo"); - cl_git_pass(git_repository_index(&index, repo)); - memset(&entry, 0, sizeof(entry)); entry.ctime.seconds = 12346789; entry.mtime.seconds = 12346789; entry.mode = 0100644; entry.file_size = 0; - git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + git_oid_cpy(&entry.id, &g_empty_id); entry.path = "a/b"; GIT_IDXENTRY_STAGE_SET(&entry, 2); - cl_git_pass(git_index_add(index, &entry)); + cl_git_pass(git_index_add(g_index, &entry)); /* create a blob beneath the previous tree entry */ entry.path = "a/b/c"; entry.flags = 0; - cl_git_pass(git_index_add(index, &entry)); + cl_git_pass(git_index_add(g_index, &entry)); /* create another tree entry above the blob */ entry.path = "a/b"; GIT_IDXENTRY_STAGE_SET(&entry, 1); - cl_git_pass(git_index_add(index, &entry)); - - git_index_free(index); + cl_git_pass(git_index_add(g_index, &entry)); } void test_index_collision__add_with_highstage_2(void) { - git_index *index; git_index_entry entry; - repo = cl_git_sandbox_init("empty_standard_repo"); - cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_repository_index(&g_index, g_repo)); memset(&entry, 0, sizeof(entry)); entry.ctime.seconds = 12346789; entry.mtime.seconds = 12346789; entry.mode = 0100644; entry.file_size = 0; - git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + git_oid_cpy(&entry.id, &g_empty_id); entry.path = "a/b/c"; GIT_IDXENTRY_STAGE_SET(&entry, 1); - cl_git_pass(git_index_add(index, &entry)); + cl_git_pass(git_index_add(g_index, &entry)); /* create a blob beneath the previous tree entry */ entry.path = "a/b/c"; GIT_IDXENTRY_STAGE_SET(&entry, 2); - cl_git_pass(git_index_add(index, &entry)); + cl_git_pass(git_index_add(g_index, &entry)); /* create another tree entry above the blob */ entry.path = "a/b"; GIT_IDXENTRY_STAGE_SET(&entry, 3); - cl_git_pass(git_index_add(index, &entry)); - - git_index_free(index); + cl_git_pass(git_index_add(g_index, &entry)); } diff --git a/tests/index/read_index.c b/tests/index/read_index.c index 82a771d54..6d14bc9fd 100644 --- a/tests/index/read_index.c +++ b/tests/index/read_index.c @@ -71,3 +71,58 @@ void test_index_read_index__maintains_stat_cache(void) } } } + +static bool roundtrip_with_read_index(const char *tree_idstr) +{ + git_oid tree_id, new_tree_id; + git_tree *tree; + git_index *tree_index; + + cl_git_pass(git_oid_fromstr(&tree_id, tree_idstr)); + cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id)); + cl_git_pass(git_index_new(&tree_index)); + cl_git_pass(git_index_read_tree(tree_index, tree)); + cl_git_pass(git_index_read_index(_index, tree_index)); + cl_git_pass(git_index_write_tree(&new_tree_id, _index)); + + git_tree_free(tree); + git_index_free(tree_index); + + return git_oid_equal(&tree_id, &new_tree_id); +} + +void test_index_read_index__produces_treesame_indexes(void) +{ + roundtrip_with_read_index("53fc32d17276939fc79ed05badaef2db09990016"); + roundtrip_with_read_index("944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"); + roundtrip_with_read_index("1810dff58d8a660512d4832e740f692884338ccd"); + roundtrip_with_read_index("d52a8fe84ceedf260afe4f0287bbfca04a117e83"); + roundtrip_with_read_index("c36d8ea75da8cb510fcb0c408c1d7e53f9a99dbe"); + roundtrip_with_read_index("7b2417a23b63e1fdde88c80e14b33247c6e5785a"); + roundtrip_with_read_index("f82a8eb4cb20e88d1030fd10d89286215a715396"); + roundtrip_with_read_index("fd093bff70906175335656e6ce6ae05783708765"); + roundtrip_with_read_index("ae90f12eea699729ed24555e40b9fd669da12a12"); +} + +void test_index_read_index__read_and_writes(void) +{ + git_oid tree_id, new_tree_id; + git_tree *tree; + git_index *tree_index, *new_index; + + cl_git_pass(git_oid_fromstr(&tree_id, "ae90f12eea699729ed24555e40b9fd669da12a12")); + cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id)); + cl_git_pass(git_index_new(&tree_index)); + cl_git_pass(git_index_read_tree(tree_index, tree)); + cl_git_pass(git_index_read_index(_index, tree_index)); + cl_git_pass(git_index_write(_index)); + + cl_git_pass(git_index_open(&new_index, git_index_path(_index))); + cl_git_pass(git_index_write_tree_to(&new_tree_id, new_index, _repo)); + + cl_assert_equal_oid(&tree_id, &new_tree_id); + + git_tree_free(tree); + git_index_free(tree_index); + git_index_free(new_index); +} diff --git a/tests/object/cache.c b/tests/object/cache.c index bdf12da7a..680f23630 100644 --- a/tests/object/cache.c +++ b/tests/object/cache.c @@ -220,7 +220,7 @@ void test_object_cache__threadmania(void) fn = (th & 1) ? cache_parsed : cache_raw; #ifdef GIT_THREADS - cl_git_pass(git_thread_create(&t[th], NULL, fn, data)); + cl_git_pass(git_thread_create(&t[th], fn, data)); #else cl_assert(fn(data) == data); git__free(data); @@ -267,7 +267,7 @@ void test_object_cache__fast_thread_rush(void) data[th] = th; #ifdef GIT_THREADS cl_git_pass( - git_thread_create(&t[th], NULL, cache_quick, &data[th])); + git_thread_create(&t[th], cache_quick, &data[th])); #else cl_assert(cache_quick(&data[th]) == &data[th]); #endif diff --git a/tests/object/tag/read.c b/tests/object/tag/read.c index c9787a413..8f28afd54 100644 --- a/tests/object/tag/read.c +++ b/tests/object/tag/read.c @@ -140,3 +140,40 @@ void test_object_tag_read__without_tagger_nor_message(void) git_tag_free(tag); git_repository_free(repo); } + +static const char *silly_tag = "object c054ccaefbf2da31c3b19178f9e3ef20a3867924\n\ +type commit\n\ +tag v1_0_1\n\ +tagger Jamis Buck 1107717917\n\ +diff --git a/lib/sqlite3/version.rb b/lib/sqlite3/version.rb\n\ +index 0b3bf69..4ee8fc2 100644\n\ +--- a/lib/sqlite3/version.rb\n\ ++++ b/lib/sqlite3/version.rb\n\ +@@ -36,7 +36,7 @@ module SQLite3\n\ + \n\ + MAJOR = 1\n\ + MINOR = 0\n\ +- TINY = 0\n\ ++ TINY = 1\n\ + \n\ + STRING = [ MAJOR, MINOR, TINY ].join( \".\" )\n\ + \n\ + -0600\n\ +\n\ +v1_0_1 release\n"; + +void test_object_tag_read__extra_header_fields(void) +{ + git_tag *tag; + git_odb *odb; + git_oid id; + + cl_git_pass(git_repository_odb__weakptr(&odb, g_repo)); + + cl_git_pass(git_odb_write(&id, odb, silly_tag, strlen(silly_tag), GIT_OBJ_TAG)); + cl_git_pass(git_tag_lookup(&tag, g_repo, &id)); + + cl_assert_equal_s("v1_0_1 release\n", git_tag_message(tag)); + + git_tag_free(tag); +} diff --git a/tests/odb/emptyobjects.c b/tests/odb/emptyobjects.c index 783d05197..61bb2b82c 100644 --- a/tests/odb/emptyobjects.c +++ b/tests/odb/emptyobjects.c @@ -2,29 +2,33 @@ #include "odb.h" #include "filebuf.h" +#define TEST_REPO_PATH "redundant.git" + git_repository *g_repo; +git_odb *g_odb; void test_odb_emptyobjects__initialize(void) { - cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); -} -void test_odb_emptyobjects__cleanup(void) -{ - git_repository_free(g_repo); + g_repo = cl_git_sandbox_init(TEST_REPO_PATH); + cl_git_pass(git_repository_odb(&g_odb, g_repo)); } -void test_odb_emptyobjects__read(void) +void test_odb_emptyobjects__cleanup(void) { - git_oid id; + git_odb_free(g_odb); + cl_git_sandbox_cleanup(); +} + +void test_odb_emptyobjects__blob_notfound(void) +{ + git_oid id, written_id; git_blob *blob; cl_git_pass(git_oid_fromstr(&id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); - cl_git_pass(git_blob_lookup(&blob, g_repo, &id)); - cl_assert_equal_i(GIT_OBJ_BLOB, git_object_type((git_object *) blob)); - cl_assert(git_blob_rawcontent(blob)); - cl_assert_equal_s("", git_blob_rawcontent(blob)); - cl_assert_equal_i(0, git_blob_rawsize(blob)); - git_blob_free(blob); + cl_git_fail_with(GIT_ENOTFOUND, git_blob_lookup(&blob, g_repo, &id)); + + cl_git_pass(git_odb_write(&written_id, g_odb, "", 0, GIT_OBJ_BLOB)); + cl_assert(git_path_exists(TEST_REPO_PATH "/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391")); } void test_odb_emptyobjects__read_tree(void) @@ -43,15 +47,12 @@ void test_odb_emptyobjects__read_tree(void) void test_odb_emptyobjects__read_tree_odb(void) { git_oid id; - git_odb *odb; git_odb_object *tree_odb; cl_git_pass(git_oid_fromstr(&id, "4b825dc642cb6eb9a060e54bf8d69288fbee4904")); - cl_git_pass(git_repository_odb(&odb, g_repo)); - cl_git_pass(git_odb_read(&tree_odb, odb, &id)); + cl_git_pass(git_odb_read(&tree_odb, g_odb, &id)); cl_assert(git_odb_object_data(tree_odb)); cl_assert_equal_s("", git_odb_object_data(tree_odb)); cl_assert_equal_i(0, git_odb_object_size(tree_odb)); git_odb_object_free(tree_odb); - git_odb_free(odb); } diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c index 200edacfd..9aaad253c 100644 --- a/tests/online/fetchhead.c +++ b/tests/online/fetchhead.c @@ -35,6 +35,19 @@ static void fetchhead_test_clone(void) cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); } +static int count_references(void) +{ + git_strarray array; + int refs; + + cl_git_pass(git_reference_list(&array, g_repo)); + refs = array.count; + + git_strarray_free(&array); + + return refs; +} + static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead) { git_remote *remote; @@ -101,3 +114,41 @@ void test_online_fetchhead__no_merges(void) cl_git_pass(git_tag_delete(g_repo, "commit_tree")); fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3); } + +void test_online_fetchhead__explicit_dst_refspec_creates_branch(void) +{ + git_reference *ref; + int refs; + + fetchhead_test_clone(); + refs = count_references(); + fetchhead_test_fetch("refs/heads/first-merge:refs/heads/explicit-refspec", FETCH_HEAD_EXPLICIT_DATA); + + cl_git_pass(git_branch_lookup(&ref, g_repo, "explicit-refspec", GIT_BRANCH_ALL)); + cl_assert_equal_i(refs + 1, count_references()); +} + +void test_online_fetchhead__empty_dst_refspec_creates_no_branch(void) +{ + git_reference *ref; + int refs; + + fetchhead_test_clone(); + refs = count_references(); + + fetchhead_test_fetch("refs/heads/first-merge", FETCH_HEAD_EXPLICIT_DATA); + cl_git_fail(git_branch_lookup(&ref, g_repo, "first-merge", GIT_BRANCH_ALL)); + + cl_assert_equal_i(refs, count_references()); +} + +void test_online_fetchhead__colon_only_dst_refspec_creates_no_branch(void) +{ + int refs; + + fetchhead_test_clone(); + refs = count_references(); + fetchhead_test_fetch("refs/heads/first-merge:", FETCH_HEAD_EXPLICIT_DATA); + + cl_assert_equal_i(refs, count_references()); +} diff --git a/tests/repo/discover.c b/tests/repo/discover.c index 86bd7458f..358daee9f 100644 --- a/tests/repo/discover.c +++ b/tests/repo/discover.c @@ -118,12 +118,22 @@ void test_repo_discover__0(void) cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs)); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs)); + append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER_SUB); + ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); + + /* this must pass as ceiling_directories cannot prevent the current + * working directory to be checked */ + ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, &sub_repository_path); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs)); + append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER); ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); //this must pass as ceiling_directories cannot predent the current //working directory to be checked - cl_git_pass(git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); + ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs, &sub_repository_path); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs)); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs)); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs)); diff --git a/tests/repo/open.c b/tests/repo/open.c index d3d087231..7cdd182a7 100644 --- a/tests/repo/open.c +++ b/tests/repo/open.c @@ -196,8 +196,9 @@ void test_repo_open__failures(void) &repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)); /* fail with ceiling too low */ - cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub")); cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr)); + cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub")); + cl_git_fail(git_repository_open_ext(&repo, "attr/sub/sub", 0, ceiling.ptr)); /* fail with no repo */ cl_git_pass(p_mkdir("alternate", 0777)); diff --git a/tests/reset/hard.c b/tests/reset/hard.c index e461f8093..69ef41e50 100644 --- a/tests/reset/hard.c +++ b/tests/reset/hard.c @@ -240,14 +240,18 @@ void test_reset_hard__switch_file_to_dir(void) { git_index_entry entry = {{ 0 }}; git_index *idx; + git_odb *odb; git_object *commit; git_tree *tree; git_signature *sig; git_oid src_tree_id, tgt_tree_id; git_oid src_id, tgt_id; + cl_git_pass(git_repository_odb(&odb, repo)); + cl_git_pass(git_odb_write(&entry.id, odb, "", 0, GIT_OBJ_BLOB)); + git_odb_free(odb); + entry.mode = GIT_FILEMODE_BLOB; - cl_git_pass(git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); cl_git_pass(git_index_new(&idx)); cl_git_pass(git_signature_now(&sig, "foo", "bar")); diff --git a/tests/status/ignore.c b/tests/status/ignore.c index c318046da..c4878b2dd 100644 --- a/tests/status/ignore.c +++ b/tests/status/ignore.c @@ -945,6 +945,44 @@ void test_status_ignore__negative_directory_ignores(void) assert_is_ignored("padded_parent/child8/bar.txt"); } +void test_status_ignore__unignore_entry_in_ignored_dir(void) +{ + static const char *test_files[] = { + "empty_standard_repo/bar.txt", + "empty_standard_repo/parent/bar.txt", + "empty_standard_repo/parent/child/bar.txt", + "empty_standard_repo/nested/parent/child/bar.txt", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/.gitignore", + "bar.txt\n" + "!parent/child/bar.txt\n"); + + assert_is_ignored("bar.txt"); + assert_is_ignored("parent/bar.txt"); + refute_is_ignored("parent/child/bar.txt"); + assert_is_ignored("nested/parent/child/bar.txt"); +} + +void test_status_ignore__do_not_unignore_basename_prefix(void) +{ + static const char *test_files[] = { + "empty_standard_repo/foo_bar.txt", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/.gitignore", + "foo_bar.txt\n" + "!bar.txt\n"); + + assert_is_ignored("foo_bar.txt"); +} + void test_status_ignore__filename_with_cr(void) { int ignored; diff --git a/tests/threads/refdb.c b/tests/threads/refdb.c index 6589e3922..f869bcb44 100644 --- a/tests/threads/refdb.c +++ b/tests/threads/refdb.c @@ -75,7 +75,7 @@ void test_threads_refdb__iterator(void) for (t = 0; t < THREADS; ++t) { id[t] = t; #ifdef GIT_THREADS - cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t])); + cl_git_pass(git_thread_create(&th[t], iterate_refs, &id[t])); #else th[t] = t; iterate_refs(&id[t]); @@ -196,7 +196,7 @@ void test_threads_refdb__edit_while_iterate(void) * for now by just running on a single thread... */ /* #ifdef GIT_THREADS */ -/* cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t])); */ +/* cl_git_pass(git_thread_create(&th[t], fn, &id[t])); */ /* #else */ fn(&id[t]); /* #endif */ @@ -211,7 +211,7 @@ void test_threads_refdb__edit_while_iterate(void) for (t = 0; t < THREADS; ++t) { id[t] = t; - cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t])); + cl_git_pass(git_thread_create(&th[t], iterate_refs, &id[t])); } for (t = 0; t < THREADS; ++t) { diff --git a/tests/threads/thread_helpers.c b/tests/threads/thread_helpers.c index 760a7bd33..54bf6097d 100644 --- a/tests/threads/thread_helpers.c +++ b/tests/threads/thread_helpers.c @@ -24,7 +24,7 @@ void run_in_parallel( for (t = 0; t < threads; ++t) { id[t] = t; #ifdef GIT_THREADS - cl_git_pass(git_thread_create(&th[t], NULL, func, &id[t])); + cl_git_pass(git_thread_create(&th[t], func, &id[t])); #else cl_assert(func(&id[t]) == &id[t]); #endif