diff --git a/CHANGELOG.md b/CHANGELOG.md index 8544ac451..24ecba426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -v0.25 + 1 -------- +v0.26 + 1 +--------- ### Changes or improvements @@ -9,6 +9,102 @@ v0.25 + 1 ### Breaking API changes +v0.26 +----- + +### Changes or improvements + +* Support for opening, creating and modifying worktrees. + +* We can now detect SHA1 collisions resulting from the SHAttered attack. These + checks can be enabled at build time via `-DUSE_SHA1DC`. + +* Fix for missing implementation of `git_merge_driver_source` getters. + +* Fix for installed pkg-config file being broken when the prefix contains + spaces. + +* We now detect when the hashsum of on-disk objects does not match their + expected hashsum. + +* We now support open-ended ranges (e.g. "master..", "...master") in our + revision range parsing code. + +* We now correctly compute ignores with leading "/" in subdirectories. + +* We now optionally call `fsync` on loose objects, packfiles and their indexes, + loose references and packed reference files. + +* We can now build against OpenSSL v1.1 and against LibreSSL. + +* `GIT_MERGE_OPTIONS_INIT` now includes a setting to perform rename detection. + This aligns this structure with the default by `git_merge` and + `git_merge_trees` when `NULL` was provided for the options. + +* Improvements for reading index v4 files. + +* Perform additional retries for filesystem operations on Windows when files + are temporarily locked by other processes. + +### API additions + +* New family of functions to handle worktrees: + + * `git_worktree_list()` lets you look up worktrees for a repository. + * `git_worktree_lookup()` lets you get a specific worktree. + * `git_worktree_open_from_repository()` lets you get the associated worktree + of a repository. + a worktree. + * `git_worktree_add` lets you create new worktrees. + * `git_worktree_prune` lets you remove worktrees from disk. + * `git_worktree_lock()` and `git_worktree_unlock()` let you lock + respectively unlock a worktree. + * `git_repository_open_from_worktree()` lets you open a repository via + * `git_repository_head_for_worktree()` lets you get the current `HEAD` for a + linked worktree. + * `git_repository_head_detached_for_worktree()` lets you check whether a + linked worktree is in detached HEAD mode. + +* `git_repository_item_path()` lets you retrieve paths for various repository + files. + +* `git_repository_commondir()` lets you retrieve the common directory of a + repository. + +* `git_branch_is_checked_out()` allows you to check whether a branch is checked + out in a repository or any of its worktrees. + +* `git_repository_submodule_cache_all()` and + `git_repository_submodule_cache_clear()` functions allow you to prime or clear + the submodule cache of a repository. + +* You can disable strict hash verifications via the + `GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION` option with `git_libgit2_opts()`. + +* You can enable us calling `fsync` for various files inside the ".git" + directory by setting the `GIT_OPT_ENABLE_FSYNC_GITDIR` option with + `git_libgit2_opts()`. + +* You can now enable "offset deltas" when creating packfiles and negotiating + packfiles with a remote server by setting `GIT_OPT_ENABLE_OFS_DELTA` option + with `GIT_libgit2_opts()`. + +* You can now set the default share mode on Windows for opening files using + `GIT_OPT_SET_WINDOWS_SHAREMODE` option with `git_libgit2_opts()`. + You can query the current share mode with `GIT_OPT_GET_WINDOWS_SHAREMODE`. + +* `git_transport_smart_proxy_options()' enables you to get the proxy options for + smart transports. + +* The `GIT_FILTER_INIT` macro and the `git_filter_init` function are provided + to initialize a `git_filter` structure. + +### Breaking API changes + +* `clone_checkout_strategy` has been removed from + `git_submodule_update_option`. The checkout strategy used to clone will + be the same strategy specified in `checkout_opts`. + v0.25 ------- @@ -150,7 +246,12 @@ v0.25 If this is `NULL`, then it will not be called and the `exists` function will be used instead. -* `git_remote_connect()` now accepts proxy options. +* `git_remote_connect()` now accepts `git_proxy_options` argument, and + `git_fetch_options` and `git_push_options` each have a `proxy_opts` field. + +* `git_merge_options` now provides a `default_driver` that can be used + to provide the name of a merge driver to be used to handle files changed + during a merge. v0.24 ------- @@ -227,10 +328,6 @@ v0.24 ### Breaking API changes -* `git_merge_options` now provides a `default_driver` that can be used - to provide the name of a merge driver to be used to handle files changed - during a merge. - * The `git_merge_tree_flag_t` is now `git_merge_flag_t`. Subsequently, its members are no longer prefixed with `GIT_MERGE_TREE_FLAG` but are now prefixed with `GIT_MERGE_FLAG`, and the `tree_flags` field of the diff --git a/CMakeLists.txt b/CMakeLists.txt index f26f46879..4783e3ef9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ OPTION( PROFILE "Generate profiling information" OFF ) OPTION( ENABLE_TRACE "Enables tracing support" OFF ) OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) +OPTION( USE_SHA1DC "Use SHA-1 with collision detection" OFF ) 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 ) @@ -237,8 +238,7 @@ ENDIF() IF (WIN32 AND WINHTTP) ADD_DEFINITIONS(-DGIT_WINHTTP) - INCLUDE_DIRECTORIES(deps/http-parser) - FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h) + ADD_DEFINITIONS(-DGIT_HTTPS) # Since MinGW does not come with headers or an import library for winhttp, # we have to include a private header and generate our own import library @@ -291,27 +291,22 @@ ELSE () LINK_LIBRARIES(${CURL_LIBRARIES}) LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS}) ENDIF() - - FIND_PACKAGE(HTTP_Parser) - IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2) - INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS}) - LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES}) - LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser") - ELSE() - MESSAGE(STATUS "http-parser was not found or is too old; using bundled 3rd-party sources.") - INCLUDE_DIRECTORIES(deps/http-parser) - FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h) - ENDIF() ENDIF() # Specify sha1 implementation -IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin") - ADD_DEFINITIONS(-DWIN32_SHA1) +IF (USE_SHA1DC) + ADD_DEFINITIONS(-DGIT_SHA1_COLLISIONDETECT) + ADD_DEFINITIONS(-DSHA1DC_NO_STANDARD_INCLUDES=1) + ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\") + ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\") + FILE(GLOB SRC_SHA1 src/hash/hash_collisiondetect.c src/hash/sha1dc/*) +ELSEIF (WIN32 AND NOT MINGW) + ADD_DEFINITIONS(-DGIT_SHA1_WIN32) FILE(GLOB SRC_SHA1 src/hash/hash_win32.c) ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - ADD_DEFINITIONS(-DGIT_COMMON_CRYPTO) -ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin") - ADD_DEFINITIONS(-DOPENSSL_SHA1) + ADD_DEFINITIONS(-DGIT_SHA1_COMMON_CRYPTO) +ELSEIF (OPENSSL_FOUND) + ADD_DEFINITIONS(-DGIT_SHA1_OPENSSL) IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") LIST(APPEND LIBGIT2_PC_LIBS "-lssl") ELSE() @@ -332,6 +327,18 @@ IF(WIN32 OR AMIGA OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") SET(SRC_REGEX deps/regex/regex.c) ENDIF() +# Optional external dependency: http-parser +FIND_PACKAGE(HTTP_Parser) +IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2) + INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS}) + LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES}) + LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser") +ELSE() + MESSAGE(STATUS "http-parser version 2 was not found; using bundled 3rd-party sources.") + INCLUDE_DIRECTORIES(deps/http-parser) + FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h) +ENDIF() + # Optional external dependency: zlib FIND_PACKAGE(ZLIB) IF (ZLIB_FOUND) @@ -461,19 +468,17 @@ IF (MSVC) SET(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}") SET(WIN_RC "src/win32/git2.rc") - - # Precompiled headers - ELSE () - SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra ${CMAKE_C_FLAGS}") + SET(CMAKE_C_FLAGS "-D_GNU_SOURCE ${CMAKE_C_FLAGS}") + + ADD_C_FLAG_IF_SUPPORTED(-Wall) + ADD_C_FLAG_IF_SUPPORTED(-Wextra) IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") SET(CMAKE_C_FLAGS "-std=c99 -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}") ENDIF() - IF (WIN32 AND NOT CYGWIN) - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") - ENDIF () + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -O0") 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}") @@ -540,11 +545,13 @@ ENDIF() IF (SECURITY_FOUND) ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT) + ADD_DEFINITIONS(-DGIT_HTTPS) INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR}) ENDIF () IF (OPENSSL_FOUND) ADD_DEFINITIONS(-DGIT_OPENSSL) + ADD_DEFINITIONS(-DGIT_HTTPS) INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES}) ENDIF() @@ -697,9 +704,9 @@ IF (BUILD_CLAR) ENABLE_TESTING() IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND) - ADD_TEST(libgit2_clar libgit2_clar -ionline) + ADD_TEST(libgit2_clar libgit2_clar -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style) ELSE () - ADD_TEST(libgit2_clar libgit2_clar -v) + ADD_TEST(libgit2_clar libgit2_clar -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style) ENDIF () # Add a test target which runs the cred callback tests, to be diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 0be4b33cc..ffb696a4d 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -18,11 +18,11 @@ other's toes. - If a function returns an object as a return value, that function is a getter and the object's lifetime is tied to the parent object. Objects which are returned as the first argument as a - pointer-to-pointer are owned by the caller and it is repsponsible + pointer-to-pointer are owned by the caller and it is responsible for freeing it. Strings are returned via `git_buf` in order to allow for re-use and safe freeing. - - Most of what libgit2 does relates to I/O so you as a general rule + - Most of what libgit2 does relates to I/O so as a general rule you should assume that any function can fail due to errors as even getting data from the filesystem can result in all sorts of errors and complex failure cases. diff --git a/COPYING b/COPYING index 1b88b9b8e..da695ebdb 100644 --- a/COPYING +++ b/COPYING @@ -958,3 +958,36 @@ necessary. Here is a sample; alter the names: That's all there is to it! ---------------------------------------------------------------------- + +The bundled SHA1 collision detection code is licensed under the MIT license: + +MIT License + +Copyright (c) 2017: + Marc Stevens + Cryptology Group + Centrum Wiskunde & Informatica + P.O. Box 94079, 1090 GB Amsterdam, Netherlands + marc@marc-stevens.nl + + Dan Shumow + Microsoft Research + danshu@microsoft.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 19d24dea1..1bbd37134 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,9 @@ We ask that you not open a GitHub Issue for help, only for bug reports. What It Can Do ============== -The goal of this library is to allow its users the ability to handle Git data in -their applications from their programming language of choice, as is used in -production for many applications including the GitHub.com site, in Plastic SCM -and also powering Microsoft's Visual Studio tools for Git. +libgit2 provides you with the ability to manage Git repositories in the +programming language of your choice. It's used in production to power many +applications including GitHub.com, Plastic SCM and Visual Studio Team Services. It does not aim to replace the git tool or its user-facing commands. Some APIs resemble the plumbing commands as those align closely with the concepts of the @@ -68,6 +67,16 @@ The library provides: * descriptive and detailed error messages * ...and more (over 175 different API calls) +As libgit2 is purely a consumer of the Git system, we have to +adjust to changes made upstream. This has two major consequences: + +* Some changes may require us to change provided interfaces. While we try to + implement functions in a generic way so that no future changes are required, + we cannot promise a completely stable API. +* As we have to keep up with changes in behavior made upstream, we may lag + behind in some areas. We usually to document these incompatibilities in our + issue tracker with the label "git change". + Optional dependencies ===================== @@ -124,6 +133,14 @@ On most systems you can build the library using the following commands Alternatively you can point the CMake GUI tool to the CMakeLists.txt file and generate platform specific build project or IDE workspace. +Once built, you can run the tests from the `build` directory with the command + + $ make test + +Alternatively you can run the test suite directly using, + + $ ./libgit2_clar + To install the library you can specify the install prefix by setting: $ cmake .. -DCMAKE_INSTALL_PREFIX=/install/prefix diff --git a/appveyor.yml b/appveyor.yml index 9e94c0726..fb3fff7dd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,7 @@ version: '{build}' branches: only: - master + - appveyor - /^maint.*/ environment: GITTEST_INVASIVE_FS_STRUCTURE: 1 diff --git a/examples/general.c b/examples/general.c index 7c05d8712..ff984a36c 100644 --- a/examples/general.c +++ b/examples/general.c @@ -57,7 +57,7 @@ static void blob_parsing(git_repository *repo); static void revwalking(git_repository *repo); static void index_walking(git_repository *repo); static void reference_listing(git_repository *repo); -static void config_files(const char *repo_path); +static void config_files(const char *repo_path, git_repository *repo); /** * Almost all libgit2 functions return 0 on success or negative on error. @@ -115,7 +115,7 @@ int main (int argc, char** argv) revwalking(repo); index_walking(repo); reference_listing(repo); - config_files(repo_path); + config_files(repo_path, repo); /** * Finally, when you're done with the repository, you can free it as well. @@ -247,6 +247,11 @@ static void object_database(git_repository *repo, git_oid *oid) */ git_oid_fmt(oid_hex, oid); printf("Written Object: %s\n", oid_hex); + + /** + * Free the object database after usage. + */ + git_odb_free(odb); } /** @@ -264,7 +269,7 @@ static void commit_writing(git_repository *repo) git_oid tree_id, parent_id, commit_id; git_tree *tree; git_commit *parent; - const git_signature *author, *cmtter; + git_signature *author, *committer; char oid_hex[GIT_OID_HEXSZ+1] = { 0 }; printf("\n*Commit Writing*\n"); @@ -276,9 +281,9 @@ static void commit_writing(git_repository *repo) * `user.email` configuration options. See the `config` section of this * example file to see how to access config values. */ - git_signature_new((git_signature **)&author, + git_signature_new(&author, "Scott Chacon", "schacon@gmail.com", 123456789, 60); - git_signature_new((git_signature **)&cmtter, + git_signature_new(&committer, "Scott A Chacon", "scott@github.com", 987654321, 90); /** @@ -301,7 +306,7 @@ static void commit_writing(git_repository *repo) repo, NULL, /* do not update the HEAD */ author, - cmtter, + committer, NULL, /* use default message encoding */ "example commit", tree, @@ -312,6 +317,14 @@ static void commit_writing(git_repository *repo) */ git_oid_fmt(oid_hex, &commit_id); printf("New Commit: %s\n", oid_hex); + + /** + * Free all objects used in the meanwhile. + */ + git_tree_free(tree); + git_commit_free(parent); + git_signature_free(author); + git_signature_free(committer); } /** @@ -431,7 +444,11 @@ static void tag_parsing(git_repository *repo) printf("Tag Name: %s\nTag Type: %s\nTag Message: %s\n", name, git_object_type2string(type), message); + /** + * Free both the commit and tag after usage. + */ git_commit_free(commit); + git_tag_free(tag); } /** @@ -485,9 +502,10 @@ static void tree_parsing(git_repository *repo) git_tree_entry_to_object(&obj, repo, entry); /* blob */ /** - * Remember to close the looked-up object once you are done using it + * Remember to close the looked-up object and tree once you are done using it */ git_object_free(obj); + git_tree_free(tree); } /** @@ -522,6 +540,11 @@ static void blob_parsing(git_repository *repo) * */ printf("Blob Size: %ld\n", (long)git_blob_rawsize(blob)); /* 8 */ git_blob_rawcontent(blob); /* "content" */ + + /** + * Free the blob after usage. + */ + git_blob_free(blob); } /** @@ -644,10 +667,7 @@ static void index_walking(git_repository *repo) static void reference_listing(git_repository *repo) { git_strarray ref_list; - const char *refname; - git_reference *ref; unsigned i; - char oid_hex[GIT_OID_HEXSZ+1]; printf("\n*Reference Listing*\n"); @@ -662,7 +682,10 @@ static void reference_listing(git_repository *repo) git_reference_list(&ref_list, repo); for (i = 0; i < ref_list.count; ++i) { - memset(oid_hex, 0, sizeof(oid_hex)); + git_reference *ref; + char oid_hex[GIT_OID_HEXSZ+1] = GIT_OID_HEX_ZERO; + const char *refname; + refname = ref_list.strings[i]; git_reference_lookup(&ref, repo, refname); @@ -679,6 +702,8 @@ static void reference_listing(git_repository *repo) fprintf(stderr, "Unexpected reference type\n"); exit(1); } + + git_reference_free(ref); } git_strarray_free(&ref_list); @@ -692,12 +717,13 @@ static void reference_listing(git_repository *repo) * * [config]: http://libgit2.github.com/libgit2/#HEAD/group/config */ -static void config_files(const char *repo_path) +static void config_files(const char *repo_path, git_repository* repo) { const char *email; char config_path[256]; - int32_t j; + int32_t autocorrect; git_config *cfg; + git_config *snap_cfg; printf("\n*Config Listing*\n"); @@ -707,9 +733,16 @@ static void config_files(const char *repo_path) sprintf(config_path, "%s/config", repo_path); check_error(git_config_open_ondisk(&cfg, config_path), "opening config"); - git_config_get_int32(&j, cfg, "help.autocorrect"); - printf("Autocorrect: %d\n", j); + if (git_config_get_int32(&autocorrect, cfg, "help.autocorrect") == 0) + printf("Autocorrect: %d\n", autocorrect); - git_config_get_string(&email, cfg, "user.email"); + check_error(git_repository_config_snapshot(&snap_cfg, repo), "config snapshot"); + git_config_get_string(&email, snap_cfg, "user.email"); printf("Email: %s\n", email); + + /** + * Remember to free the configurations after usage. + */ + git_config_free(cfg); + git_config_free(snap_cfg); } diff --git a/examples/network/clone.c b/examples/network/clone.c index caf41cca8..540000bfd 100644 --- a/examples/network/clone.c +++ b/examples/network/clone.c @@ -50,7 +50,7 @@ static int sideband_progress(const char *str, int len, void *payload) { (void)payload; // unused - printf("remote: %*s", len, str); + printf("remote: %.*s", len, str); fflush(stdout); return 0; } diff --git a/examples/network/common.c b/examples/network/common.c index d123eedbd..1a81a10f8 100644 --- a/examples/network/common.c +++ b/examples/network/common.c @@ -1,5 +1,7 @@ #include "common.h" #include +#include +#include /* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/ * with permission of the original author, Martin Pool. @@ -20,15 +22,27 @@ int cred_acquire_cb(git_cred **out, unsigned int UNUSED(allowed_types), void * UNUSED(payload)) { - char username[128] = {0}; - char password[128] = {0}; + char *username = NULL, *password = NULL; + int error; printf("Username: "); - scanf("%s", username); + if (getline(&username, NULL, stdin) < 0) { + fprintf(stderr, "Unable to read username: %s", strerror(errno)); + return -1; + } /* Yup. Right there on your terminal. Careful where you copy/paste output. */ printf("Password: "); - scanf("%s", password); + if (getline(&password, NULL, stdin) < 0) { + fprintf(stderr, "Unable to read password: %s", strerror(errno)); + free(username); + return -1; + } - return git_cred_userpass_plaintext_new(out, username, password); + error = git_cred_userpass_plaintext_new(out, username, password); + + free(username); + free(password); + + return error; } diff --git a/include/git2/branch.h b/include/git2/branch.h index 34354f4e5..88fe723a0 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -245,6 +245,18 @@ GIT_EXTERN(int) git_branch_upstream_name( GIT_EXTERN(int) git_branch_is_head( const git_reference *branch); +/** + * Determine if the current branch is checked out in any linked + * repository. + * + * @param branch Reference to the branch. + * + * @return 1 if branch is checked out, 0 if it isn't, + * error code otherwise. + */ +GIT_EXTERN(int) git_branch_is_checked_out( + const git_reference *branch); + /** * Return the name of remote that the remote tracking branch belongs to. * diff --git a/include/git2/commit.h b/include/git2/commit.h index 4cc637466..692b3bdd9 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -255,7 +255,8 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor( /** * Get an arbitrary header field * - * @param out the buffer to fill + * @param out the buffer to fill; existing content will be + * overwritten * @param commit the commit to look in * @param field the header field to return * @return 0 on succeess, GIT_ENOTFOUND if the field does not exist, @@ -270,8 +271,10 @@ GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, * `GITERR_INVALID`. If the commit does not have a signature, the * error class will be `GITERR_OBJECT`. * - * @param signature the signature block - * @param signed_data signed data; this is the commit contents minus the signature block + * @param signature the signature block; existing content will be + * overwritten + * @param signed_data signed data; this is the commit contents minus the signature block; + * existing content will be overwritten * @param repo the repository in which the commit exists * @param commit_id the commit from which to extract the data * @param field the name of the header field containing the signature diff --git a/include/git2/common.h b/include/git2/common.h index 99c99812b..f65cfdd01 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -175,8 +175,14 @@ typedef enum { GIT_OPT_SET_SSL_CERT_LOCATIONS, GIT_OPT_SET_USER_AGENT, GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, + GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, GIT_OPT_SET_SSL_CIPHERS, GIT_OPT_GET_USER_AGENT, + GIT_OPT_ENABLE_OFS_DELTA, + GIT_OPT_ENABLE_FSYNC_GITDIR, + GIT_OPT_GET_WINDOWS_SHAREMODE, + GIT_OPT_SET_WINDOWS_SHAREMODE, + GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, } git_libgit2_opt_t; /** @@ -281,6 +287,17 @@ typedef enum { * > - `user_agent` is the value that will be delivered as the * > User-Agent header on HTTP requests. * + * * opts(GIT_OPT_SET_WINDOWS_SHAREMODE, unsigned long value) + * + * > Set the share mode used when opening files on Windows. + * > For more information, see the documentation for CreateFile. + * > The default is: FILE_SHARE_READ | FILE_SHARE_WRITE. This is + * > ignored and unused on non-Windows platforms. + * + * * opts(GIT_OPT_GET_WINDOWS_SHAREMODE, unsigned long *value) + * + * > Get the share mode used when opening files on Windows. + * * * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled) * * > Enable strict input validation when creating new objects @@ -289,12 +306,45 @@ typedef enum { * > will be validated when creating a new commit. This defaults * > to enabled. * + * * opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, int enabled) + * + * > Validate the target of a symbolic ref when creating it. For + * > example, `foobar` is not a valid ref, therefore `foobar` is + * > not a valid target for a symbolic ref by default, whereas + * > `refs/heads/foobar` is. Disabling this bypasses validation + * > so that an arbitrary strings such as `foobar` can be used + * > for a symbolic ref target. This defaults to enabled. + * * * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers) * * > Set the SSL ciphers use for HTTPS connections. * > * > - `ciphers` is the list of ciphers that are eanbled. * + * * opts(GIT_OPT_ENABLE_OFS_DELTA, int enabled) + * + * > Enable or disable the use of "offset deltas" when creating packfiles, + * > and the negotiation of them when talking to a remote server. + * > Offset deltas store a delta base location as an offset into the + * > packfile from the current location, which provides a shorter encoding + * > and thus smaller resultant packfiles. + * > Packfiles containing offset deltas can still be read. + * > This defaults to enabled. + * + * * opts(GIT_OPT_ENABLE_FSYNC_GITDIR, int enabled) + * + * > Enable synchronized writes of files in the gitdir using `fsync` + * > (or the platform equivalent) to ensure that new object data + * > is written to permanent storage, not simply cached. This + * > defaults to disabled. + * + * opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, int enabled) + * + * > Enable strict verification of object hashsums when reading + * > objects from disk. This may impact performance due to an + * > additional checksum calculation on each object. This defaults + * > to enabled. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff --git a/include/git2/errors.h b/include/git2/errors.h index e959ffd8a..6f5580253 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -53,6 +53,8 @@ typedef enum { GIT_PASSTHROUGH = -30, /**< Internal only */ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */ + GIT_RETRY = -32, /**< Internal only */ + GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */ } git_error_code; /** @@ -100,6 +102,8 @@ typedef enum { GITERR_REBASE, GITERR_FILESYSTEM, GITERR_PATCH, + GITERR_WORKTREE, + GITERR_SHA1 } git_error_t; /** diff --git a/include/git2/global.h b/include/git2/global.h index ce5bdf444..2a87e10c6 100644 --- a/include/git2/global.h +++ b/include/git2/global.h @@ -14,7 +14,7 @@ GIT_BEGIN_DECL /** * Init the global state * - * This function must the called before any other libgit2 function in + * This function must be called before any other libgit2 function in * order to set up global state and threading. * * This function may be called multiple times - it will return the number diff --git a/include/git2/index.h b/include/git2/index.h index e58b3287e..35af2e5bf 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -575,15 +575,16 @@ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path); * This method will fail in bare index instances. * * The `pathspec` is a list of file names or shell glob patterns that will - * matched against files in the repository's working directory. Each file - * that matches will be added to the index (either updating an existing - * entry or adding a new entry). You can disable glob expansion and force - * exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH` flag. + * be matched against files in the repository's working directory. Each + * file that matches will be added to the index (either updating an + * existing entry or adding a new entry). You can disable glob expansion + * and force exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH` + * flag. * * Files that are ignored will be skipped (unlike `git_index_add_bypath`). * If a file is already tracked in the index, then it *will* be updated - * even if it is ignored. Pass the `GIT_INDEX_ADD_FORCE` flag to - * skip the checking of ignore rules. + * even if it is ignored. Pass the `GIT_INDEX_ADD_FORCE` flag to skip + * the checking of ignore rules. * * To emulate `git add -A` and generate an error if the pathspec contains * the exact path of an ignored file (when not using FORCE), add the diff --git a/include/git2/merge.h b/include/git2/merge.h index c6f6cba6c..94ac8b5c5 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -290,7 +290,8 @@ typedef struct { } git_merge_options; #define GIT_MERGE_OPTIONS_VERSION 1 -#define GIT_MERGE_OPTIONS_INIT {GIT_MERGE_OPTIONS_VERSION} +#define GIT_MERGE_OPTIONS_INIT { \ + GIT_MERGE_OPTIONS_VERSION, GIT_MERGE_FIND_RENAMES } /** * Initializes a `git_merge_options` with default values. Equivalent to diff --git a/include/git2/odb.h b/include/git2/odb.h index b3ed2706c..b7dc0c5f3 100644 --- a/include/git2/odb.h +++ b/include/git2/odb.h @@ -488,7 +488,7 @@ GIT_EXTERN(git_otype) git_odb_object_type(git_odb_object *object); * The backends are checked in relative ordering, based on the * value of the `priority` parameter. * - * Read for more information. + * Read for more information. * * @param odb database to add the backend to * @param backend pointer to a git_odb_backend instance @@ -509,7 +509,7 @@ GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int * * Writing is disabled on alternate backends. * - * Read for more information. + * Read for more information. * * @param odb database to add the backend to * @param backend pointer to a git_odb_backend instance diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h index b17cfd8ba..9199538ce 100644 --- a/include/git2/odb_backend.h +++ b/include/git2/odb_backend.h @@ -39,7 +39,7 @@ GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_ * @param out location to store the odb backend pointer * @param objects_dir the Git repository's objects directory * @param compression_level zlib compression level to use - * @param do_fsync whether to do an fsync() after writing (currently ignored) + * @param do_fsync whether to do an fsync() after writing * @param dir_mode permissions to use creating a directory or 0 for defaults * @param file_mode permissions to use creating a file or 0 for defaults * diff --git a/include/git2/oid.h b/include/git2/oid.h index 8ad51c8ba..aaa678cc0 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -50,17 +50,16 @@ GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str); * Parse a hex formatted null-terminated string into a git_oid. * * @param out oid structure the result is written into. - * @param str input hex string; must be at least 4 characters - * long and null-terminated. + * @param str input hex string; must be null-terminated. * @return 0 or an error code */ GIT_EXTERN(int) git_oid_fromstrp(git_oid *out, const char *str); /** - * Parse N characters of a hex formatted object id into a git_oid + * Parse N characters of a hex formatted object id into a git_oid. * - * If N is odd, N-1 characters will be parsed instead. - * The remaining space in the git_oid will be set to zero. + * If N is odd, the last byte's high nibble will be read in and the + * low nibble set to zero. * * @param out oid structure the result is written into. * @param str input hex string of at least size `length` diff --git a/include/git2/remote.h b/include/git2/remote.h index 691114172..e9e4e5b65 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -715,8 +715,8 @@ GIT_EXTERN(int) git_remote_prune(git_remote *remote, const git_remote_callbacks * Peform all the steps from a push. * * @param remote the remote to push to - * @param refspecs the refspecs to use for pushing. If none are - * passed, the configured refspecs will be used + * @param refspecs the refspecs to use for pushing. If NULL or an empty + * array, the configured refspecs will be used * @param opts options to use for this push */ GIT_EXTERN(int) git_remote_push(git_remote *remote, @@ -796,7 +796,7 @@ GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name); * for the remote will be removed. * * @param repo the repository in which to act -* @param name the name of the remove to delete +* @param name the name of the remote to delete * @return 0 on success, or an error code. */ GIT_EXTERN(int) git_remote_delete(git_repository *repo, const char *name); diff --git a/include/git2/repository.h b/include/git2/repository.h index 3d70d1b89..8aac0b3f7 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -35,6 +35,17 @@ GIT_BEGIN_DECL * @return 0 or an error code */ GIT_EXTERN(int) git_repository_open(git_repository **out, const char *path); +/** + * Open working tree as a repository + * + * Open the working directory of the working tree as a normal + * repository that can then be worked on. + * + * @param out Output pointer containing opened repository + * @param wt Working tree to open + * @return 0 or an error code + */ +GIT_EXTERN(int) git_repository_open_from_worktree(git_repository **out, git_worktree *wt); /** * Create a "fake" repository to wrap an object database @@ -334,6 +345,17 @@ GIT_EXTERN(int) git_repository_init_ext( */ GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo); +/** + * Retrieve the referenced HEAD for the worktree + * + * @param out pointer to the reference which will be retrieved + * @param repo a repository object + * @param name name of the worktree to retrieve HEAD for + * @return 0 when successful, error-code otherwise + */ +GIT_EXTERN(int) git_repository_head_for_worktree(git_reference **out, git_repository *repo, + const char *name); + /** * Check if a repository's HEAD is detached * @@ -346,6 +368,20 @@ GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo); */ GIT_EXTERN(int) git_repository_head_detached(git_repository *repo); +/* + * Check if a worktree's HEAD is detached + * + * A worktree's HEAD is detached when it points directly to a + * commit instead of a branch. + * + * @param repo a repository object + * @param name name of the worktree to retrieve HEAD for + * @return 1 if HEAD is detached, 0 if its not; error code if + * there was an error + */ +GIT_EXTERN(int) git_repository_head_detached_for_worktree(git_repository *repo, + const char *name); + /** * Check if the current branch is unborn * @@ -370,6 +406,42 @@ GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo); */ GIT_EXTERN(int) git_repository_is_empty(git_repository *repo); +/** + * List of items which belong to the git repository layout + */ +typedef enum { + GIT_REPOSITORY_ITEM_GITDIR, + GIT_REPOSITORY_ITEM_WORKDIR, + GIT_REPOSITORY_ITEM_COMMONDIR, + GIT_REPOSITORY_ITEM_INDEX, + GIT_REPOSITORY_ITEM_OBJECTS, + GIT_REPOSITORY_ITEM_REFS, + GIT_REPOSITORY_ITEM_PACKED_REFS, + GIT_REPOSITORY_ITEM_REMOTES, + GIT_REPOSITORY_ITEM_CONFIG, + GIT_REPOSITORY_ITEM_INFO, + GIT_REPOSITORY_ITEM_HOOKS, + GIT_REPOSITORY_ITEM_LOGS, + GIT_REPOSITORY_ITEM_MODULES, + GIT_REPOSITORY_ITEM_WORKTREES +} git_repository_item_t; + +/** + * Get the location of a specific repository file or directory + * + * This function will retrieve the path of a specific repository + * item. It will thereby honor things like the repository's + * common directory, gitdir, etc. In case a file path cannot + * exist for a given item (e.g. the working directory of a bare + * repository), GIT_ENOTFOUND is returned. + * + * @param out Buffer to store the path at + * @param repo Repository to get path for + * @param item The repository item for which to retrieve the path + * @return 0, GIT_ENOTFOUND if the path cannot exist or an error code + */ +GIT_EXTERN(int) git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item); + /** * Get the path of this repository * @@ -392,6 +464,17 @@ GIT_EXTERN(const char *) git_repository_path(git_repository *repo); */ GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo); +/** + * Get the path of the shared common directory for this repository + * + * If the repository is bare is not a worktree, the git directory + * path is returned. + * + * @param repo A repository object + * @return the path to the common dir + */ +GIT_EXTERN(const char *) git_repository_commondir(git_repository *repo); + /** * Set the path to the working directory for this repository * @@ -420,6 +503,14 @@ GIT_EXTERN(int) git_repository_set_workdir( */ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo); +/** + * Check if a repository is a linked work tree + * + * @param repo Repo to test + * @return 1 if the repository is a linked work tree, 0 otherwise. + */ +GIT_EXTERN(int) git_repository_is_worktree(git_repository *repo); + /** * Get the configuration file for this repository. * diff --git a/include/git2/reset.h b/include/git2/reset.h index 79075291f..bd29c69e4 100644 --- a/include/git2/reset.h +++ b/include/git2/reset.h @@ -53,7 +53,7 @@ typedef enum { * * @param reset_type Kind of reset operation to perform. * - * @param checkout_opts Checkout options to be used for a HARD reset. + * @param checkout_opts Optional checkout options to be used for a HARD reset. * The checkout_strategy field will be overridden (based on reset_type). * This parameter can be used to propagate notify and progress callbacks. * diff --git a/include/git2/revert.h b/include/git2/revert.h index 2de194219..82dbadcfc 100644 --- a/include/git2/revert.h +++ b/include/git2/revert.h @@ -75,7 +75,7 @@ GIT_EXTERN(int) git_revert_commit( * * @param repo the repository to revert * @param commit the commit to revert - * @param given_opts merge flags + * @param given_opts the revert options (or null for defaults) * @return zero on success, -1 on failure. */ GIT_EXTERN(int) git_revert( diff --git a/include/git2/stash.h b/include/git2/stash.h index 733d75a7f..3af9cde38 100644 --- a/include/git2/stash.h +++ b/include/git2/stash.h @@ -173,7 +173,7 @@ GIT_EXTERN(int) git_stash_apply_init_options( * @param repo The owning repository. * @param index The position within the stash list. 0 points to the * most recent stashed state. - * @param options Options to control how stashes are applied. + * @param options Optional options to control how stashes are applied. * * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the * given index, GIT_EMERGECONFLICT if changes exist in the working @@ -242,7 +242,7 @@ GIT_EXTERN(int) git_stash_drop( * @param repo The owning repository. * @param index The position within the stash list. 0 points to the * most recent stashed state. - * @param options Options to control how stashes are applied. + * @param options Optional options to control how stashes are applied. * * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the given * index, or error code. (see git_stash_apply() above for details) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 540ecf5c7..b2b3039fe 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -134,9 +134,7 @@ typedef struct git_submodule_update_options { * checkout, set the `checkout_strategy` to * `GIT_CHECKOUT_NONE`. Generally you will want the use * GIT_CHECKOUT_SAFE to update files in the working - * directory. Use the `clone_checkout_strategy` field - * to set the checkout strategy that will be used in - * the case where update needs to clone the repository. + * directory. */ git_checkout_options checkout_opts; @@ -148,13 +146,6 @@ typedef struct git_submodule_update_options { */ git_fetch_options fetch_opts; - /** - * The checkout strategy to use when the sub repository needs to - * be cloned. Use GIT_CHECKOUT_SAFE to create all files - * in the working directory for the newly cloned repository. - */ - unsigned int clone_checkout_strategy; - /** * Allow fetching from the submodule's default remote if the target * commit isn't found. Enabled by default. @@ -166,7 +157,7 @@ typedef struct git_submodule_update_options { #define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \ { GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \ { GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \ - GIT_FETCH_OPTIONS_INIT, GIT_CHECKOUT_SAFE, 1 } + GIT_FETCH_OPTIONS_INIT, 1 } /** * Initializes a `git_submodule_update_options` with default values. diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h index d0e5d4d6f..6d575d4fd 100644 --- a/include/git2/sys/filter.h +++ b/include/git2/sys/filter.h @@ -271,6 +271,17 @@ struct git_filter { }; #define GIT_FILTER_VERSION 1 +#define GIT_FILTER_INIT {GIT_FILTER_VERSION} + +/** + * Initializes a `git_filter` with default values. Equivalent to + * creating an instance with GIT_FILTER_INIT. + * + * @param filter the `git_filter` struct to initialize. + * @param version Version the struct; pass `GIT_FILTER_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_filter_init(git_filter *filter, unsigned int version); /** * Register a filter under a given name with a given priority. diff --git a/include/git2/sys/merge.h b/include/git2/sys/merge.h index 031941042..eed106c07 100644 --- a/include/git2/sys/merge.h +++ b/include/git2/sys/merge.h @@ -36,23 +36,23 @@ GIT_EXTERN(git_merge_driver *) git_merge_driver_lookup(const char *name); typedef struct git_merge_driver_source git_merge_driver_source; /** Get the repository that the source data is coming from. */ -GIT_EXTERN(git_repository *) git_merge_driver_source_repo( +GIT_EXTERN(const git_repository *) git_merge_driver_source_repo( const git_merge_driver_source *src); /** Gets the ancestor of the file to merge. */ -GIT_EXTERN(git_index_entry *) git_merge_driver_source_ancestor( +GIT_EXTERN(const git_index_entry *) git_merge_driver_source_ancestor( const git_merge_driver_source *src); /** Gets the ours side of the file to merge. */ -GIT_EXTERN(git_index_entry *) git_merge_driver_source_ours( +GIT_EXTERN(const git_index_entry *) git_merge_driver_source_ours( const git_merge_driver_source *src); /** Gets the theirs side of the file to merge. */ -GIT_EXTERN(git_index_entry *) git_merge_driver_source_theirs( +GIT_EXTERN(const git_index_entry *) git_merge_driver_source_theirs( const git_merge_driver_source *src); /** Gets the merge file options that the merge was invoked with */ -GIT_EXTERN(git_merge_file_options *) git_merge_driver_source_file_options( +GIT_EXTERN(const git_merge_file_options *) git_merge_driver_source_file_options( const git_merge_driver_source *src); diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h index 800396c86..0c9142143 100644 --- a/include/git2/sys/repository.h +++ b/include/git2/sys/repository.h @@ -135,6 +135,35 @@ GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index */ GIT_EXTERN(int) git_repository_set_bare(git_repository *repo); +/** + * Load and cache all submodules. + * + * Because the `.gitmodules` file is unstructured, loading submodules is an + * O(N) operation. Any operation (such as `git_rebase_init`) that requires + * accessing all submodules is O(N^2) in the number of submodules, if it + * has to look each one up individually. This function loads all submodules + * and caches them so that subsequent calls to `git_submodule_lookup` are O(1). + * + * @param repo the repository whose submodules will be cached. + */ +GIT_EXTERN(int) git_repository_submodule_cache_all( + git_repository *repo); + +/** + * Clear the submodule cache. + * + * Clear the submodule cache populated by `git_repository_submodule_cache_all`. + * If there is no cache, do nothing. + * + * The cache incorporates data from the repository's configuration, as well + * as the state of the working tree, the index, and HEAD. So any time any + * of these has changed, the cache might become invalid. + * + * @param repo the repository whose submodule cache will be cleared + */ +GIT_EXTERN(int) git_repository_submodule_cache_clear( + git_repository *repo); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index 60e38b21a..a395de5ed 100644 --- a/include/git2/sys/transport.h +++ b/include/git2/sys/transport.h @@ -241,6 +241,16 @@ GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport, */ GIT_EXTERN(int) git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods); +/** + * Get a copy of the proxy options + * + * The url is copied and must be freed by the caller. + * + * @param out options struct to fill + * @param transport the transport to extract the data from. + */ +GIT_EXTERN(int) git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport); + /* *** End of base transport interface *** *** Begin interface for subtransports for the smart transport *** diff --git a/include/git2/transport.h b/include/git2/transport.h index 0ec241699..0c371bf4b 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -321,13 +321,13 @@ GIT_EXTERN(void) git_cred_free(git_cred *cred); /** * Signature of a function which acquires a credential object. * - * - cred: The newly created credential object. - * - url: The resource for which we are demanding a credential. - * - username_from_url: The username that was embedded in a "user\@host" + * @param cred The newly created credential object. + * @param url The resource for which we are demanding a credential. + * @param username_from_url The username that was embedded in a "user\@host" * remote url, or NULL if not included. - * - allowed_types: A bitmask stating which cred types are OK to return. - * - payload: The payload provided when specifying this callback. - * - returns 0 for success, < 0 to indicate an error, > 0 to indicate + * @param allowed_types A bitmask stating which cred types are OK to return. + * @param payload The payload provided when specifying this callback. + * @return 0 for success, < 0 to indicate an error, > 0 to indicate * no credential was acquired */ typedef int (*git_cred_acquire_cb)( diff --git a/include/git2/tree.h b/include/git2/tree.h index 2e4735c4b..4740b1ffa 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -375,6 +375,19 @@ GIT_EXTERN(void) git_treebuilder_filter( GIT_EXTERN(int) git_treebuilder_write( git_oid *id, git_treebuilder *bld); +/** + * Write the contents of the tree builder as a tree object + * using a shared git_buf. + * + * @see git_treebuilder_write + * + * @param oid Pointer to store the OID of the newly written tree + * @param bld Tree builder to write + * @param tree Shared buffer for writing the tree. Will be grown as necessary. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_treebuilder_write_with_buffer( + git_oid *oid, git_treebuilder *bld, git_buf *tree); /** Callback for the tree traversal method */ typedef int (*git_treewalk_cb)( diff --git a/include/git2/types.h b/include/git2/types.h index 6f41014b3..dfdaa2920 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -104,6 +104,9 @@ typedef struct git_refdb_backend git_refdb_backend; */ typedef struct git_repository git_repository; +/** Representation of a working tree */ +typedef struct git_worktree git_worktree; + /** Representation of a generic object in a repository */ typedef struct git_object git_object; diff --git a/include/git2/version.h b/include/git2/version.h index d190893fe..becf97bd5 100644 --- a/include/git2/version.h +++ b/include/git2/version.h @@ -7,12 +7,12 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "0.25.1" +#define LIBGIT2_VERSION "0.26.0" #define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 25 -#define LIBGIT2_VER_REVISION 1 +#define LIBGIT2_VER_MINOR 26 +#define LIBGIT2_VER_REVISION 0 #define LIBGIT2_VER_PATCH 0 -#define LIBGIT2_SOVERSION 25 +#define LIBGIT2_SOVERSION 26 #endif diff --git a/include/git2/worktree.h b/include/git2/worktree.h new file mode 100644 index 000000000..d3fa88e3f --- /dev/null +++ b/include/git2/worktree.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_worktree_h__ +#define INCLUDE_git_worktree_h__ + +#include "common.h" +#include "buffer.h" +#include "types.h" +#include "strarray.h" + +/** + * @file git2/worktrees.h + * @brief Git worktree related functions + * @defgroup git_commit Git worktree related functions + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * List names of linked working trees + * + * The returned list should be released with `git_strarray_free` + * when no longer needed. + * + * @param out pointer to the array of working tree names + * @param repo the repo to use when listing working trees + * @return 0 or an error code + */ +GIT_EXTERN(int) git_worktree_list(git_strarray *out, git_repository *repo); + +/** + * Lookup a working tree by its name for a given repository + * + * @param out Output pointer to looked up worktree or `NULL` + * @param repo The repository containing worktrees + * @param name Name of the working tree to look up + * @return 0 or an error code + */ +GIT_EXTERN(int) git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name); + +/** + * Open a worktree of a given repository + * + * If a repository is not the main tree but a worktree, this + * function will look up the worktree inside the parent + * repository and create a new `git_worktree` structure. + * + * @param out Out-pointer for the newly allocated worktree + * @param repo Repository to look up worktree for + */ +GIT_EXTERN(int) git_worktree_open_from_repository(git_worktree **out, git_repository *repo); + +/** + * Free a previously allocated worktree + * + * @param wt worktree handle to close. If NULL nothing occurs. + */ +GIT_EXTERN(void) git_worktree_free(git_worktree *wt); + +/** + * Check if worktree is valid + * + * A valid worktree requires both the git data structures inside + * the linked parent repository and the linked working copy to be + * present. + * + * @param wt Worktree to check + * @return 0 when worktree is valid, error-code otherwise + */ +GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt); + +typedef struct git_worktree_add_options { + unsigned int version; + + int lock; /**< lock newly created worktree */ +} git_worktree_add_options; + +#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1 +#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0} + +/** + * Initializes a `git_worktree_add_options` with default vaules. + * Equivalent to creating an instance with + * GIT_WORKTREE_ADD_OPTIONS_INIT. + * + * @param opts the struct to initialize + * @param version Verison of struct; pass `GIT_WORKTREE_ADD_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +int git_worktree_add_init_options(git_worktree_add_options *opts, + unsigned int version); + +/** + * Add a new working tree + * + * Add a new working tree for the repository, that is create the + * required data structures inside the repository and check out + * the current HEAD at `path` + * + * @param out Output pointer containing new working tree + * @param repo Repository to create working tree for + * @param name Name of the working tree + * @param path Path to create working tree at + * @param opts Options to modify default behavior. May be NULL + * @return 0 or an error code + */ +GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo, + const char *name, const char *path, + const git_worktree_add_options *opts); + +/** + * Lock worktree if not already locked + * + * Lock a worktree, optionally specifying a reason why the linked + * working tree is being locked. + * + * @param wt Worktree to lock + * @param reason Reason why the working tree is being locked + * @return 0 on success, non-zero otherwise + */ +GIT_EXTERN(int) git_worktree_lock(git_worktree *wt, char *reason); + +/** + * Unlock a locked worktree + * + * @param wt Worktree to unlock + * @return 0 on success, 1 if worktree was not locked, error-code + * otherwise + */ +GIT_EXTERN(int) git_worktree_unlock(git_worktree *wt); + +/** + * Check if worktree is locked + * + * A worktree may be locked if the linked working tree is stored + * on a portable device which is not available. + * + * @param reason Buffer to store reason in. If NULL no reason is stored. + * @param wt Worktree to check + * @return 0 when the working tree not locked, a value greater + * than zero if it is locked, less than zero if there was an + * error + */ +GIT_EXTERN(int) git_worktree_is_locked(git_buf *reason, const git_worktree *wt); + +/** + * Flags which can be passed to git_worktree_prune to alter its + * behavior. + */ +typedef enum { + /* Prune working tree even if working tree is valid */ + GIT_WORKTREE_PRUNE_VALID = 1u << 0, + /* Prune working tree even if it is locked */ + GIT_WORKTREE_PRUNE_LOCKED = 1u << 1, + /* Prune checked out working tree */ + GIT_WORKTREE_PRUNE_WORKING_TREE = 1u << 2, +} git_worktree_prune_t; + +typedef struct git_worktree_prune_options { + unsigned int version; + + uint32_t flags; +} git_worktree_prune_options; + +#define GIT_WORKTREE_PRUNE_OPTIONS_VERSION 1 +#define GIT_WORKTREE_PRUNE_OPTIONS_INIT {GIT_WORKTREE_PRUNE_OPTIONS_VERSION,0} + +/** + * Initializes a `git_worktree_prune_options` with default vaules. + * Equivalent to creating an instance with + * GIT_WORKTREE_PRUNE_OPTIONS_INIT. + * + * @param opts the struct to initialize + * @param version Verison of struct; pass `GIT_WORKTREE_PRUNE_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_worktree_prune_init_options( + git_worktree_prune_options *opts, + unsigned int version); + +/** + * Is the worktree prunable with the given options? + * + * A worktree is not prunable in the following scenarios: + * + * - the worktree is linking to a valid on-disk worktree. The + * `valid` member will cause this check to be ignored. + * - the worktree is locked. The `locked` flag will cause this + * check to be ignored. + * + * If the worktree is not valid and not locked or if the above + * flags have been passed in, this function will return a + * positive value. + */ +GIT_EXTERN(int) git_worktree_is_prunable(git_worktree *wt, + git_worktree_prune_options *opts); + +/** + * Prune working tree + * + * Prune the working tree, that is remove the git data + * structures on disk. The repository will only be pruned of + * `git_worktree_is_prunable` succeeds. + * + * @param wt Worktree to prune + * @param opts Specifies which checks to override. See + * `git_worktree_is_prunable`. May be NULL + * @return 0 or an error code + */ +GIT_EXTERN(int) git_worktree_prune(git_worktree *wt, + git_worktree_prune_options *opts); + +/** @} */ +GIT_END_DECL +#endif diff --git a/libgit2.pc.in b/libgit2.pc.in index 329a560a7..96b965955 100644 --- a/libgit2.pc.in +++ b/libgit2.pc.in @@ -1,4 +1,4 @@ -prefix=@PKGCONFIG_PREFIX@ +prefix="@PKGCONFIG_PREFIX@" libdir=@PKGCONFIG_LIBDIR@ includedir=@PKGCONFIG_INCLUDEDIR@ @@ -6,7 +6,7 @@ Name: libgit2 Description: The git library, take 2 Version: @LIBGIT2_VERSION_STRING@ -Libs: -L"${libdir}" -lgit2 +Libs: -L${libdir} -lgit2 Libs.private: @LIBGIT2_PC_LIBS@ Requires.private: @LIBGIT2_PC_REQUIRES@ diff --git a/script/appveyor-mingw.sh b/script/appveyor-mingw.sh index 198801875..d171a72d5 100755 --- a/script/appveyor-mingw.sh +++ b/script/appveyor-mingw.sh @@ -7,15 +7,17 @@ if [ "$ARCH" = "i686" ]; then curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/sjlj/$f fi 7z x $f > /dev/null - mv mingw32 /MinGW + export PATH=`pwd`/mingw32/bin:$PATH else f=x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z if ! [ -e $f ]; then curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/$f fi 7z x $f > /dev/null - mv mingw64 /MinGW + export PATH=`pwd`/mingw64/bin:$PATH fi cd build +gcc --version +cmake --version cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON .. -G"$GENERATOR" cmake --build . --config RelWithDebInfo diff --git a/script/install-deps-osx.sh b/script/install-deps-osx.sh index 4b8393b19..94314dbaa 100755 --- a/script/install-deps-osx.sh +++ b/script/install-deps-osx.sh @@ -3,6 +3,7 @@ set -x brew update -brew install homebrew/dupes/zlib +brew install zlib brew install curl +brew install openssl brew install libssh2 diff --git a/src/apply.c b/src/apply.c index 635934299..595f5f300 100644 --- a/src/apply.c +++ b/src/apply.c @@ -173,7 +173,7 @@ static int apply_hunk( git_diff_line *line = git_array_get(patch->lines, linenum); if (!line) { - error = apply_err("Preimage does not contain line %"PRIuZ, linenum); + error = apply_err("preimage does not contain line %"PRIuZ, linenum); goto done; } @@ -193,7 +193,7 @@ static int apply_hunk( line_num = hunk->hunk.new_start ? hunk->hunk.new_start - 1 : 0; if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) { - error = apply_err("Hunk at line %d did not apply", + error = apply_err("hunk at line %d did not apply", hunk->hunk.new_start); goto done; } diff --git a/src/attr.c b/src/attr.c index d43a15f50..999f41318 100644 --- a/src/attr.c +++ b/src/attr.c @@ -7,8 +7,6 @@ #include "git2/oid.h" #include -GIT__USE_STRMAP - const char *git_attr__true = "[internal]__TRUE__"; const char *git_attr__false = "[internal]__FALSE__"; const char *git_attr__unset = "[internal]__UNSET__"; @@ -209,7 +207,7 @@ int git_attr_foreach( if (git_strmap_exists(seen, assign->name)) continue; - git_strmap_insert(seen, assign->name, assign, error); + git_strmap_insert(seen, assign->name, assign, &error); if (error < 0) goto cleanup; @@ -292,7 +290,7 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session) int error = 0; const char *workdir = git_repository_workdir(repo); git_index *idx = NULL; - git_buf sys = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT; if (attr_session && attr_session->init_setup) return 0; @@ -304,40 +302,45 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session) * definitions will be available for later file parsing */ - error = system_attr_file(&sys, attr_session); + error = system_attr_file(&path, attr_session); if (error == 0) error = preload_attr_file( - repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr); + repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, path.ptr); if (error != GIT_ENOTFOUND) - return error; - - git_buf_free(&sys); + goto out; if ((error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0) - return error; + goto out; + + if ((error = git_repository_item_path(&path, + repo, GIT_REPOSITORY_ITEM_INFO)) < 0) + goto out; if ((error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_FILE, - git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0) - return error; + path.ptr, GIT_ATTR_FILE_INREPO)) < 0) + goto out; if (workdir != NULL && (error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0) - return error; + goto out; if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || (error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0) - return error; + goto out; if (attr_session) attr_session->init_setup = 1; +out: + git_buf_free(&path); + return error; } @@ -472,7 +475,7 @@ static int collect_attr_files( git_vector *files) { int error = 0; - git_buf dir = GIT_BUF_INIT; + git_buf dir = GIT_BUF_INIT, attrfile = GIT_BUF_INIT; const char *workdir = git_repository_workdir(repo); attr_walk_up_info info = { NULL }; @@ -494,9 +497,13 @@ static int collect_attr_files( * - $GIT_PREFIX/etc/gitattributes */ + error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO); + if (error < 0) + goto cleanup; + error = push_attr_file( repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, - git_repository_path(repo), GIT_ATTR_FILE_INREPO); + attrfile.ptr, GIT_ATTR_FILE_INREPO); if (error < 0) goto cleanup; @@ -538,6 +545,7 @@ static int collect_attr_files( cleanup: if (error < 0) release_attr_files(files); + git_buf_free(&attrfile); git_buf_free(&dir); return error; diff --git a/src/attr_file.c b/src/attr_file.c index 11d149358..e30ea5e0c 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -30,7 +30,7 @@ int git_attr_file__new( GITERR_CHECK_ALLOC(attrs); if (git_mutex_init(&attrs->lock) < 0) { - giterr_set(GITERR_OS, "Failed to initialize lock"); + giterr_set(GITERR_OS, "failed to initialize lock"); git__free(attrs); return -1; } @@ -49,7 +49,7 @@ int git_attr_file__clear_rules(git_attr_file *file, bool need_lock) git_attr_rule *rule; if (need_lock && git_mutex_lock(&file->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock attribute file"); + giterr_set(GITERR_OS, "failed to lock attribute file"); return -1; } @@ -140,7 +140,7 @@ int git_attr_file__load( break; } default: - giterr_set(GITERR_INVALID, "Unknown file source %d", source); + giterr_set(GITERR_INVALID, "unknown file source %d", source); return -1; } @@ -212,7 +212,7 @@ int git_attr_file__out_of_date( } default: - giterr_set(GITERR_INVALID, "Invalid file type %d", file->source); + giterr_set(GITERR_INVALID, "invalid file type %d", file->source); return -1; } } @@ -238,7 +238,7 @@ int git_attr_file__parse_buffer( context = attrs->entry->path; if (git_mutex_lock(&attrs->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock attribute file"); + giterr_set(GITERR_OS, "failed to lock attribute file"); return -1; } @@ -395,9 +395,13 @@ bool git_attr_fnmatch__match( if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) { bool samename; - /* for attribute checks or root ignore checks, fail match */ + /* + * for attribute checks or checks at the root of this match's + * containing_dir (or root of the repository if no containing_dir), + * do not match. + */ if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) || - path->basename == path->path) + path->basename == relpath) return false; flags |= FNM_LEADING_DIR; diff --git a/src/attr_file.h b/src/attr_file.h index 388ecf4c0..a9af2403a 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -15,7 +15,7 @@ #include "fileops.h" #define GIT_ATTR_FILE ".gitattributes" -#define GIT_ATTR_FILE_INREPO "info/attributes" +#define GIT_ATTR_FILE_INREPO "attributes" #define GIT_ATTR_FILE_SYSTEM "gitattributes" #define GIT_ATTR_FILE_XDG "attributes" diff --git a/src/attrcache.c b/src/attrcache.c index a57110684..54161894b 100644 --- a/src/attrcache.c +++ b/src/attrcache.c @@ -5,14 +5,12 @@ #include "sysdir.h" #include "ignore.h" -GIT__USE_STRMAP - GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache) { GIT_UNUSED(cache); /* avoid warning if threading is off */ if (git_mutex_lock(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to get attr cache lock"); + giterr_set(GITERR_OS, "unable to get attr cache lock"); return -1; } return 0; @@ -82,7 +80,7 @@ static int attr_cache_make_entry( &entry, git_repository_workdir(repo), path, &cache->pool); if (!error) { - git_strmap_insert(cache->files, entry->path, entry, error); + git_strmap_insert(cache->files, entry->path, entry, &error); if (error > 0) error = 0; } @@ -105,8 +103,11 @@ static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file) GIT_REFCOUNT_OWN(file, entry); GIT_REFCOUNT_INC(file); - old = git__compare_and_swap( - &entry->file[file->source], entry->file[file->source], file); + /* + * Replace the existing value if another thread has + * created it in the meantime. + */ + old = git__swap(entry->file[file->source], file); if (old) { GIT_REFCOUNT_OWN(old, NULL); @@ -121,20 +122,22 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) { int error = 0; git_attr_file_entry *entry; + git_attr_file *old = NULL; if (!file) return 0; + if ((error = attr_cache_lock(cache)) < 0) return error; if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL) - file = git__compare_and_swap(&entry->file[file->source], file, NULL); + old = git__compare_and_swap(&entry->file[file->source], file, NULL); attr_cache_unlock(cache); - if (file) { - GIT_REFCOUNT_OWN(file, NULL); - git_attr_file__free(file); + if (old) { + GIT_REFCOUNT_OWN(old, NULL); + git_attr_file__free(old); } return error; @@ -287,14 +290,16 @@ static int attr_cache__lookup_path( const char *cfgval = entry->value; /* expand leading ~/ as needed */ - if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' && - !git_sysdir_find_global_file(&buf, &cfgval[2])) - *out = git_buf_detach(&buf); - else if (cfgval) + if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') { + if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2]))) + *out = git_buf_detach(&buf); + } else if (cfgval) { *out = git__strdup(cfgval); + } } - else if (!git_sysdir_find_xdg_file(&buf, fallback)) + else if (!git_sysdir_find_xdg_file(&buf, fallback)) { *out = git_buf_detach(&buf); + } git_config_entry_free(entry); git_buf_free(&buf); @@ -309,7 +314,7 @@ static void attr_cache__free(git_attr_cache *cache) if (!cache) return; - unlock = (git_mutex_lock(&cache->lock) == 0); + unlock = (attr_cache_lock(cache) == 0); if (cache->files != NULL) { git_attr_file_entry *entry; @@ -345,13 +350,13 @@ static void attr_cache__free(git_attr_cache *cache) cache->cfg_excl_file = NULL; if (unlock) - git_mutex_unlock(&cache->lock); + attr_cache_unlock(cache); git_mutex_free(&cache->lock); git__free(cache); } -int git_attr_cache__do_init(git_repository *repo) +int git_attr_cache__init(git_repository *repo) { int ret = 0; git_attr_cache *cache = git_repository_attr_cache(repo); @@ -365,7 +370,7 @@ int git_attr_cache__do_init(git_repository *repo) /* set up lock */ if (git_mutex_init(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to initialize lock for attr cache"); + giterr_set(GITERR_OS, "unable to initialize lock for attr cache"); git__free(cache); return -1; } @@ -429,11 +434,11 @@ int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) if (macro->assigns.length == 0) return 0; - if (git_mutex_lock(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to get attr cache lock"); + if (attr_cache_lock(cache) < 0) { + giterr_set(GITERR_OS, "unable to get attr cache lock"); error = -1; } else { - git_strmap_insert(macros, macro->match.pattern, macro, error); + git_strmap_insert(macros, macro->match.pattern, macro, &error); git_mutex_unlock(&cache->lock); } diff --git a/src/attrcache.h b/src/attrcache.h index 44e1ffdce..b91edd3e8 100644 --- a/src/attrcache.h +++ b/src/attrcache.h @@ -22,10 +22,7 @@ typedef struct { git_pool pool; } git_attr_cache; -extern int git_attr_cache__do_init(git_repository *repo); - -#define git_attr_cache__init(REPO) \ - (git_repository_attr_cache(REPO) ? 0 : git_attr_cache__do_init(REPO)) +extern int git_attr_cache__init(git_repository *repo); /* get file - loading and reload as needed */ extern int git_attr_cache__get( diff --git a/src/blame_git.c b/src/blame_git.c index 96785c75b..13f5cb47c 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -478,14 +478,15 @@ cleanup: * The blobs of origin and porigin exactly match, so everything origin is * suspected for can be blamed on the parent. */ -static void pass_whole_blame(git_blame *blame, +static int pass_whole_blame(git_blame *blame, git_blame__origin *origin, git_blame__origin *porigin) { git_blame__entry *e; - if (!porigin->blob) - git_object_lookup((git_object**)&porigin->blob, blame->repository, - git_blob_id(origin->blob), GIT_OBJ_BLOB); + if (!porigin->blob && + git_object_lookup((git_object**)&porigin->blob, blame->repository, + git_blob_id(origin->blob), GIT_OBJ_BLOB) < 0) + return -1; for (e=blame->ent; e; e=e->next) { if (!same_suspect(e->suspect, origin)) continue; @@ -493,6 +494,8 @@ static void pass_whole_blame(git_blame *blame, origin_decref(e->suspect); e->suspect = porigin; } + + return 0; } static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) @@ -514,11 +517,12 @@ static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) if (!num_parents) { git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit)); goto finish; - } - else if (num_parents < (int)ARRAY_SIZE(sg_buf)) + } else if (num_parents < (int)ARRAY_SIZE(sg_buf)) memset(sg_buf, 0, sizeof(sg_buf)); - else + else { sg_origin = git__calloc(num_parents, sizeof(*sg_origin)); + GITERR_CHECK_ALLOC(sg_origin); + } for (i=0; iblob && origin->blob && !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) { - pass_whole_blame(blame, origin, porigin); + error = pass_whole_blame(blame, origin, porigin); origin_decref(porigin); goto finish; } diff --git a/src/blob.c b/src/blob.c index 1926c9e58..19d3039fb 100644 --- a/src/blob.c +++ b/src/blob.c @@ -96,7 +96,7 @@ static int write_file_stream( p_close(fd); if (written != file_size || read_len < 0) { - giterr_set(GITERR_OS, "Failed to read file into stream"); + giterr_set(GITERR_OS, "failed to read file into stream"); error = -1; } @@ -142,7 +142,7 @@ static int write_symlink( read_len = p_readlink(path, link_data, link_size); if (read_len != (ssize_t)link_size) { - giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path); + giterr_set(GITERR_OS, "failed to create blob: cannot read symlink '%s'", path); git__free(link_data); return -1; } @@ -186,7 +186,7 @@ int git_blob__create_from_paths( goto done; if (S_ISDIR(st.st_mode)) { - giterr_set(GITERR_ODB, "cannot create blob from '%s'; it is a directory", content_path); + giterr_set(GITERR_ODB, "cannot create blob from '%s': it is a directory", content_path); error = GIT_EDIRECTORY; goto done; } @@ -326,8 +326,8 @@ int git_blob_create_fromstream(git_writestream **out, git_repository *repo, cons stream->parent.close = blob_writestream_close; stream->parent.free = blob_writestream_free; - if ((error = git_buf_joinpath(&path, - git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0) + if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0 + || (error = git_buf_joinpath(&path, path.ptr, "streamed")) < 0) goto cleanup; if ((error = git_filebuf_open_withsize(&stream->fbuf, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, diff --git a/src/branch.c b/src/branch.c index 8d1ed6577..fe4955ad6 100644 --- a/src/branch.c +++ b/src/branch.c @@ -13,6 +13,7 @@ #include "refs.h" #include "remote.h" #include "annotated_commit.h" +#include "worktree.h" #include "git2/branch.h" @@ -33,7 +34,7 @@ static int retrieve_branch_reference( /* OOM */; else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) giterr_set( - GITERR_REFERENCE, "Cannot locate %s branch '%s'", + GITERR_REFERENCE, "cannot locate %s branch '%s'", is_remote ? "remote-tracking" : "local", branch_name); *branch_reference_out = branch; /* will be NULL on error */ @@ -46,7 +47,7 @@ static int not_a_local_branch(const char *reference_name) { giterr_set( GITERR_INVALID, - "Reference '%s' is not a local branch.", reference_name); + "reference '%s' is not a local branch.", reference_name); return -1; } @@ -80,7 +81,7 @@ static int create_branch( } if (is_unmovable_head && force) { - giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is " + giterr_set(GITERR_REFERENCE, "cannot force update branch '%s' as it is " "the current HEAD of the repository.", branch_name); error = -1; goto cleanup; @@ -126,6 +127,31 @@ int git_branch_create_from_annotated( repository, branch_name, commit->commit, commit->description, force); } +static int branch_equals(git_repository *repo, const char *path, void *payload) +{ + git_reference *branch = (git_reference *) payload; + git_reference *head = NULL; + int equal = 0; + + if (git_reference__read_head(&head, repo, path) < 0 || + git_reference_type(head) != GIT_REF_SYMBOLIC) + goto done; + + equal = !git__strcmp(head->target.symbolic, branch->name); + +done: + git_reference_free(head); + return equal; +} + +int git_branch_is_checked_out(const git_reference *branch) +{ + assert(branch && git_reference_is_branch(branch)); + + return git_repository_foreach_head(git_reference_owner(branch), + branch_equals, (void *) branch) == 1; +} + int git_branch_delete(git_reference *branch) { int is_head; @@ -135,7 +161,7 @@ int git_branch_delete(git_reference *branch) assert(branch); if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) { - giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", + giterr_set(GITERR_INVALID, "reference '%s' is not a valid branch.", git_reference_name(branch)); return GIT_ENOTFOUND; } @@ -144,11 +170,17 @@ int git_branch_delete(git_reference *branch) return is_head; if (is_head) { - giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is " + giterr_set(GITERR_REFERENCE, "cannot delete branch '%s' as it is " "the current HEAD of the repository.", git_reference_name(branch)); return -1; } + if (git_reference_is_branch(branch) && git_branch_is_checked_out(branch)) { + giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is " + "the current HEAD of a linked repository.", git_reference_name(branch)); + return -1; + } + if (git_buf_join(&config_section, '.', "branch", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) goto on_error; @@ -306,7 +338,7 @@ int git_branch_name( branch_name += strlen(GIT_REFS_REMOTES_DIR); } else { giterr_set(GITERR_INVALID, - "Reference '%s' is neither a local nor a remote branch.", ref->name); + "reference '%s' is neither a local nor a remote branch.", ref->name); return -1; } *out = branch_name; @@ -436,7 +468,7 @@ int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refna /* Verify that this is a remote branch */ if (!git_reference__is_remote(refname)) { - giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.", + giterr_set(GITERR_INVALID, "reference '%s' is not a remote branch.", refname); error = GIT_ERROR; goto cleanup; @@ -463,7 +495,7 @@ int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refna git_remote_free(remote); giterr_set(GITERR_REFERENCE, - "Reference '%s' is ambiguous", refname); + "reference '%s' is ambiguous", refname); error = GIT_EAMBIGUOUS; goto cleanup; } @@ -477,7 +509,7 @@ int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refna error = git_buf_puts(buf, remote_name); } else { giterr_set(GITERR_REFERENCE, - "Could not determine remote for '%s'", refname); + "could not determine remote for '%s'", refname); error = GIT_ENOTFOUND; } @@ -566,7 +598,7 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name) local = 0; else { giterr_set(GITERR_REFERENCE, - "Cannot set upstream for branch '%s'", shortname); + "cannot set upstream for branch '%s'", shortname); return GIT_ENOTFOUND; } diff --git a/src/buffer.c b/src/buffer.c index d135ebe4a..6dfcbfbe6 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -18,18 +18,19 @@ char git_buf__initbuf[1]; char git_buf__oom[1]; #define ENSURE_SIZE(b, d) \ - if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\ + if ((d) > (b)->asize && git_buf_grow((b), (d)) < 0)\ return -1; -void git_buf_init(git_buf *buf, size_t initial_size) +int git_buf_init(git_buf *buf, size_t initial_size) { buf->asize = 0; buf->size = 0; buf->ptr = git_buf__initbuf; - if (initial_size) - git_buf_grow(buf, initial_size); + ENSURE_SIZE(buf, initial_size); + + return 0; } int git_buf_try_grow( @@ -577,7 +578,7 @@ char *git_buf_detach(git_buf *buf) return data; } -void git_buf_attach(git_buf *buf, char *ptr, size_t asize) +int git_buf_attach(git_buf *buf, char *ptr, size_t asize) { git_buf_free(buf); @@ -588,9 +589,10 @@ void git_buf_attach(git_buf *buf, char *ptr, size_t asize) buf->asize = (asize < buf->size) ? buf->size + 1 : asize; else /* pass 0 to fall back on strlen + 1 */ buf->asize = buf->size + 1; - } else { - git_buf_grow(buf, asize); } + + ENSURE_SIZE(buf, asize); + return 0; } void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size) @@ -724,9 +726,7 @@ int git_buf_join( GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1); - if (git_buf_grow(buf, alloc_len) < 0) - return -1; - assert(buf->ptr); + ENSURE_SIZE(buf, alloc_len); /* fix up internal pointers */ if (offset_a >= 0) @@ -780,8 +780,7 @@ int git_buf_join3( GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b); GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c); GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1); - if (git_buf_grow(buf, len_total) < 0) - return -1; + ENSURE_SIZE(buf, len_total); tgt = buf->ptr; @@ -962,14 +961,14 @@ int git_buf_unquote(git_buf *buf) case '0': case '1': case '2': case '3': if (j == buf->size-3) { giterr_set(GITERR_INVALID, - "Truncated quoted character \\%c", ch); + "truncated quoted character \\%c", ch); return -1; } if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' || buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') { giterr_set(GITERR_INVALID, - "Truncated quoted character \\%c%c%c", + "truncated quoted character \\%c%c%c", buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]); return -1; } @@ -981,7 +980,7 @@ int git_buf_unquote(git_buf *buf) break; default: - giterr_set(GITERR_INVALID, "Invalid quoted character \\%c", ch); + giterr_set(GITERR_INVALID, "invalid quoted character \\%c", ch); return -1; } } @@ -995,6 +994,6 @@ int git_buf_unquote(git_buf *buf) return 0; invalid: - giterr_set(GITERR_INVALID, "Invalid quoted line"); + giterr_set(GITERR_INVALID, "invalid quoted line"); return -1; } diff --git a/src/buffer.h b/src/buffer.h index cdfca6d99..b0aece488 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -34,7 +34,7 @@ GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf) * For the cases where GIT_BUF_INIT cannot be used to do static * initialization. */ -extern void git_buf_init(git_buf *buf, size_t initial_size); +extern int git_buf_init(git_buf *buf, size_t initial_size); /** * Resize the buffer allocation to make more space. @@ -66,13 +66,14 @@ extern int git_buf_try_grow( * library, when providing git_buf's, may wish to provide a NULL ptr for * ease of handling. The buffer routines, however, expect a non-NULL ptr * always. This helper method simply handles NULL input, converting to a - * git_buf__initbuf. + * git_buf__initbuf. If a buffer with a non-NULL ptr is passed in, this method + * assures that the buffer is '\0'-terminated. */ extern void git_buf_sanitize(git_buf *buf); extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b); extern char *git_buf_detach(git_buf *buf); -extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize); +extern int git_buf_attach(git_buf *buf, char *ptr, size_t asize); /* Populates a `git_buf` where the contents are not "owned" by the * buffer, and calls to `git_buf_free` will not free the given buf. diff --git a/src/cache.c b/src/cache.c index ca5173c0d..c92a3a78a 100644 --- a/src/cache.c +++ b/src/cache.c @@ -15,8 +15,6 @@ #include "object.h" #include "git2/oid.h" -GIT__USE_OIDMAP - bool git_cache__enabled = true; ssize_t git_cache__max_storage = (256 * 1024 * 1024); git_atomic_ssize git_cache__current_storage = {0}; @@ -47,13 +45,13 @@ void git_cache_dump_stats(git_cache *cache) { git_cached_obj *object; - if (kh_size(cache->map) == 0) + if (git_cache_size(cache) == 0) return; - printf("Cache %p: %d items cached, %"PRIdZ" bytes\n", - cache, kh_size(cache->map), cache->used_memory); + printf("Cache %p: %"PRIuZ" items cached, %"PRIdZ" bytes\n", + cache, git_cache_size(cache), cache->used_memory); - kh_foreach_value(cache->map, object, { + git_oidmap_foreach_value(cache->map, object, { char oid_str[9]; printf(" %s%c %s (%"PRIuZ")\n", git_object_type2string(object->type), @@ -70,7 +68,7 @@ int git_cache_init(git_cache *cache) cache->map = git_oidmap_alloc(); GITERR_CHECK_ALLOC(cache->map); if (git_rwlock_init(&cache->lock)) { - giterr_set(GITERR_OS, "Failed to initialize cache rwlock"); + giterr_set(GITERR_OS, "failed to initialize cache rwlock"); return -1; } return 0; @@ -81,14 +79,14 @@ static void clear_cache(git_cache *cache) { git_cached_obj *evict = NULL; - if (kh_size(cache->map) == 0) + if (git_cache_size(cache) == 0) return; - kh_foreach_value(cache->map, evict, { + git_oidmap_foreach_value(cache->map, evict, { git_cached_obj_decref(evict); }); - kh_clear(oid, cache->map); + git_oidmap_clear(cache->map); git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory); cache->used_memory = 0; } @@ -119,22 +117,22 @@ static void cache_evict_entries(git_cache *cache) ssize_t evicted_memory = 0; /* do not infinite loop if there's not enough entries to evict */ - if (evict_count > kh_size(cache->map)) { + if (evict_count > git_cache_size(cache)) { clear_cache(cache); return; } while (evict_count > 0) { - khiter_t pos = seed++ % kh_end(cache->map); + khiter_t pos = seed++ % git_oidmap_end(cache->map); - if (kh_exist(cache->map, pos)) { - git_cached_obj *evict = kh_val(cache->map, pos); + if (git_oidmap_has_data(cache->map, pos)) { + git_cached_obj *evict = git_oidmap_value_at(cache->map, pos); evict_count--; evicted_memory += evict->size; git_cached_obj_decref(evict); - kh_del(oid, cache->map, pos); + git_oidmap_delete_at(cache->map, pos); } } @@ -156,9 +154,9 @@ static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags) if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0) return NULL; - pos = kh_get(oid, cache->map, oid); - if (pos != kh_end(cache->map)) { - entry = kh_val(cache->map, pos); + pos = git_oidmap_lookup_index(cache->map, oid); + if (git_oidmap_valid_index(cache->map, pos)) { + entry = git_oidmap_value_at(cache->map, pos); if (flags && entry->flags != flags) { entry = NULL; @@ -193,16 +191,14 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) if (git_cache__current_storage.val > git_cache__max_storage) cache_evict_entries(cache); - pos = kh_get(oid, cache->map, &entry->oid); + pos = git_oidmap_lookup_index(cache->map, &entry->oid); /* not found */ - if (pos == kh_end(cache->map)) { + if (!git_oidmap_valid_index(cache->map, pos)) { int rval; - pos = kh_put(oid, cache->map, &entry->oid, &rval); + git_oidmap_insert(cache->map, &entry->oid, entry, &rval); if (rval >= 0) { - kh_key(cache->map, pos) = &entry->oid; - kh_val(cache->map, pos) = entry; git_cached_obj_incref(entry); cache->used_memory += entry->size; git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size); @@ -210,7 +206,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) } /* found */ else { - git_cached_obj *stored_entry = kh_val(cache->map, pos); + git_cached_obj *stored_entry = git_oidmap_value_at(cache->map, pos); if (stored_entry->flags == entry->flags) { git_cached_obj_decref(entry); @@ -221,8 +217,8 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) git_cached_obj_decref(stored_entry); git_cached_obj_incref(entry); - kh_key(cache->map, pos) = &entry->oid; - kh_val(cache->map, pos) = entry; + git_oidmap_set_key_at(cache->map, pos, &entry->oid); + git_oidmap_set_value_at(cache->map, pos, entry); } else { /* NO OP */ } diff --git a/src/cache.h b/src/cache.h index 697123739..0f0bfcf5d 100644 --- a/src/cache.h +++ b/src/cache.h @@ -53,7 +53,7 @@ void *git_cache_get_any(git_cache *cache, const git_oid *oid); GIT_INLINE(size_t) git_cache_size(git_cache *cache) { - return (size_t)kh_size(cache->map); + return (size_t)git_oidmap_size(cache->map); } GIT_INLINE(void) git_cached_obj_incref(void *_obj) diff --git a/src/checkout.c b/src/checkout.c index 62950913e..25018d291 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -35,8 +35,6 @@ #include "pool.h" #include "strmap.h" -GIT__USE_STRMAP - /* See docs/checkout-internals.md for more information */ enum { @@ -372,10 +370,8 @@ static int checkout_action_wd_only( */ const git_index_entry *e = git_index_get_byindex(data->index, pos); - if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) { - notify = GIT_CHECKOUT_NOTIFY_DIRTY; - remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0); - } + if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) + return git_iterator_advance_into(wditem, workdir); } } @@ -1021,13 +1017,13 @@ static int checkout_conflicts_load_byname_entry( *theirs_out = NULL; if (!name_entry->ancestor) { - giterr_set(GITERR_INDEX, "A NAME entry exists without an ancestor"); + giterr_set(GITERR_INDEX, "a NAME entry exists without an ancestor"); error = -1; goto done; } if (!name_entry->ours && !name_entry->theirs) { - giterr_set(GITERR_INDEX, "A NAME entry exists without an ours or theirs"); + giterr_set(GITERR_INDEX, "a NAME entry exists without an ours or theirs"); error = -1; goto done; } @@ -1035,7 +1031,7 @@ static int checkout_conflicts_load_byname_entry( if ((ancestor = checkout_conflicts_search_ancestor(data, name_entry->ancestor)) == NULL) { giterr_set(GITERR_INDEX, - "A NAME entry referenced ancestor entry '%s' which does not exist in the main index", + "a NAME entry referenced ancestor entry '%s' which does not exist in the main index", name_entry->ancestor); error = -1; goto done; @@ -1047,7 +1043,7 @@ static int checkout_conflicts_load_byname_entry( else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL || ours->ours == NULL) { giterr_set(GITERR_INDEX, - "A NAME entry referenced our entry '%s' which does not exist in the main index", + "a NAME entry referenced our entry '%s' which does not exist in the main index", name_entry->ours); error = -1; goto done; @@ -1062,7 +1058,7 @@ static int checkout_conflicts_load_byname_entry( else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL || theirs->theirs == NULL) { giterr_set(GITERR_INDEX, - "A NAME entry referenced their entry '%s' which does not exist in the main index", + "a NAME entry referenced their entry '%s' which does not exist in the main index", name_entry->theirs); error = -1; goto done; @@ -1161,7 +1157,7 @@ static int checkout_conflicts_mark_directoryfile( if ((error = git_index_find(&j, index, path)) < 0) { if (error == GIT_ENOTFOUND) giterr_set(GITERR_INDEX, - "Index inconsistency, could not find entry for expected conflict '%s'", path); + "index inconsistency, could not find entry for expected conflict '%s'", path); goto done; } @@ -1169,7 +1165,7 @@ static int checkout_conflicts_mark_directoryfile( for (; j < len; j++) { if ((entry = git_index_get_byindex(index, j)) == NULL) { giterr_set(GITERR_INDEX, - "Index inconsistency, truncated index while loading expected conflict '%s'", path); + "index inconsistency, truncated index while loading expected conflict '%s'", path); error = -1; goto done; } @@ -1254,14 +1250,14 @@ static int checkout_verify_paths( if (action & CHECKOUT_ACTION__REMOVE) { if (!git_path_isvalid(repo, delta->old_file.path, flags)) { - giterr_set(GITERR_CHECKOUT, "Cannot remove invalid path '%s'", delta->old_file.path); + giterr_set(GITERR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path); return -1; } } if (action & ~CHECKOUT_ACTION__REMOVE) { if (!git_path_isvalid(repo, delta->new_file.path, flags)) { - giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->new_file.path); + giterr_set(GITERR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path); return -1; } } @@ -1430,7 +1426,7 @@ static int mkpath2file( */ error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES); } else if (errno != ENOENT) { - giterr_set(GITERR_OS, "Failed to stat file '%s'", path); + giterr_set(GITERR_OS, "failed to stat '%s'", path); return GIT_EEXISTS; } else { giterr_clear(); @@ -1454,7 +1450,7 @@ static int checkout_stream_write( int ret; if ((ret = p_write(stream->fd, buffer, len)) < 0) - giterr_set(GITERR_OS, "Could not write to '%s'", stream->path); + giterr_set(GITERR_OS, "could not write to '%s'", stream->path); return ret; } @@ -1503,7 +1499,7 @@ static int blob_content_to_file( mode = GIT_FILEMODE_BLOB; if ((fd = p_open(path, flags, mode)) < 0) { - giterr_set(GITERR_OS, "Could not open '%s' for writing", path); + giterr_set(GITERR_OS, "could not open '%s' for writing", path); return fd; } @@ -1540,7 +1536,7 @@ static int blob_content_to_file( data->perfdata.stat_calls++; if ((error = p_stat(path, st)) < 0) { - giterr_set(GITERR_OS, "Error statting '%s'", path); + giterr_set(GITERR_OS, "failed to stat '%s'", path); return error; } @@ -1567,7 +1563,7 @@ static int blob_content_to_link( if (data->can_symlink) { if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0) - giterr_set(GITERR_OS, "Could not create symlink %s", path); + giterr_set(GITERR_OS, "could not create symlink %s", path); } else { error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path); } @@ -1576,7 +1572,7 @@ static int blob_content_to_link( data->perfdata.stat_calls++; if ((error = p_lstat(path, st)) < 0) - giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path); + giterr_set(GITERR_CHECKOUT, "could not stat symlink %s", path); st->st_mode = GIT_FILEMODE_LINK; } @@ -1621,7 +1617,7 @@ static int checkout_submodule_update_index( data->perfdata.stat_calls++; if (p_stat(fullpath->ptr, &st) < 0) { giterr_set( - GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path); + GITERR_CHECKOUT, "could not stat submodule %s\n", file->path); return GIT_ENOTFOUND; } @@ -1694,7 +1690,7 @@ static int checkout_safe_for_update_only( return 0; /* otherwise, stat error and no update */ - giterr_set(GITERR_OS, "Failed to stat file '%s'", path); + giterr_set(GITERR_OS, "failed to stat '%s'", path); return -1; } @@ -1966,7 +1962,7 @@ static int checkout_path_suffixed(git_buf *path, const char *suffix) if (i == INT_MAX) { git_buf_truncate(path, path_len); - giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path->ptr); + giterr_set(GITERR_CHECKOUT, "could not write '%s': working directory file exists", path->ptr); return GIT_EEXISTS; } @@ -2097,7 +2093,7 @@ static int checkout_write_merge( goto done; if (result.path == NULL || result.mode == 0) { - giterr_set(GITERR_CHECKOUT, "Could not merge contents of file"); + giterr_set(GITERR_CHECKOUT, "could not merge contents of file"); error = GIT_ECONFLICT; goto done; } @@ -2321,8 +2317,6 @@ static void checkout_data_clear(checkout_data *data) git__free(data->pfx); data->pfx = NULL; - git_strmap_free(data->mkdir_map); - git_buf_free(&data->target_path); git_buf_free(&data->tmp); @@ -2330,6 +2324,7 @@ static void checkout_data_clear(checkout_data *data) data->index = NULL; git_strmap_free(data->mkdir_map); + data->mkdir_map = NULL; git_attr_session__free(&data->attr_session); } @@ -2345,7 +2340,7 @@ static int checkout_data_init( memset(data, 0, sizeof(*data)); if (!repo) { - giterr_set(GITERR_CHECKOUT, "Cannot checkout nothing"); + giterr_set(GITERR_CHECKOUT, "cannot checkout nothing"); return -1; } @@ -2553,6 +2548,10 @@ int git_checkout_iterator( GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; baseline_opts.start = data.pfx; baseline_opts.end = data.pfx; + if (opts && (opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) { + baseline_opts.pathlist.count = opts->paths.count; + baseline_opts.pathlist.strings = opts->paths.strings; + } if (data.opts.baseline_index) { if ((error = git_iterator_for_index( @@ -2647,7 +2646,7 @@ int git_checkout_index( if (!index && !repo) { giterr_set(GITERR_CHECKOUT, - "Must provide either repository or index to checkout"); + "must provide either repository or index to checkout"); return -1; } @@ -2655,7 +2654,7 @@ int git_checkout_index( git_index_owner(index) && git_index_owner(index) != repo) { giterr_set(GITERR_CHECKOUT, - "Index to checkout does not match repository"); + "index to checkout does not match repository"); return -1; } else if(index && repo && !git_index_owner(index)) { GIT_REFCOUNT_OWN(index, repo); @@ -2694,12 +2693,12 @@ int git_checkout_tree( if (!treeish && !repo) { giterr_set(GITERR_CHECKOUT, - "Must provide either repository or tree to checkout"); + "must provide either repository or tree to checkout"); return -1; } if (treeish && repo && git_object_owner(treeish) != repo) { giterr_set(GITERR_CHECKOUT, - "Object to checkout does not match repository"); + "object to checkout does not match repository"); return -1; } @@ -2709,7 +2708,7 @@ int git_checkout_tree( if (treeish) { if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) { giterr_set( - GITERR_CHECKOUT, "Provided object cannot be peeled to a tree"); + GITERR_CHECKOUT, "provided object cannot be peeled to a tree"); return -1; } } diff --git a/src/cherrypick.c b/src/cherrypick.c index c92975194..d8b6858ae 100644 --- a/src/cherrypick.c +++ b/src/cherrypick.c @@ -28,7 +28,7 @@ static int write_cherrypick_head( git_buf file_path = GIT_BUF_INIT; int error = 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRYPICK_HEAD_FILE)) >= 0 && + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_CHERRYPICK_HEAD_FILE)) >= 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) >= 0 && (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) error = git_filebuf_commit(&file); @@ -49,7 +49,7 @@ static int write_merge_msg( git_buf file_path = GIT_BUF_INIT; int error = 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) < 0 || (error = git_filebuf_printf(&file, "%s", commit_msg)) < 0) goto cleanup; @@ -130,13 +130,13 @@ int git_cherrypick_commit( if (git_commit_parentcount(cherrypick_commit) > 1) { if (!mainline) return cherrypick_seterr(cherrypick_commit, - "Mainline branch is not specified but %s is a merge commit"); + "mainline branch is not specified but %s is a merge commit"); parent = mainline; } else { if (mainline) return cherrypick_seterr(cherrypick_commit, - "Mainline branch specified but %s is not a merge commit"); + "mainline branch specified but %s is not a merge commit"); parent = git_commit_parentcount(cherrypick_commit); } diff --git a/src/clone.c b/src/clone.c index 0d4756e28..16ddface2 100644 --- a/src/clone.c +++ b/src/clone.c @@ -513,9 +513,8 @@ static int clone_local_into(git_repository *repo, git_remote *remote, const git_ return error; } - git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR); - git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR); - if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) { + if (git_repository_item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0 + || git_repository_item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) { error = -1; goto cleanup; } diff --git a/src/commit.c b/src/commit.c index 76e6dcbc9..4a340058a 100644 --- a/src/commit.c +++ b/src/commit.c @@ -159,6 +159,9 @@ static int git_commit__create_internal( if (git_repository_odb__weakptr(&odb, repo) < 0) goto cleanup; + if (git_odb__freshen(odb, tree) < 0) + goto cleanup; + if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0) goto cleanup; @@ -468,7 +471,7 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj) return 0; bad_buffer: - giterr_set(GITERR_OBJECT, "Failed to parse bad commit object"); + giterr_set(GITERR_OBJECT, "failed to parse bad commit object"); return -1; } @@ -598,7 +601,7 @@ int git_commit_parent( parent_id = git_commit_parent_id(commit, n); if (parent_id == NULL) { - giterr_set(GITERR_INVALID, "Parent %u does not exist", n); + giterr_set(GITERR_INVALID, "parent %u does not exist", n); return GIT_ENOTFOUND; } @@ -642,7 +645,7 @@ int git_commit_header_field(git_buf *out, const git_commit *commit, const char * { const char *eol, *buf = commit->raw_header; - git_buf_sanitize(out); + git_buf_clear(out); while ((eol = strchr(buf, '\n'))) { /* We can skip continuations here */ @@ -706,8 +709,8 @@ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_r const char *h, *eol; int error; - git_buf_sanitize(signature); - git_buf_sanitize(signed_data); + git_buf_clear(signature); + git_buf_clear(signed_data); if (!field) field = "gpgsig"; @@ -766,8 +769,9 @@ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_r if (git_buf_oom(signature)) goto oom; + error = git_buf_puts(signed_data, eol+1); git_odb_object_free(obj); - return git_buf_puts(signed_data, eol+1); + return error; } giterr_set(GITERR_OBJECT, "this commit is not signed"); diff --git a/src/commit_list.c b/src/commit_list.c index a1681ffae..3bba58c27 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -61,7 +61,7 @@ static int commit_error(git_commit_list_node *commit, const char *msg) git_oid_fmt(commit_oid, &commit->oid); commit_oid[GIT_OID_HEXSZ] = '\0'; - giterr_set(GITERR_ODB, "Failed to parse commit %s - %s", commit_oid, msg); + giterr_set(GITERR_ODB, "failed to parse commit %s - %s", commit_oid, msg); return -1; } @@ -191,7 +191,7 @@ int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit) return error; if (obj->cached.type != GIT_OBJ_COMMIT) { - giterr_set(GITERR_INVALID, "Object is no commit object"); + giterr_set(GITERR_INVALID, "object is no commit object"); error = -1; } else error = commit_quick_parse( diff --git a/src/common.h b/src/common.h index f12cc98cf..e566aeabd 100644 --- a/src/common.h +++ b/src/common.h @@ -188,7 +188,7 @@ GIT_INLINE(int) giterr__check_version(const void *structure, unsigned int expect if (actual > 0 && actual <= expected_max) return 0; - giterr_set(GITERR_INVALID, "Invalid version %d on %s", actual, name); + giterr_set(GITERR_INVALID, "invalid version %d on %s", actual, name); return -1; } #define GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) return -1 diff --git a/src/config.c b/src/config.c index 403b7090d..169a62880 100644 --- a/src/config.c +++ b/src/config.c @@ -109,7 +109,7 @@ int git_config_add_file_ondisk( res = p_stat(path, &st); if (res < 0 && errno != ENOENT) { - giterr_set(GITERR_CONFIG, "Error stat'ing config file '%s'", path); + giterr_set(GITERR_CONFIG, "failed to stat '%s'", path); return -1; } @@ -203,7 +203,7 @@ static int find_internal_file_by_level( if (pos == -1) { giterr_set(GITERR_CONFIG, - "No config file exists for the given level '%i'", (int)level); + "no config file exists for the given level '%i'", (int)level); return GIT_ENOTFOUND; } @@ -218,7 +218,7 @@ static int duplicate_level(void **old_raw, void *new_raw) GIT_UNUSED(new_raw); - giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (int)(*old)->level); + giterr_set(GITERR_CONFIG, "a file with the same level (%i) has already been added to the config", (int)(*old)->level); return GIT_EEXISTS; } @@ -576,22 +576,50 @@ int git_config_foreach_match( * Setters **************/ -static int config_error_nofiles(const char *name) +typedef enum { + BACKEND_USE_SET, + BACKEND_USE_DELETE +} backend_use; + +static const char *uses[] = { + "set", + "delete" +}; + +static int get_backend_for_use(git_config_backend **out, + git_config *cfg, const char *name, backend_use use) { + size_t i; + file_internal *f; + + *out = NULL; + + if (git_vector_length(&cfg->files) == 0) { + giterr_set(GITERR_CONFIG, + "cannot %s value for '%s' when no config files exist", + uses[use], name); + return GIT_ENOTFOUND; + } + + git_vector_foreach(&cfg->files, i, f) { + if (!f->file->readonly) { + *out = f->file; + return 0; + } + } + giterr_set(GITERR_CONFIG, - "Cannot set value for '%s' when no config files exist", name); + "cannot %s value for '%s' when all config files are readonly", + uses[use], name); return GIT_ENOTFOUND; } int git_config_delete_entry(git_config *cfg, const char *name) { git_config_backend *file; - file_internal *internal; - internal = git_vector_get(&cfg->files, 0); - if (!internal || !internal->file) - return config_error_nofiles(name); - file = internal->file; + if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0) + return GIT_ENOTFOUND; return file->del(file, name); } @@ -617,17 +645,14 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) { int error; git_config_backend *file; - file_internal *internal; if (!value) { - giterr_set(GITERR_CONFIG, "The value to set cannot be NULL"); + giterr_set(GITERR_CONFIG, "the value to set cannot be NULL"); return -1; } - internal = git_vector_get(&cfg->files, 0); - if (!internal || !internal->file) - return config_error_nofiles(name); - file = internal->file; + if (get_backend_for_use(&file, cfg, name, BACKEND_USE_SET) < 0) + return GIT_ENOTFOUND; error = file->set(file, name, value); @@ -674,7 +699,7 @@ int git_config__update_entry( static int config_error_notfound(const char *name) { - giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); + giterr_set(GITERR_CONFIG, "config value '%s' was not found", name); return GIT_ENOTFOUND; } @@ -1032,12 +1057,9 @@ on_error: int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) { git_config_backend *file; - file_internal *internal; - internal = git_vector_get(&cfg->files, 0); - if (!internal || !internal->file) - return config_error_nofiles(name); - file = internal->file; + if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0) + return GIT_ENOTFOUND; return file->set_multivar(file, name, regexp, value); } @@ -1045,12 +1067,9 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp) { git_config_backend *file; - file_internal *internal; - internal = git_vector_get(&cfg->files, 0); - if (!internal || !internal->file) - return config_error_nofiles(name); - file = internal->file; + if (get_backend_for_use(&file, cfg, name, BACKEND_USE_DELETE) < 0) + return GIT_ENOTFOUND; return file->del_multivar(file, name, regexp); } @@ -1236,7 +1255,7 @@ int git_config_lookup_map_value( } fail_parse: - giterr_set(GITERR_CONFIG, "Failed to map '%s'", value); + giterr_set(GITERR_CONFIG, "failed to map '%s'", value); return -1; } @@ -1270,7 +1289,7 @@ int git_config_parse_bool(int *out, const char *value) return 0; } - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value); + giterr_set(GITERR_CONFIG, "failed to parse '%s' as a boolean value", value); return -1; } @@ -1313,7 +1332,7 @@ int git_config_parse_int64(int64_t *out, const char *value) } fail_parse: - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value ? value : "(null)"); + giterr_set(GITERR_CONFIG, "failed to parse '%s' as an integer", value ? value : "(null)"); return -1; } @@ -1333,15 +1352,12 @@ int git_config_parse_int32(int32_t *out, const char *value) return 0; fail_parse: - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value ? value : "(null)"); + giterr_set(GITERR_CONFIG, "failed to parse '%s' as a 32-bit integer", value ? value : "(null)"); return -1; } int git_config_parse_path(git_buf *out, const char *value) { - int error = 0; - const git_buf *home; - assert(out && value); git_buf_sanitize(out); @@ -1352,16 +1368,7 @@ int git_config_parse_path(git_buf *out, const char *value) return -1; } - if ((error = git_sysdir_get(&home, GIT_SYSDIR_GLOBAL)) < 0) - return error; - - git_buf_sets(out, home->ptr); - git_buf_puts(out, value + 1); - - if (git_buf_oom(out)) - return -1; - - return 0; + return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL); } return git_buf_sets(out, value); @@ -1398,7 +1405,7 @@ int git_config__normalize_name(const char *in, char **out) invalid: git__free(name); - giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in); + giterr_set(GITERR_CONFIG, "invalid config item name '%s'", in); return GIT_EINVALIDSPEC; } @@ -1461,7 +1468,7 @@ int git_config_rename_section( replace.ptr, strchr(replace.ptr, '.'))) < 0) { giterr_set( - GITERR_CONFIG, "Invalid config section '%s'", new_section_name); + GITERR_CONFIG, "invalid config section '%s'", new_section_name); goto cleanup; } diff --git a/src/config_cache.c b/src/config_cache.c index dbea871b9..840722274 100644 --- a/src/config_cache.c +++ b/src/config_cache.c @@ -78,6 +78,7 @@ static struct map_data _cvar_maps[] = { {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT }, {"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT }, {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT }, + {"core.fsyncobjectfiles", NULL, 0, GIT_FSYNCOBJECTFILES_DEFAULT }, }; int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar) diff --git a/src/config_file.c b/src/config_file.c index 9ff021e7e..e15d57bbb 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -21,8 +21,6 @@ #include #include -GIT__USE_STRMAP - typedef struct cvar_t { struct cvar_t *next; git_config_entry *entry; @@ -126,7 +124,7 @@ static int config_snapshot(git_config_backend **out, git_config_backend *in); static void set_parse_error(struct reader *reader, int col, const char *error_str) { - giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)", + giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)", error_str, reader->file_path, reader->line_number, col); } @@ -179,7 +177,7 @@ static int append_entry(git_strmap *values, cvar_t *var) pos = git_strmap_lookup_index(values, var->entry->name); if (!git_strmap_valid_index(values, pos)) { - git_strmap_insert(values, var->entry->name, var, error); + git_strmap_insert(values, var->entry->name, var, &error); } else { existing = git_strmap_value_at(values, pos); while (existing->next != NULL) { @@ -233,7 +231,7 @@ static refcounted_strmap *refcounted_strmap_take(diskfile_header *h) refcounted_strmap *map; if (git_mutex_lock(&h->values_mutex) < 0) { - giterr_set(GITERR_OS, "Failed to lock config backend"); + giterr_set(GITERR_OS, "failed to lock config backend"); return NULL; } @@ -322,7 +320,7 @@ static int config__refresh(git_config_backend *cfg) goto out; if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) { - giterr_set(GITERR_OS, "Failed to lock config backend"); + giterr_set(GITERR_OS, "failed to lock config backend"); goto out; } @@ -479,7 +477,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val cvar_t *existing = git_strmap_value_at(values, pos); if (existing->next != NULL) { - giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set"); + giterr_set(GITERR_CONFIG, "multivar incompatible with simple set"); ret = -1; goto out; } @@ -611,7 +609,7 @@ static int config_delete(git_config_backend *cfg, const char *name) if (!git_strmap_valid_index(values, pos)) { refcounted_strmap_free(map); - giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); + giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name); return GIT_ENOTFOUND; } @@ -619,7 +617,7 @@ static int config_delete(git_config_backend *cfg, const char *name) refcounted_strmap_free(map); if (var->next != NULL) { - giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete"); + giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete"); return -1; } @@ -651,7 +649,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con if (!git_strmap_valid_index(values, pos)) { refcounted_strmap_free(map); git__free(key); - giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); + giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name); return GIT_ENOTFOUND; } @@ -1029,7 +1027,7 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con first_quote = strchr(line, '"'); if (first_quote == NULL) { set_parse_error(reader, 0, "Missing quotation marks in section header"); - return -1; + goto end_error; } last_quote = strrchr(line, '"'); @@ -1037,14 +1035,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con if (quoted_len == 0) { set_parse_error(reader, 0, "Missing closing quotation mark in section header"); - return -1; + goto end_error; } GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len); GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); - git_buf_grow(&buf, alloc_len); - git_buf_printf(&buf, "%s.", base_name); + if (git_buf_grow(&buf, alloc_len) < 0 || + git_buf_printf(&buf, "%s.", base_name) < 0) + goto end_error; rpos = 0; @@ -1060,8 +1059,7 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con switch (c) { case 0: set_parse_error(reader, 0, "Unexpected end-of-line in section header"); - git_buf_free(&buf); - return -1; + goto end_error; case '"': goto end_parse; @@ -1071,8 +1069,7 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con if (c == 0) { set_parse_error(reader, rpos, "Unexpected end-of-line in section header"); - git_buf_free(&buf); - return -1; + goto end_error; } default: @@ -1084,6 +1081,9 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con } while (line + rpos < last_quote); end_parse: + if (git_buf_oom(&buf)) + goto end_error; + if (line[rpos] != '"' || line[rpos + 1] != ']') { set_parse_error(reader, rpos, "Unexpected text after closing quotes"); git_buf_free(&buf); @@ -1092,6 +1092,11 @@ end_parse: *section_name = git_buf_detach(&buf); return 0; + +end_error: + git_buf_free(&buf); + + return -1; } static int parse_section_header(struct reader *reader, char **section_out) @@ -1251,7 +1256,7 @@ static int included_path(git_buf *out, const char *dir, const char *path) { /* From the user's home */ if (path[0] == '~' && path[1] == '/') - return git_sysdir_find_global_file(out, &path[1]); + return git_sysdir_expand_global_file(out, &path[1]); return git_path_join_unrooted(out, path, dir, NULL); } @@ -1262,7 +1267,7 @@ static const char *escaped = "\n\t\b\"\\"; /* Escape the values to write them to the file */ static char *escape_value(const char *ptr) { - git_buf buf = GIT_BUF_INIT; + git_buf buf; size_t len; const char *esc; @@ -1272,7 +1277,8 @@ static char *escape_value(const char *ptr) if (!len) return git__calloc(1, sizeof(char)); - git_buf_grow(&buf, len); + if (git_buf_init(&buf, len) < 0) + return NULL; while (*ptr != '\0') { if ((esc = strchr(escaped, *ptr)) != NULL) { @@ -1325,7 +1331,7 @@ static int unescape_line( *fixed++ = escaped[esc - escapes]; } else { git__free(str); - giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr); + giterr_set(GITERR_CONFIG, "invalid escape at %s", ptr); return -1; } } @@ -1639,7 +1645,7 @@ static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct re struct parse_data parse_data; if (depth >= MAX_INCLUDE_DEPTH) { - giterr_set(GITERR_CONFIG, "Maximum config include depth reached"); + giterr_set(GITERR_CONFIG, "maximum config include depth reached"); return -1; } diff --git a/src/config_file.h b/src/config_file.h index 1c52892c3..654e6cacf 100644 --- a/src/config_file.h +++ b/src/config_file.h @@ -7,6 +7,7 @@ #ifndef INCLUDE_config_file_h__ #define INCLUDE_config_file_h__ +#include "git2/sys/config.h" #include "git2/config.h" GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level) diff --git a/src/crlf.c b/src/crlf.c index 11895b19f..b8ae5cda1 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -218,7 +218,7 @@ static const char *line_ending(struct crlf_attrs *ca) return "\r\n"; line_ending_error: - giterr_set(GITERR_INVALID, "Invalid input to line ending filter"); + giterr_set(GITERR_INVALID, "invalid input to line ending filter"); return NULL; } diff --git a/src/delta.c b/src/delta.c index dc45697b6..073cba7c6 100644 --- a/src/delta.c +++ b/src/delta.c @@ -131,7 +131,7 @@ static int lookup_index_alloc( GITERR_CHECK_ALLOC_ADD(&index_len, index_len, hash_len); if (!git__is_ulong(index_len)) { - giterr_set(GITERR_NOMEMORY, "Overly large delta"); + giterr_set(GITERR_NOMEMORY, "overly large delta"); return -1; } @@ -544,12 +544,12 @@ int git_delta_apply( * base object, resulting in data corruption or segfault. */ if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) { - giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data"); + giterr_set(GITERR_INVALID, "failed to apply delta: base size does not match given data"); return -1; } if (hdr_sz(&res_sz, &delta, delta_end) < 0) { - giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data"); + giterr_set(GITERR_INVALID, "failed to apply delta: base size does not match given data"); return -1; } @@ -614,6 +614,6 @@ fail: *out = NULL; *out_len = 0; - giterr_set(GITERR_INVALID, "Failed to apply delta"); + giterr_set(GITERR_INVALID, "failed to apply delta"); return -1; } diff --git a/src/describe.c b/src/describe.c index fc48fbde4..4a1e25378 100644 --- a/src/describe.c +++ b/src/describe.c @@ -19,8 +19,6 @@ #include "vector.h" #include "repository.h" -GIT__USE_OIDMAP - /* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */ struct commit_name { @@ -127,7 +125,7 @@ static int add_to_known_names( if (!found) { int ret; - git_oidmap_insert(names, &e->peeled, e, ret); + git_oidmap_insert(names, &e->peeled, e, &ret); if (ret < 0) return -1; } @@ -335,14 +333,14 @@ static int display_name(git_buf *buf, git_repository *repo, struct commit_name * { if (n->prio == 2 && !n->tag) { if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) { - giterr_set(GITERR_TAG, "Annotated tag '%s' not available", n->path); + giterr_set(GITERR_TAG, "annotated tag '%s' not available", n->path); return -1; } } if (n->tag && !n->name_checked) { if (!git_tag_name(n->tag)) { - giterr_set(GITERR_TAG, "Annotated tag '%s' has no embedded name", n->path); + giterr_set(GITERR_TAG, "annotated tag '%s' has no embedded name", n->path); return -1; } @@ -471,7 +469,7 @@ static int describe( if (!data->opts->max_candidates_tags) { error = describe_not_found( git_commit_id(commit), - "Cannot describe - no tag exactly matches '%s'"); + "cannot describe - no tag exactly matches '%s'"); goto cleanup; } @@ -564,15 +562,15 @@ static int describe( } if (unannotated_cnt) { error = describe_not_found(git_commit_id(commit), - "Cannot describe - " - "No annotated tags can describe '%s'." - "However, there were unannotated tags."); + "cannot describe - " + "no annotated tags can describe '%s'; " + "however, there were unannotated tags."); goto cleanup; } else { error = describe_not_found(git_commit_id(commit), - "Cannot describe - " - "No tags can describe '%s'."); + "cannot describe - " + "no tags can describe '%s'."); goto cleanup; } } @@ -695,8 +693,8 @@ int git_describe_commit( goto cleanup; if (git_oidmap_size(data.names) == 0 && !opts->show_commit_oid_as_fallback) { - giterr_set(GITERR_DESCRIBE, "Cannot describe - " - "No reference found, cannot describe anything."); + giterr_set(GITERR_DESCRIBE, "cannot describe - " + "no reference found, cannot describe anything."); error = -1; goto cleanup; } @@ -793,7 +791,7 @@ int git_describe_format(git_buf *out, const git_describe_result *result, const g if (opts.always_use_long_format && opts.abbreviated_size == 0) { - giterr_set(GITERR_DESCRIBE, "Cannot describe - " + giterr_set(GITERR_DESCRIBE, "cannot describe - " "'always_use_long_format' is incompatible with a zero" "'abbreviated_size'"); return -1; diff --git a/src/diff.c b/src/diff.c index 317d49597..a93bd4cd0 100644 --- a/src/diff.c +++ b/src/diff.c @@ -120,6 +120,41 @@ int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff) return 0; } +int git_diff_foreach( + git_diff *diff, + git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, + git_diff_hunk_cb hunk_cb, + git_diff_line_cb data_cb, + void *payload) +{ + int error = 0; + git_diff_delta *delta; + size_t idx; + + assert(diff); + + git_vector_foreach(&diff->deltas, idx, delta) { + git_patch *patch; + + /* check flags against patch status */ + if (git_diff_delta__should_skip(&diff->opts, delta)) + continue; + + if ((error = git_patch_from_diff(&patch, diff, idx)) != 0) + break; + + error = git_patch__invoke_callbacks(patch, file_cb, binary_cb, + hunk_cb, data_cb, payload); + git_patch_free(patch); + + if (error) + break; + } + + return error; +} + int git_diff_format_email__append_header_tobuf( git_buf *out, const git_oid *id, diff --git a/src/diff_driver.c b/src/diff_driver.c index 1a7f09a44..9109f3155 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -16,8 +16,6 @@ #include "config.h" #include "repository.h" -GIT__USE_STRMAP - typedef enum { DIFF_DRIVER_AUTO = 0, DIFF_DRIVER_BINARY = 1, @@ -151,7 +149,7 @@ static git_diff_driver_registry *git_repository_driver_registry( } if (!repo->diff_drivers) - giterr_set(GITERR_REPOSITORY, "Unable to create diff driver registry"); + giterr_set(GITERR_REPOSITORY, "unable to create diff driver registry"); return repo->diff_drivers; } @@ -217,7 +215,7 @@ static int git_diff_driver_builtin( goto done; } - git_strmap_insert(reg->drivers, drv->name, drv, error); + git_strmap_insert(reg->drivers, drv->name, drv, &error); if (error > 0) error = 0; @@ -331,7 +329,7 @@ static int git_diff_driver_load( goto done; /* store driver in registry */ - git_strmap_insert(reg->drivers, drv->name, drv, error); + git_strmap_insert(reg->drivers, drv->name, drv, &error); if (error < 0) goto done; error = 0; diff --git a/src/diff_file.c b/src/diff_file.c index cc1029038..d5fc5e940 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -304,7 +304,7 @@ static int diff_file_content_load_workdir_symlink( read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len); if (read_len < 0) { - giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file->path); + giterr_set(GITERR_OS, "failed to read symlink '%s'", fc->file->path); return -1; } diff --git a/src/diff_generate.c b/src/diff_generate.c index 06f9b19c7..f6cc04fed 100644 --- a/src/diff_generate.c +++ b/src/diff_generate.c @@ -624,7 +624,7 @@ int git_diff__oid_for_entry( error = git_odb__hashlink(out, full_path.ptr); diff->base.perf.oid_calculations++; } else if (!git__is_sizet(entry.file_size)) { - giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", + giterr_set(GITERR_OS, "file size overflow (for 32-bits) on '%s'", entry.path); error = -1; } else if (!(error = git_filter_list_load(&fl, @@ -1587,7 +1587,7 @@ int git_diff__commit( char commit_oidstr[GIT_OID_HEXSZ + 1]; error = -1; - giterr_set(GITERR_INVALID, "Commit %s is a merge commit", + giterr_set(GITERR_INVALID, "commit %s is a merge commit", git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit))); goto on_error; } diff --git a/src/diff_parse.c b/src/diff_parse.c index e640063af..5e3a7a177 100644 --- a/src/diff_parse.c +++ b/src/diff_parse.c @@ -37,7 +37,6 @@ static git_diff_parsed *diff_parsed_alloc(void) GIT_REFCOUNT_INC(diff); diff->base.type = GIT_DIFF_TYPE_PARSED; - diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE; diff->base.strcomp = git__strcmp; diff->base.strncomp = git__strncmp; diff->base.pfxcomp = git__prefixcmp; @@ -45,6 +44,13 @@ static git_diff_parsed *diff_parsed_alloc(void) diff->base.patch_fn = git_patch_parsed_from_diff; diff->base.free_fn = diff_parsed_free; + if (git_diff_init_options(&diff->base.opts, GIT_DIFF_OPTIONS_VERSION) < 0) { + git__free(diff); + return NULL; + } + + diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE; + git_pool_init(&diff->base.pool, 1); if (git_vector_init(&diff->patches, 0, NULL) < 0 || diff --git a/src/diff_print.c b/src/diff_print.c index fd1a186c1..5aa8a37e6 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -222,7 +222,7 @@ static int diff_print_one_raw( if (pi->id_strlen > id_abbrev) { giterr_set(GITERR_PATCH, - "The patch input contains %d id characters (cannot print %d)", + "the patch input contains %d id characters (cannot print %d)", id_abbrev, pi->id_strlen); return -1; } @@ -273,7 +273,7 @@ static int diff_print_oid_range( if (delta->old_file.mode && id_strlen > delta->old_file.id_abbrev) { giterr_set(GITERR_PATCH, - "The patch input contains %d id characters (cannot print %d)", + "the patch input contains %d id characters (cannot print %d)", delta->old_file.id_abbrev, id_strlen); return -1; } @@ -281,7 +281,7 @@ static int diff_print_oid_range( if ((delta->new_file.mode && id_strlen > delta->new_file.id_abbrev)) { giterr_set(GITERR_PATCH, - "The patch input contains %d id characters (cannot print %d)", + "the patch input contains %d id characters (cannot print %d)", delta->new_file.id_abbrev, id_strlen); return -1; } @@ -680,7 +680,7 @@ int git_diff_print( print_file = diff_print_one_name_status; break; default: - giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format); + giterr_set(GITERR_INVALID, "unknown diff output format (%d)", format); return -1; } @@ -708,7 +708,7 @@ int git_diff_print_callback__to_buf( GIT_UNUSED(delta); GIT_UNUSED(hunk); if (!output) { - giterr_set(GITERR_INVALID, "Buffer pointer must be provided"); + giterr_set(GITERR_INVALID, "buffer pointer must be provided"); return -1; } diff --git a/src/diff_tform.c b/src/diff_tform.c index e8848bd45..b004ddd66 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -131,7 +131,7 @@ int git_diff__merge( if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) || reversed != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) { giterr_set(GITERR_INVALID, - "Attempt to merge diffs created with conflicting options"); + "attempt to merge diffs created with conflicting options"); return -1; } @@ -553,8 +553,8 @@ static int similarity_measure( *score = -1; - /* don't try to compare files of different types */ - if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode)) + /* don't try to compare things that aren't files */ + if (!GIT_MODE_ISBLOB(a_file->mode) || !GIT_MODE_ISBLOB(b_file->mode)) return 0; /* if exact match is requested, force calculation of missing OIDs now */ diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index 5bd6381b5..60c4d85cb 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -50,7 +50,7 @@ static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header) return 0; fail: - giterr_set(GITERR_INVALID, "Malformed hunk header from xdiff"); + giterr_set(GITERR_INVALID, "malformed hunk header from xdiff"); return -1; } @@ -99,7 +99,7 @@ static int diff_update_lines( info->new_lineno += (int)line->num_lines; break; default: - giterr_set(GITERR_INVALID, "Unknown diff line origin %02x", + giterr_set(GITERR_INVALID, "unknown diff line origin %02x", (unsigned int)line->origin); return -1; } diff --git a/src/fetch.c b/src/fetch.c index 4d895752c..f408a5174 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -113,7 +113,7 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts) remote->need_pack = 0; if (filter_wants(remote, opts) < 0) { - giterr_set(GITERR_NET, "Failed to filter the reference list for wants"); + giterr_set(GITERR_NET, "failed to filter the reference list for wants"); return -1; } diff --git a/src/fetchhead.c b/src/fetchhead.c index 3d16c2166..6e6f3eb5e 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -115,7 +115,7 @@ int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs) assert(repo && fetchhead_refs); - if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0) + if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0) return -1; if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) { @@ -149,7 +149,7 @@ static int fetchhead_ref_parse( if (!*line) { giterr_set(GITERR_FETCHHEAD, - "Empty line in FETCH_HEAD line %"PRIuZ, line_num); + "empty line in FETCH_HEAD line %"PRIuZ, line_num); return -1; } @@ -163,13 +163,13 @@ static int fetchhead_ref_parse( if (strlen(oid_str) != GIT_OID_HEXSZ) { giterr_set(GITERR_FETCHHEAD, - "Invalid object ID in FETCH_HEAD line %"PRIuZ, line_num); + "invalid object ID in FETCH_HEAD line %"PRIuZ, line_num); return -1; } if (git_oid_fromstr(oid, oid_str) < 0) { const git_error *oid_err = giterr_last(); - const char *err_msg = oid_err ? oid_err->message : "Invalid object ID"; + const char *err_msg = oid_err ? oid_err->message : "invalid object ID"; giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ, err_msg, line_num); @@ -180,7 +180,7 @@ static int fetchhead_ref_parse( if (*line) { if ((is_merge_str = git__strsep(&line, "\t")) == NULL) { giterr_set(GITERR_FETCHHEAD, - "Invalid description data in FETCH_HEAD line %"PRIuZ, line_num); + "invalid description data in FETCH_HEAD line %"PRIuZ, line_num); return -1; } @@ -190,13 +190,13 @@ static int fetchhead_ref_parse( *is_merge = 0; else { giterr_set(GITERR_FETCHHEAD, - "Invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num); + "invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num); return -1; } if ((desc = line) == NULL) { giterr_set(GITERR_FETCHHEAD, - "Invalid description in FETCH_HEAD line %"PRIuZ, line_num); + "invalid description in FETCH_HEAD line %"PRIuZ, line_num); return -1; } @@ -213,7 +213,7 @@ static int fetchhead_ref_parse( if ((desc = strstr(name, "' ")) == NULL || git__prefixcmp(desc, "' of ") != 0) { giterr_set(GITERR_FETCHHEAD, - "Invalid description in FETCH_HEAD line %"PRIuZ, line_num); + "invalid description in FETCH_HEAD line %"PRIuZ, line_num); return -1; } @@ -249,7 +249,7 @@ int git_repository_fetchhead_foreach(git_repository *repo, assert(repo && cb); - if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0) + if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0) return -1; if ((error = git_futils_readbuffer(&file, git_buf_cstr(&path))) < 0) @@ -277,7 +277,7 @@ int git_repository_fetchhead_foreach(git_repository *repo, } if (*buffer) { - giterr_set(GITERR_FETCHHEAD, "No EOL at line %"PRIuZ, line_num+1); + giterr_set(GITERR_FETCHHEAD, "no EOL at line %"PRIuZ, line_num+1); error = -1; goto done; } diff --git a/src/filebuf.c b/src/filebuf.c index 582399470..80250ccdf 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -23,7 +23,7 @@ static int verify_last_error(git_filebuf *file) { switch (file->last_error) { case BUFERR_WRITE: - giterr_set(GITERR_OS, "Failed to write out file"); + giterr_set(GITERR_OS, "failed to write out file"); return -1; case BUFERR_MEM: @@ -48,7 +48,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) else { giterr_clear(); /* actual OS error code just confuses */ giterr_set(GITERR_OS, - "Failed to lock file '%s' for writing", file->path_lock); + "failed to lock file '%s' for writing", file->path_lock); return GIT_ELOCKED; } } @@ -75,7 +75,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) source = p_open(file->path_original, O_RDONLY); if (source < 0) { giterr_set(GITERR_OS, - "Failed to open file '%s' for reading", + "failed to open file '%s' for reading", file->path_original); return -1; } @@ -90,10 +90,10 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) p_close(source); if (read_bytes < 0) { - giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original); + giterr_set(GITERR_OS, "failed to read file '%s'", file->path_original); return -1; } else if (error < 0) { - giterr_set(GITERR_OS, "Failed to write file '%s'", file->path_lock); + giterr_set(GITERR_OS, "failed to write file '%s'", file->path_lock); return -1; } } @@ -246,7 +246,7 @@ static int resolve_symlink(git_buf *out, const char *path) root = git_path_root(target.ptr); if (root >= 0) { - if ((error = git_buf_puts(&curpath, target.ptr)) < 0) + if ((error = git_buf_sets(&curpath, target.ptr)) < 0) goto cleanup; } else { git_buf dir = GIT_BUF_INIT; @@ -291,6 +291,9 @@ int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mo if (flags & GIT_FILEBUF_DO_NOT_BUFFER) file->do_not_buffer = true; + if (flags & GIT_FILEBUF_FSYNC) + file->do_fsync = true; + file->buf_size = size; file->buf_pos = 0; file->fd = -1; @@ -316,7 +319,7 @@ int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mo if (compression != 0) { /* Initialize the ZLib stream */ if (deflateInit(&file->zs, compression) != Z_OK) { - giterr_set(GITERR_ZLIB, "Failed to initialize zlib"); + giterr_set(GITERR_ZLIB, "failed to initialize zlib"); goto cleanup; } @@ -425,18 +428,26 @@ int git_filebuf_commit(git_filebuf *file) file->fd_is_open = false; + if (file->do_fsync && p_fsync(file->fd) < 0) { + giterr_set(GITERR_OS, "failed to fsync '%s'", file->path_lock); + goto on_error; + } + if (p_close(file->fd) < 0) { - giterr_set(GITERR_OS, "Failed to close file at '%s'", file->path_lock); + giterr_set(GITERR_OS, "failed to close file at '%s'", file->path_lock); goto on_error; } file->fd = -1; if (p_rename(file->path_lock, file->path_original) < 0) { - giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original); + giterr_set(GITERR_OS, "failed to rename lockfile to '%s'", file->path_original); goto on_error; } + if (file->do_fsync && git_futils_fsync_parent(file->path_original) < 0) + goto on_error; + file->did_rename = true; git_filebuf_cleanup(file); @@ -571,7 +582,7 @@ int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file) res = p_stat(file->path_original, &st); if (res < 0) { - giterr_set(GITERR_OS, "Could not get stat info for '%s'", + giterr_set(GITERR_OS, "could not get stat info for '%s'", file->path_original); return res; } diff --git a/src/filebuf.h b/src/filebuf.h index 467708d45..c65aea780 100644 --- a/src/filebuf.h +++ b/src/filebuf.h @@ -20,7 +20,8 @@ #define GIT_FILEBUF_FORCE (1 << 3) #define GIT_FILEBUF_TEMPORARY (1 << 4) #define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5) -#define GIT_FILEBUF_DEFLATE_SHIFT (6) +#define GIT_FILEBUF_FSYNC (1 << 6) +#define GIT_FILEBUF_DEFLATE_SHIFT (7) #define GIT_FILELOCK_EXTENSION ".lock\0" #define GIT_FILELOCK_EXTLENGTH 6 @@ -47,6 +48,7 @@ struct git_filebuf { bool created_lock; bool did_rename; bool do_not_buffer; + bool do_fsync; int last_error; }; diff --git a/src/fileops.c b/src/fileops.c index a82202c98..2f3f58d4f 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -13,8 +13,6 @@ #include "win32/findfile.h" #endif -GIT__USE_STRMAP - int git_futils_mkpath2file(const char *file_path, const mode_t mode) { return git_futils_mkdir( @@ -37,13 +35,13 @@ int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode) if ((fd = p_mkstemp(path_out->ptr)) < 0) { giterr_set(GITERR_OS, - "Failed to create temporary file '%s'", path_out->ptr); + "failed to create temporary file '%s'", path_out->ptr); return -1; } if (p_chmod(path_out->ptr, (mode & ~mask))) { giterr_set(GITERR_OS, - "Failed to set permissions on file '%s'", path_out->ptr); + "failed to set permissions on file '%s'", path_out->ptr); return -1; } @@ -59,7 +57,7 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode fd = p_creat(path, mode); if (fd < 0) { - giterr_set(GITERR_OS, "Failed to create file '%s'", path); + giterr_set(GITERR_OS, "failed to create file '%s'", path); return -1; } @@ -68,12 +66,12 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode int git_futils_creat_locked(const char *path, const mode_t mode) { - int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | - O_EXCL | O_BINARY | O_CLOEXEC, mode); + int fd = p_open(path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, + mode); if (fd < 0) { int error = errno; - giterr_set(GITERR_OS, "Failed to create locked file '%s'", path); + giterr_set(GITERR_OS, "failed to create locked file '%s'", path); switch (error) { case EEXIST: return GIT_ELOCKED; @@ -108,7 +106,7 @@ git_off_t git_futils_filesize(git_file fd) struct stat sb; if (p_fstat(fd, &sb)) { - giterr_set(GITERR_OS, "Failed to stat file descriptor"); + giterr_set(GITERR_OS, "failed to stat file descriptor"); return -1; } @@ -137,7 +135,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) git_buf_clear(buf); if (!git__is_ssizet(len)) { - giterr_set(GITERR_INVALID, "Read too large."); + giterr_set(GITERR_INVALID, "read too large"); return -1; } @@ -149,7 +147,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) read_size = p_read(fd, buf->ptr, len); if (read_size != (ssize_t)len) { - giterr_set(GITERR_OS, "Failed to read descriptor"); + giterr_set(GITERR_OS, "failed to read descriptor"); git_buf_free(buf); return -1; } @@ -184,7 +182,7 @@ int git_futils_readbuffer_updated( } if (!git__is_sizet(st.st_size+1)) { - giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path); + giterr_set(GITERR_OS, "invalid regular file stat for '%s'", path); return -1; } @@ -198,28 +196,29 @@ int git_futils_readbuffer_updated( p_close(fd); - if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { - git_buf_free(&buf); - return error; - } + if (checksum) { + if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { + git_buf_free(&buf); + return error; + } - /* - * If we were given a checksum, we only want to use it if it's different - */ - if (checksum && !git_oid__cmp(checksum, &checksum_new)) { - git_buf_free(&buf); - if (updated) - *updated = 0; + /* + * If we were given a checksum, we only want to use it if it's different + */ + if (!git_oid__cmp(checksum, &checksum_new)) { + git_buf_free(&buf); + if (updated) + *updated = 0; - return 0; + return 0; + } + + git_oid_cpy(checksum, &checksum_new); } /* * If we're here, the file did change, or the user didn't have an old version */ - if (checksum) - git_oid_cpy(checksum, &checksum_new); - if (updated != NULL) *updated = 1; @@ -237,26 +236,43 @@ int git_futils_readbuffer(git_buf *buf, const char *path) int git_futils_writebuffer( const git_buf *buf, const char *path, int flags, mode_t mode) { - int fd, error = 0; + int fd, do_fsync = 0, error = 0; - if (flags <= 0) + if (!flags) flags = O_CREAT | O_TRUNC | O_WRONLY; + + if ((flags & O_FSYNC) != 0) + do_fsync = 1; + + flags &= ~O_FSYNC; + if (!mode) mode = GIT_FILEMODE_BLOB; if ((fd = p_open(path, flags, mode)) < 0) { - giterr_set(GITERR_OS, "Could not open '%s' for writing", path); + giterr_set(GITERR_OS, "could not open '%s' for writing", path); return fd; } if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) { - giterr_set(GITERR_OS, "Could not write to '%s'", path); + giterr_set(GITERR_OS, "could not write to '%s'", path); (void)p_close(fd); return error; } - if ((error = p_close(fd)) < 0) - giterr_set(GITERR_OS, "Error while closing '%s'", path); + if (do_fsync && (error = p_fsync(fd)) < 0) { + giterr_set(GITERR_OS, "could not fsync '%s'", path); + p_close(fd); + return error; + } + + if ((error = p_close(fd)) < 0) { + giterr_set(GITERR_OS, "error while closing '%s'", path); + return error; + } + + if (do_fsync && (flags & O_CREAT)) + error = git_futils_fsync_parent(path); return error; } @@ -267,7 +283,7 @@ int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmod return -1; if (p_rename(from, to) < 0) { - giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to); + giterr_set(GITERR_OS, "failed to rename '%s' to '%s'", from, to); return -1; } @@ -288,13 +304,19 @@ int git_futils_mmap_ro_file(git_map *out, const char *path) if (fd < 0) return fd; - len = git_futils_filesize(fd); + if ((len = git_futils_filesize(fd)) < 0) { + result = -1; + goto out; + } + if (!git__is_sizet(len)) { - giterr_set(GITERR_OS, "File `%s` too large to mmap", path); - return -1; + giterr_set(GITERR_OS, "file `%s` too large to mmap", path); + result = -1; + goto out; } result = git_futils_mmap_ro(out, fd, 0, (size_t)len); +out: p_close(fd); return result; } @@ -314,14 +336,14 @@ GIT_INLINE(int) mkdir_validate_dir( /* with exclusive create, existing dir is an error */ if ((flags & GIT_MKDIR_EXCL) != 0) { giterr_set(GITERR_FILESYSTEM, - "Failed to make directory '%s': directory exists", path); + "failed to make directory '%s': directory exists", path); return GIT_EEXISTS; } if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) || (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) { if (p_unlink(path) < 0) { - giterr_set(GITERR_OS, "Failed to remove %s '%s'", + giterr_set(GITERR_OS, "failed to remove %s '%s'", S_ISLNK(st->st_mode) ? "symlink" : "file", path); return GIT_EEXISTS; } @@ -329,7 +351,7 @@ GIT_INLINE(int) mkdir_validate_dir( opts->perfdata.mkdir_calls++; if (p_mkdir(path, mode) < 0) { - giterr_set(GITERR_OS, "Failed to make directory '%s'", path); + giterr_set(GITERR_OS, "failed to make directory '%s'", path); return GIT_EEXISTS; } } @@ -339,14 +361,14 @@ GIT_INLINE(int) mkdir_validate_dir( opts->perfdata.stat_calls++; if (p_stat(path, st) < 0) { - giterr_set(GITERR_OS, "Failed to make directory '%s'", path); + giterr_set(GITERR_OS, "failed to make directory '%s'", path); return GIT_EEXISTS; } } else if (!S_ISDIR(st->st_mode)) { giterr_set(GITERR_FILESYSTEM, - "Failed to make directory '%s': directory exists", path); + "failed to make directory '%s': directory exists", path); return GIT_EEXISTS; } @@ -569,7 +591,7 @@ int git_futils_mkdir_relative( retry_lstat: if (p_lstat(make_path.ptr, &st) < 0) { if (mkdir_attempted || errno != ENOENT) { - giterr_set(GITERR_OS, "Cannot access component in path '%s'", make_path.ptr); + giterr_set(GITERR_OS, "cannot access component in path '%s'", make_path.ptr); error = -1; goto done; } @@ -580,7 +602,7 @@ retry_lstat: if (p_mkdir(make_path.ptr, mode) < 0) { if (errno == EEXIST) goto retry_lstat; - giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); + giterr_set(GITERR_OS, "failed to make directory '%s'", make_path.ptr); error = -1; goto done; } @@ -607,7 +629,7 @@ retry_lstat: memcpy(cache_path, make_path.ptr, make_path.size + 1); - git_strmap_insert(opts->dir_map, cache_path, cache_path, error); + git_strmap_insert(opts->dir_map, cache_path, cache_path, &error); if (error < 0) goto done; } @@ -621,7 +643,7 @@ retry_lstat: opts->perfdata.stat_calls++; if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { - giterr_set(GITERR_OS, "Path is not a directory '%s'", + giterr_set(GITERR_OS, "path is not a directory '%s'", make_path.ptr); error = GIT_ENOTFOUND; } @@ -644,10 +666,10 @@ typedef struct { static int futils__error_cannot_rmdir(const char *path, const char *filemsg) { if (filemsg) - giterr_set(GITERR_OS, "Could not remove directory. File '%s' %s", + giterr_set(GITERR_OS, "could not remove directory '%s': %s", path, filemsg); else - giterr_set(GITERR_OS, "Could not remove directory '%s'", path); + giterr_set(GITERR_OS, "could not remove directory '%s'", path); return -1; } @@ -748,6 +770,9 @@ static int futils__rmdir_empty_parent(void *opaque, const char *path) if (en == ENOENT || en == ENOTDIR) { /* do nothing */ + } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0 && + en == EBUSY) { + error = git_path_set_error(errno, path, "rmdir"); } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { error = GIT_ITEROVER; } else { @@ -815,7 +840,7 @@ static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done) error = p_write(ofd, buffer, len); if (len < 0) { - giterr_set(GITERR_OS, "Read error while copying file"); + giterr_set(GITERR_OS, "read error while copying file"); error = (int)len; } @@ -871,14 +896,14 @@ static int cp_link(const char *from, const char *to, size_t link_size) read_len = p_readlink(from, link_data, link_size); if (read_len != (ssize_t)link_size) { - giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from); + giterr_set(GITERR_OS, "failed to read symlink data for '%s'", from); error = -1; } else { link_data[read_len] = '\0'; if (p_symlink(link_data, to) < 0) { - giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'", + giterr_set(GITERR_OS, "could not symlink '%s' as '%s'", link_data, to); error = -1; } @@ -974,7 +999,7 @@ static int _cp_r_callback(void *ref, git_buf *from) return 0; if (p_unlink(info->to.ptr) < 0) { - giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'", + giterr_set(GITERR_OS, "cannot overwrite existing file '%s'", info->to.ptr); return GIT_EEXISTS; } @@ -1109,3 +1134,37 @@ void git_futils_filestamp_set_from_stat( memset(stamp, 0, sizeof(*stamp)); } } + +int git_futils_fsync_dir(const char *path) +{ +#ifdef GIT_WIN32 + GIT_UNUSED(path); + return 0; +#else + int fd, error = -1; + + if ((fd = p_open(path, O_RDONLY)) < 0) { + giterr_set(GITERR_OS, "failed to open directory '%s' for fsync", path); + return -1; + } + + if ((error = p_fsync(fd)) < 0) + giterr_set(GITERR_OS, "failed to fsync directory '%s'", path); + + p_close(fd); + return error; +#endif +} + +int git_futils_fsync_parent(const char *path) +{ + char *parent; + int error; + + if ((parent = git_path_dirname(path)) == NULL) + return -1; + + error = git_futils_fsync_dir(parent); + git__free(parent); + return error; +} diff --git a/src/fileops.h b/src/fileops.h index 65c96a6f5..46886b0d7 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -25,6 +25,13 @@ extern int git_futils_readbuffer_updated( git_buf *obj, const char *path, git_oid *checksum, int *updated); extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len); +/* Additional constants for `git_futils_writebuffer`'s `open_flags`. We + * support these internally and they will be removed before the `open` call. + */ +#ifndef O_FSYNC +# define O_FSYNC (1 << 31) +#endif + extern int git_futils_writebuffer( const git_buf *buf, const char *path, int open_flags, mode_t mode); @@ -356,4 +363,22 @@ extern void git_futils_filestamp_set( extern void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st); +/** + * `fsync` the parent directory of the given path, if `fsync` is + * supported for directories on this platform. + * + * @param path Path of the directory to sync. + * @return 0 on success, -1 on error + */ +extern int git_futils_fsync_dir(const char *path); + +/** + * `fsync` the parent directory of the given path, if `fsync` is + * supported for directories on this platform. + * + * @param path Path of the file whose parent directory should be synced. + * @return 0 on success, -1 on error + */ +extern int git_futils_fsync_parent(const char *path); + #endif /* INCLUDE_fileops_h__ */ diff --git a/src/filter.c b/src/filter.c index a0628d779..361e08529 100644 --- a/src/filter.c +++ b/src/filter.c @@ -296,7 +296,7 @@ int git_filter_unregister(const char *name) /* cannot unregister default filters */ if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) { - giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name); + giterr_set(GITERR_FILTER, "cannot unregister filter '%s'", name); return -1; } @@ -306,7 +306,7 @@ int git_filter_unregister(const char *name) } if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { - giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); + giterr_set(GITERR_FILTER, "cannot find filter '%s' to unregister", name); error = GIT_ENOTFOUND; goto done; } @@ -645,7 +645,7 @@ int git_filter_list_push( git_rwlock_rdunlock(&filter_registry.lock); if (fdef == NULL) { - giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); + giterr_set(GITERR_FILTER, "cannot use an unregistered filter"); return -1; } @@ -758,7 +758,7 @@ static int buf_from_blob(git_buf *out, git_blob *blob) git_off_t rawsize = git_blob_rawsize(blob); if (!git__is_sizet(rawsize)) { - giterr_set(GITERR_OS, "Blob is too large to filter"); + giterr_set(GITERR_OS, "blob is too large to filter"); return -1; } @@ -895,7 +895,7 @@ static int stream_list_init( git_array_size(filters->filters) - 1 - i : i; git_filter_entry *fe = git_array_get(filters->filters, filter_idx); git_writestream *filter_stream; - + assert(fe->filter->stream || fe->filter->apply); /* If necessary, create a stream that proxies the traditional @@ -911,14 +911,19 @@ static int stream_list_init( last_stream); if (error < 0) - return error; + goto out; git_vector_insert(streams, filter_stream); last_stream = filter_stream; } - *out = last_stream; - return 0; +out: + if (error) + last_stream->close(last_stream); + else + *out = last_stream; + + return error; } void stream_list_free(git_vector *streams) @@ -943,12 +948,13 @@ int git_filter_list_stream_file( git_vector filter_streams = GIT_VECTOR_INIT; git_writestream *stream_start; ssize_t readlen; - int fd = -1, error; + int fd = -1, error, initialized = 0; if ((error = stream_list_init( &stream_start, &filter_streams, filters, target)) < 0 || (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0) goto done; + initialized = 1; if ((fd = git_futils_open_ro(abspath.ptr)) < 0) { error = fd; @@ -960,13 +966,13 @@ int git_filter_list_stream_file( goto done; } - if (!readlen) - error = stream_start->close(stream_start); - else if (readlen < 0) + if (readlen < 0) error = readlen; - done: + if (initialized) + error |= stream_start->close(stream_start); + if (fd >= 0) p_close(fd); stream_list_free(&filter_streams); @@ -981,20 +987,24 @@ int git_filter_list_stream_data( { git_vector filter_streams = GIT_VECTOR_INIT; git_writestream *stream_start; - int error = 0, close_error; + int error, initialized = 0; git_buf_sanitize(data); if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0) goto out; + initialized = 1; - error = stream_start->write(stream_start, data->ptr, data->size); + if ((error = stream_start->write( + stream_start, data->ptr, data->size)) < 0) + goto out; out: - close_error = stream_start->close(stream_start); + if (initialized) + error |= stream_start->close(stream_start); + stream_list_free(&filter_streams); - /* propagate the stream init or write error */ - return error < 0 ? error : close_error; + return error; } int git_filter_list_stream_blob( @@ -1012,3 +1022,9 @@ int git_filter_list_stream_blob( return git_filter_list_stream_data(filters, &in, target); } + +int git_filter_init(git_filter *filter, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter, version, git_filter, GIT_FILTER_INIT); + return 0; +} diff --git a/src/global.c b/src/global.c index e2ad8fe71..afa57e1d6 100644 --- a/src/global.c +++ b/src/global.c @@ -22,7 +22,7 @@ git_mutex git__mwindow_mutex; -#define MAX_SHUTDOWN_CB 8 +#define MAX_SHUTDOWN_CB 9 static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; static git_atomic git__n_shutdown_callbacks; diff --git a/src/hash.h b/src/hash.h index 0bc02a8a9..0db0339dc 100644 --- a/src/hash.h +++ b/src/hash.h @@ -16,11 +16,13 @@ int git_hash_global_init(void); int git_hash_ctx_init(git_hash_ctx *ctx); void git_hash_ctx_cleanup(git_hash_ctx *ctx); -#if defined(GIT_COMMON_CRYPTO) +#if defined(GIT_SHA1_COLLISIONDETECT) +# include "hash/hash_collisiondetect.h" +#elif defined(GIT_SHA1_COMMON_CRYPTO) # include "hash/hash_common_crypto.h" -#elif defined(OPENSSL_SHA1) +#elif defined(GIT_SHA1_OPENSSL) # include "hash/hash_openssl.h" -#elif defined(WIN32_SHA1) +#elif defined(GIT_SHA1_WIN32) # include "hash/hash_win32.h" #else # include "hash/hash_generic.h" diff --git a/src/hash/hash_collisiondetect.h b/src/hash/hash_collisiondetect.h new file mode 100644 index 000000000..5fdae8df6 --- /dev/null +++ b/src/hash/hash_collisiondetect.h @@ -0,0 +1,47 @@ +/* + * 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_hash_collisiondetect_h__ +#define INCLUDE_hash_collisiondetect_h__ + +#include "hash.h" +#include "sha1dc/sha1.h" + +struct git_hash_ctx { + SHA1_CTX c; +}; + +#define git_hash_global_init() 0 +#define git_hash_ctx_init(ctx) git_hash_init(ctx) +#define git_hash_ctx_cleanup(ctx) + +GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx) +{ + assert(ctx); + SHA1DCInit(&ctx->c); + return 0; +} + +GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + assert(ctx); + SHA1DCUpdate(&ctx->c, data, len); + return 0; +} + +GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx) +{ + assert(ctx); + if (SHA1DCFinal(out->id, &ctx->c)) { + giterr_set(GITERR_SHA1, "SHA1 collision attack detected"); + return -1; + } + + return 0; +} + +#endif /* INCLUDE_hash_collisiondetect_h__ */ diff --git a/src/hash/sha1dc/sha1.c b/src/hash/sha1dc/sha1.c new file mode 100644 index 000000000..facea1bb5 --- /dev/null +++ b/src/hash/sha1dc/sha1.c @@ -0,0 +1,1856 @@ +/*** +* Copyright 2017 Marc Stevens , Dan Shumow (danshu@microsoft.com) +* Distributed under the MIT Software License. +* See accompanying file LICENSE.txt or copy at +* https://opensource.org/licenses/MIT +***/ + +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include +#include +#include +#include +#endif + +#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C +#include SHA1DC_CUSTOM_INCLUDE_SHA1_C +#endif + +#ifndef SHA1DC_INIT_SAFE_HASH_DEFAULT +#define SHA1DC_INIT_SAFE_HASH_DEFAULT 1 +#endif + +#include "sha1.h" +#include "ubc_check.h" + + +/* + Because Little-Endian architectures are most common, + we only set SHA1DC_BIGENDIAN if one of these conditions is met. + Note that all MSFT platforms are little endian, + so none of these will be defined under the MSC compiler. + If you are compiling on a big endian platform and your compiler does not define one of these, + you will have to add whatever macros your tool chain defines to indicate Big-Endianness. + */ +#ifdef SHA1DC_BIGENDIAN +#undef SHA1DC_BIGENDIAN +#endif + +#if (defined(_BYTE_ORDER) || defined(__BYTE_ORDER) || defined(__BYTE_ORDER__)) + +#if ((defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) || \ + (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \ + (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) ) +#define SHA1DC_BIGENDIAN +#endif + +#else + +#if (defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN) || defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \ + defined(__sparc)) +#define SHA1DC_BIGENDIAN +#endif + +#endif + +#if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN)) +#undef SHA1DC_BIGENDIAN +#endif +#if (defined(SHA1DC_FORCE_BIGENDIAN) && !defined(SHA1DC_BIGENDIAN)) +#define SHA1DC_BIGENDIAN +#endif +/*ENDIANNESS SELECTION*/ + +#if (defined SHA1DC_FORCE_UNALIGNED_ACCESS || \ + defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \ + defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \ + defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \ + defined(__386) || defined(_M_X64) || defined(_M_AMD64)) + +#define SHA1DC_ALLOW_UNALIGNED_ACCESS + +#endif /*UNALIGNMENT DETECTION*/ + + +#define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n)))) +#define rotate_left(x,n) (((x)<<(n))|((x)>>(32-(n)))) + +#define sha1_bswap32(x) \ + {x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); x = (x << 16) | (x >> 16);} + +#define sha1_mix(W, t) (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1)) + +#ifdef SHA1DC_BIGENDIAN + #define sha1_load(m, t, temp) { temp = m[t]; } +#else + #define sha1_load(m, t, temp) { temp = m[t]; sha1_bswap32(temp); } +#endif + +#define sha1_store(W, t, x) *(volatile uint32_t *)&W[t] = x + +#define sha1_f1(b,c,d) ((d)^((b)&((c)^(d)))) +#define sha1_f2(b,c,d) ((b)^(c)^(d)) +#define sha1_f3(b,c,d) (((b)&(c))+((d)&((b)^(c)))) +#define sha1_f4(b,c,d) ((b)^(c)^(d)) + +#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, m, t) \ + { e += rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; b = rotate_left(b, 30); } +#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, m, t) \ + { e += rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; b = rotate_left(b, 30); } +#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, m, t) \ + { e += rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; b = rotate_left(b, 30); } +#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, m, t) \ + { e += rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; b = rotate_left(b, 30); } + +#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, m, t) \ + { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; } +#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, m, t) \ + { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; } +#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, m, t) \ + { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; } +#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, m, t) \ + { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; } + +#define SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, t, temp) \ + {sha1_load(m, t, temp); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30);} + +#define SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(a, b, c, d, e, W, t, temp) \ + {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30); } + +#define SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, t, temp) \ + {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1; b = rotate_left(b, 30); } + +#define SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, t, temp) \ + {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC; b = rotate_left(b, 30); } + +#define SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, t, temp) \ + {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6; b = rotate_left(b, 30); } + + +#define SHA1_STORE_STATE(i) states[i][0] = a; states[i][1] = b; states[i][2] = c; states[i][3] = d; states[i][4] = e; + +#ifdef BUILDNOCOLLDETECTSHA1COMPRESSION +void sha1_compression(uint32_t ihv[5], const uint32_t m[16]) +{ + uint32_t W[80]; + uint32_t a,b,c,d,e; + unsigned i; + + memcpy(W, m, 16 * 4); + for (i = 16; i < 80; ++i) + W[i] = sha1_mix(W, i); + + a = ihv[0]; b = ihv[1]; c = ihv[2]; d = ihv[3]; e = ihv[4]; + + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); + + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); + + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); + + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); + + ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; +} +#endif /*BUILDNOCOLLDETECTSHA1COMPRESSION*/ + + +static void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]) +{ + uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4]; + + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); + HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); + + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); + HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); + + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); + HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); + + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); + HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); + + ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; +} + + + +void sha1_compression_states(uint32_t ihv[5], const uint32_t m[16], uint32_t W[80], uint32_t states[80][5]) +{ + uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4]; + uint32_t temp; + +#ifdef DOSTORESTATE00 + SHA1_STORE_STATE(0) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 0, temp); + +#ifdef DOSTORESTATE01 + SHA1_STORE_STATE(1) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 1, temp); + +#ifdef DOSTORESTATE02 + SHA1_STORE_STATE(2) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 2, temp); + +#ifdef DOSTORESTATE03 + SHA1_STORE_STATE(3) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 3, temp); + +#ifdef DOSTORESTATE04 + SHA1_STORE_STATE(4) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 4, temp); + +#ifdef DOSTORESTATE05 + SHA1_STORE_STATE(5) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 5, temp); + +#ifdef DOSTORESTATE06 + SHA1_STORE_STATE(6) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 6, temp); + +#ifdef DOSTORESTATE07 + SHA1_STORE_STATE(7) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 7, temp); + +#ifdef DOSTORESTATE08 + SHA1_STORE_STATE(8) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 8, temp); + +#ifdef DOSTORESTATE09 + SHA1_STORE_STATE(9) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 9, temp); + +#ifdef DOSTORESTATE10 + SHA1_STORE_STATE(10) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 10, temp); + +#ifdef DOSTORESTATE11 + SHA1_STORE_STATE(11) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 11, temp); + +#ifdef DOSTORESTATE12 + SHA1_STORE_STATE(12) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 12, temp); + +#ifdef DOSTORESTATE13 + SHA1_STORE_STATE(13) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 13, temp); + +#ifdef DOSTORESTATE14 + SHA1_STORE_STATE(14) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 14, temp); + +#ifdef DOSTORESTATE15 + SHA1_STORE_STATE(15) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 15, temp); + +#ifdef DOSTORESTATE16 + SHA1_STORE_STATE(16) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(e, a, b, c, d, W, 16, temp); + +#ifdef DOSTORESTATE17 + SHA1_STORE_STATE(17) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(d, e, a, b, c, W, 17, temp); + +#ifdef DOSTORESTATE18 + SHA1_STORE_STATE(18) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(c, d, e, a, b, W, 18, temp); + +#ifdef DOSTORESTATE19 + SHA1_STORE_STATE(19) +#endif + SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(b, c, d, e, a, W, 19, temp); + + + +#ifdef DOSTORESTATE20 + SHA1_STORE_STATE(20) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 20, temp); + +#ifdef DOSTORESTATE21 + SHA1_STORE_STATE(21) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 21, temp); + +#ifdef DOSTORESTATE22 + SHA1_STORE_STATE(22) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 22, temp); + +#ifdef DOSTORESTATE23 + SHA1_STORE_STATE(23) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 23, temp); + +#ifdef DOSTORESTATE24 + SHA1_STORE_STATE(24) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 24, temp); + +#ifdef DOSTORESTATE25 + SHA1_STORE_STATE(25) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 25, temp); + +#ifdef DOSTORESTATE26 + SHA1_STORE_STATE(26) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 26, temp); + +#ifdef DOSTORESTATE27 + SHA1_STORE_STATE(27) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 27, temp); + +#ifdef DOSTORESTATE28 + SHA1_STORE_STATE(28) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 28, temp); + +#ifdef DOSTORESTATE29 + SHA1_STORE_STATE(29) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 29, temp); + +#ifdef DOSTORESTATE30 + SHA1_STORE_STATE(30) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 30, temp); + +#ifdef DOSTORESTATE31 + SHA1_STORE_STATE(31) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 31, temp); + +#ifdef DOSTORESTATE32 + SHA1_STORE_STATE(32) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 32, temp); + +#ifdef DOSTORESTATE33 + SHA1_STORE_STATE(33) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 33, temp); + +#ifdef DOSTORESTATE34 + SHA1_STORE_STATE(34) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 34, temp); + +#ifdef DOSTORESTATE35 + SHA1_STORE_STATE(35) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 35, temp); + +#ifdef DOSTORESTATE36 + SHA1_STORE_STATE(36) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 36, temp); + +#ifdef DOSTORESTATE37 + SHA1_STORE_STATE(37) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 37, temp); + +#ifdef DOSTORESTATE38 + SHA1_STORE_STATE(38) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 38, temp); + +#ifdef DOSTORESTATE39 + SHA1_STORE_STATE(39) +#endif + SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 39, temp); + + + +#ifdef DOSTORESTATE40 + SHA1_STORE_STATE(40) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 40, temp); + +#ifdef DOSTORESTATE41 + SHA1_STORE_STATE(41) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 41, temp); + +#ifdef DOSTORESTATE42 + SHA1_STORE_STATE(42) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 42, temp); + +#ifdef DOSTORESTATE43 + SHA1_STORE_STATE(43) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 43, temp); + +#ifdef DOSTORESTATE44 + SHA1_STORE_STATE(44) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 44, temp); + +#ifdef DOSTORESTATE45 + SHA1_STORE_STATE(45) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 45, temp); + +#ifdef DOSTORESTATE46 + SHA1_STORE_STATE(46) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 46, temp); + +#ifdef DOSTORESTATE47 + SHA1_STORE_STATE(47) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 47, temp); + +#ifdef DOSTORESTATE48 + SHA1_STORE_STATE(48) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 48, temp); + +#ifdef DOSTORESTATE49 + SHA1_STORE_STATE(49) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 49, temp); + +#ifdef DOSTORESTATE50 + SHA1_STORE_STATE(50) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 50, temp); + +#ifdef DOSTORESTATE51 + SHA1_STORE_STATE(51) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 51, temp); + +#ifdef DOSTORESTATE52 + SHA1_STORE_STATE(52) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 52, temp); + +#ifdef DOSTORESTATE53 + SHA1_STORE_STATE(53) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 53, temp); + +#ifdef DOSTORESTATE54 + SHA1_STORE_STATE(54) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 54, temp); + +#ifdef DOSTORESTATE55 + SHA1_STORE_STATE(55) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 55, temp); + +#ifdef DOSTORESTATE56 + SHA1_STORE_STATE(56) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 56, temp); + +#ifdef DOSTORESTATE57 + SHA1_STORE_STATE(57) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 57, temp); + +#ifdef DOSTORESTATE58 + SHA1_STORE_STATE(58) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 58, temp); + +#ifdef DOSTORESTATE59 + SHA1_STORE_STATE(59) +#endif + SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 59, temp); + + + + +#ifdef DOSTORESTATE60 + SHA1_STORE_STATE(60) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 60, temp); + +#ifdef DOSTORESTATE61 + SHA1_STORE_STATE(61) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 61, temp); + +#ifdef DOSTORESTATE62 + SHA1_STORE_STATE(62) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 62, temp); + +#ifdef DOSTORESTATE63 + SHA1_STORE_STATE(63) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 63, temp); + +#ifdef DOSTORESTATE64 + SHA1_STORE_STATE(64) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 64, temp); + +#ifdef DOSTORESTATE65 + SHA1_STORE_STATE(65) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 65, temp); + +#ifdef DOSTORESTATE66 + SHA1_STORE_STATE(66) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 66, temp); + +#ifdef DOSTORESTATE67 + SHA1_STORE_STATE(67) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 67, temp); + +#ifdef DOSTORESTATE68 + SHA1_STORE_STATE(68) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 68, temp); + +#ifdef DOSTORESTATE69 + SHA1_STORE_STATE(69) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 69, temp); + +#ifdef DOSTORESTATE70 + SHA1_STORE_STATE(70) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 70, temp); + +#ifdef DOSTORESTATE71 + SHA1_STORE_STATE(71) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 71, temp); + +#ifdef DOSTORESTATE72 + SHA1_STORE_STATE(72) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 72, temp); + +#ifdef DOSTORESTATE73 + SHA1_STORE_STATE(73) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 73, temp); + +#ifdef DOSTORESTATE74 + SHA1_STORE_STATE(74) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 74, temp); + +#ifdef DOSTORESTATE75 + SHA1_STORE_STATE(75) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 75, temp); + +#ifdef DOSTORESTATE76 + SHA1_STORE_STATE(76) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 76, temp); + +#ifdef DOSTORESTATE77 + SHA1_STORE_STATE(77) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 77, temp); + +#ifdef DOSTORESTATE78 + SHA1_STORE_STATE(78) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 78, temp); + +#ifdef DOSTORESTATE79 + SHA1_STORE_STATE(79) +#endif + SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 79, temp); + + + + ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; +} + + + + +#define SHA1_RECOMPRESS(t) \ +static void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) \ +{ \ + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; \ + if (t > 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 79); \ + if (t > 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 78); \ + if (t > 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 77); \ + if (t > 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 76); \ + if (t > 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 75); \ + if (t > 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 74); \ + if (t > 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 73); \ + if (t > 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 72); \ + if (t > 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 71); \ + if (t > 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 70); \ + if (t > 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 69); \ + if (t > 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 68); \ + if (t > 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 67); \ + if (t > 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 66); \ + if (t > 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 65); \ + if (t > 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 64); \ + if (t > 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 63); \ + if (t > 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 62); \ + if (t > 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 61); \ + if (t > 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 60); \ + if (t > 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 59); \ + if (t > 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 58); \ + if (t > 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 57); \ + if (t > 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 56); \ + if (t > 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 55); \ + if (t > 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 54); \ + if (t > 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 53); \ + if (t > 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 52); \ + if (t > 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 51); \ + if (t > 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 50); \ + if (t > 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 49); \ + if (t > 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 48); \ + if (t > 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 47); \ + if (t > 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 46); \ + if (t > 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 45); \ + if (t > 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 44); \ + if (t > 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 43); \ + if (t > 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 42); \ + if (t > 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 41); \ + if (t > 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 40); \ + if (t > 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 39); \ + if (t > 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 38); \ + if (t > 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 37); \ + if (t > 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 36); \ + if (t > 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 35); \ + if (t > 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 34); \ + if (t > 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 33); \ + if (t > 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 32); \ + if (t > 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 31); \ + if (t > 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 30); \ + if (t > 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 29); \ + if (t > 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 28); \ + if (t > 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 27); \ + if (t > 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 26); \ + if (t > 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 25); \ + if (t > 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 24); \ + if (t > 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 23); \ + if (t > 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 22); \ + if (t > 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 21); \ + if (t > 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 20); \ + if (t > 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 19); \ + if (t > 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 18); \ + if (t > 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 17); \ + if (t > 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 16); \ + if (t > 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 15); \ + if (t > 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 14); \ + if (t > 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 13); \ + if (t > 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 12); \ + if (t > 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 11); \ + if (t > 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 10); \ + if (t > 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 9); \ + if (t > 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 8); \ + if (t > 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 7); \ + if (t > 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 6); \ + if (t > 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 5); \ + if (t > 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 4); \ + if (t > 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 3); \ + if (t > 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 2); \ + if (t > 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 1); \ + if (t > 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 0); \ + ihvin[0] = a; ihvin[1] = b; ihvin[2] = c; ihvin[3] = d; ihvin[4] = e; \ + a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; \ + if (t <= 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 0); \ + if (t <= 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 1); \ + if (t <= 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 2); \ + if (t <= 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 3); \ + if (t <= 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 4); \ + if (t <= 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 5); \ + if (t <= 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 6); \ + if (t <= 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 7); \ + if (t <= 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 8); \ + if (t <= 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 9); \ + if (t <= 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 10); \ + if (t <= 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 11); \ + if (t <= 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 12); \ + if (t <= 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 13); \ + if (t <= 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 14); \ + if (t <= 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 15); \ + if (t <= 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 16); \ + if (t <= 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 17); \ + if (t <= 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 18); \ + if (t <= 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 19); \ + if (t <= 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 20); \ + if (t <= 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 21); \ + if (t <= 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 22); \ + if (t <= 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 23); \ + if (t <= 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 24); \ + if (t <= 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 25); \ + if (t <= 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 26); \ + if (t <= 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 27); \ + if (t <= 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 28); \ + if (t <= 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 29); \ + if (t <= 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 30); \ + if (t <= 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 31); \ + if (t <= 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 32); \ + if (t <= 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 33); \ + if (t <= 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 34); \ + if (t <= 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 35); \ + if (t <= 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 36); \ + if (t <= 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 37); \ + if (t <= 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 38); \ + if (t <= 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 39); \ + if (t <= 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 40); \ + if (t <= 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 41); \ + if (t <= 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 42); \ + if (t <= 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 43); \ + if (t <= 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 44); \ + if (t <= 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 45); \ + if (t <= 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 46); \ + if (t <= 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 47); \ + if (t <= 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 48); \ + if (t <= 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 49); \ + if (t <= 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 50); \ + if (t <= 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 51); \ + if (t <= 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 52); \ + if (t <= 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 53); \ + if (t <= 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 54); \ + if (t <= 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 55); \ + if (t <= 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 56); \ + if (t <= 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 57); \ + if (t <= 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 58); \ + if (t <= 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 59); \ + if (t <= 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 60); \ + if (t <= 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 61); \ + if (t <= 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 62); \ + if (t <= 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 63); \ + if (t <= 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 64); \ + if (t <= 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 65); \ + if (t <= 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 66); \ + if (t <= 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 67); \ + if (t <= 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 68); \ + if (t <= 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 69); \ + if (t <= 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 70); \ + if (t <= 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 71); \ + if (t <= 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 72); \ + if (t <= 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 73); \ + if (t <= 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 74); \ + if (t <= 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 75); \ + if (t <= 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 76); \ + if (t <= 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 77); \ + if (t <= 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 78); \ + if (t <= 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 79); \ + ihvout[0] = ihvin[0] + a; ihvout[1] = ihvin[1] + b; ihvout[2] = ihvin[2] + c; ihvout[3] = ihvin[3] + d; ihvout[4] = ihvin[4] + e; \ +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4127) /* Complier complains about the checks in the above macro being constant. */ +#endif + +#ifdef DOSTORESTATE0 +SHA1_RECOMPRESS(0) +#endif + +#ifdef DOSTORESTATE1 +SHA1_RECOMPRESS(1) +#endif + +#ifdef DOSTORESTATE2 +SHA1_RECOMPRESS(2) +#endif + +#ifdef DOSTORESTATE3 +SHA1_RECOMPRESS(3) +#endif + +#ifdef DOSTORESTATE4 +SHA1_RECOMPRESS(4) +#endif + +#ifdef DOSTORESTATE5 +SHA1_RECOMPRESS(5) +#endif + +#ifdef DOSTORESTATE6 +SHA1_RECOMPRESS(6) +#endif + +#ifdef DOSTORESTATE7 +SHA1_RECOMPRESS(7) +#endif + +#ifdef DOSTORESTATE8 +SHA1_RECOMPRESS(8) +#endif + +#ifdef DOSTORESTATE9 +SHA1_RECOMPRESS(9) +#endif + +#ifdef DOSTORESTATE10 +SHA1_RECOMPRESS(10) +#endif + +#ifdef DOSTORESTATE11 +SHA1_RECOMPRESS(11) +#endif + +#ifdef DOSTORESTATE12 +SHA1_RECOMPRESS(12) +#endif + +#ifdef DOSTORESTATE13 +SHA1_RECOMPRESS(13) +#endif + +#ifdef DOSTORESTATE14 +SHA1_RECOMPRESS(14) +#endif + +#ifdef DOSTORESTATE15 +SHA1_RECOMPRESS(15) +#endif + +#ifdef DOSTORESTATE16 +SHA1_RECOMPRESS(16) +#endif + +#ifdef DOSTORESTATE17 +SHA1_RECOMPRESS(17) +#endif + +#ifdef DOSTORESTATE18 +SHA1_RECOMPRESS(18) +#endif + +#ifdef DOSTORESTATE19 +SHA1_RECOMPRESS(19) +#endif + +#ifdef DOSTORESTATE20 +SHA1_RECOMPRESS(20) +#endif + +#ifdef DOSTORESTATE21 +SHA1_RECOMPRESS(21) +#endif + +#ifdef DOSTORESTATE22 +SHA1_RECOMPRESS(22) +#endif + +#ifdef DOSTORESTATE23 +SHA1_RECOMPRESS(23) +#endif + +#ifdef DOSTORESTATE24 +SHA1_RECOMPRESS(24) +#endif + +#ifdef DOSTORESTATE25 +SHA1_RECOMPRESS(25) +#endif + +#ifdef DOSTORESTATE26 +SHA1_RECOMPRESS(26) +#endif + +#ifdef DOSTORESTATE27 +SHA1_RECOMPRESS(27) +#endif + +#ifdef DOSTORESTATE28 +SHA1_RECOMPRESS(28) +#endif + +#ifdef DOSTORESTATE29 +SHA1_RECOMPRESS(29) +#endif + +#ifdef DOSTORESTATE30 +SHA1_RECOMPRESS(30) +#endif + +#ifdef DOSTORESTATE31 +SHA1_RECOMPRESS(31) +#endif + +#ifdef DOSTORESTATE32 +SHA1_RECOMPRESS(32) +#endif + +#ifdef DOSTORESTATE33 +SHA1_RECOMPRESS(33) +#endif + +#ifdef DOSTORESTATE34 +SHA1_RECOMPRESS(34) +#endif + +#ifdef DOSTORESTATE35 +SHA1_RECOMPRESS(35) +#endif + +#ifdef DOSTORESTATE36 +SHA1_RECOMPRESS(36) +#endif + +#ifdef DOSTORESTATE37 +SHA1_RECOMPRESS(37) +#endif + +#ifdef DOSTORESTATE38 +SHA1_RECOMPRESS(38) +#endif + +#ifdef DOSTORESTATE39 +SHA1_RECOMPRESS(39) +#endif + +#ifdef DOSTORESTATE40 +SHA1_RECOMPRESS(40) +#endif + +#ifdef DOSTORESTATE41 +SHA1_RECOMPRESS(41) +#endif + +#ifdef DOSTORESTATE42 +SHA1_RECOMPRESS(42) +#endif + +#ifdef DOSTORESTATE43 +SHA1_RECOMPRESS(43) +#endif + +#ifdef DOSTORESTATE44 +SHA1_RECOMPRESS(44) +#endif + +#ifdef DOSTORESTATE45 +SHA1_RECOMPRESS(45) +#endif + +#ifdef DOSTORESTATE46 +SHA1_RECOMPRESS(46) +#endif + +#ifdef DOSTORESTATE47 +SHA1_RECOMPRESS(47) +#endif + +#ifdef DOSTORESTATE48 +SHA1_RECOMPRESS(48) +#endif + +#ifdef DOSTORESTATE49 +SHA1_RECOMPRESS(49) +#endif + +#ifdef DOSTORESTATE50 +SHA1_RECOMPRESS(50) +#endif + +#ifdef DOSTORESTATE51 +SHA1_RECOMPRESS(51) +#endif + +#ifdef DOSTORESTATE52 +SHA1_RECOMPRESS(52) +#endif + +#ifdef DOSTORESTATE53 +SHA1_RECOMPRESS(53) +#endif + +#ifdef DOSTORESTATE54 +SHA1_RECOMPRESS(54) +#endif + +#ifdef DOSTORESTATE55 +SHA1_RECOMPRESS(55) +#endif + +#ifdef DOSTORESTATE56 +SHA1_RECOMPRESS(56) +#endif + +#ifdef DOSTORESTATE57 +SHA1_RECOMPRESS(57) +#endif + +#ifdef DOSTORESTATE58 +SHA1_RECOMPRESS(58) +#endif + +#ifdef DOSTORESTATE59 +SHA1_RECOMPRESS(59) +#endif + +#ifdef DOSTORESTATE60 +SHA1_RECOMPRESS(60) +#endif + +#ifdef DOSTORESTATE61 +SHA1_RECOMPRESS(61) +#endif + +#ifdef DOSTORESTATE62 +SHA1_RECOMPRESS(62) +#endif + +#ifdef DOSTORESTATE63 +SHA1_RECOMPRESS(63) +#endif + +#ifdef DOSTORESTATE64 +SHA1_RECOMPRESS(64) +#endif + +#ifdef DOSTORESTATE65 +SHA1_RECOMPRESS(65) +#endif + +#ifdef DOSTORESTATE66 +SHA1_RECOMPRESS(66) +#endif + +#ifdef DOSTORESTATE67 +SHA1_RECOMPRESS(67) +#endif + +#ifdef DOSTORESTATE68 +SHA1_RECOMPRESS(68) +#endif + +#ifdef DOSTORESTATE69 +SHA1_RECOMPRESS(69) +#endif + +#ifdef DOSTORESTATE70 +SHA1_RECOMPRESS(70) +#endif + +#ifdef DOSTORESTATE71 +SHA1_RECOMPRESS(71) +#endif + +#ifdef DOSTORESTATE72 +SHA1_RECOMPRESS(72) +#endif + +#ifdef DOSTORESTATE73 +SHA1_RECOMPRESS(73) +#endif + +#ifdef DOSTORESTATE74 +SHA1_RECOMPRESS(74) +#endif + +#ifdef DOSTORESTATE75 +SHA1_RECOMPRESS(75) +#endif + +#ifdef DOSTORESTATE76 +SHA1_RECOMPRESS(76) +#endif + +#ifdef DOSTORESTATE77 +SHA1_RECOMPRESS(77) +#endif + +#ifdef DOSTORESTATE78 +SHA1_RECOMPRESS(78) +#endif + +#ifdef DOSTORESTATE79 +SHA1_RECOMPRESS(79) +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +static void sha1_recompression_step(uint32_t step, uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) +{ + switch (step) + { +#ifdef DOSTORESTATE0 + case 0: + sha1recompress_fast_0(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE1 + case 1: + sha1recompress_fast_1(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE2 + case 2: + sha1recompress_fast_2(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE3 + case 3: + sha1recompress_fast_3(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE4 + case 4: + sha1recompress_fast_4(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE5 + case 5: + sha1recompress_fast_5(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE6 + case 6: + sha1recompress_fast_6(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE7 + case 7: + sha1recompress_fast_7(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE8 + case 8: + sha1recompress_fast_8(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE9 + case 9: + sha1recompress_fast_9(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE10 + case 10: + sha1recompress_fast_10(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE11 + case 11: + sha1recompress_fast_11(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE12 + case 12: + sha1recompress_fast_12(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE13 + case 13: + sha1recompress_fast_13(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE14 + case 14: + sha1recompress_fast_14(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE15 + case 15: + sha1recompress_fast_15(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE16 + case 16: + sha1recompress_fast_16(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE17 + case 17: + sha1recompress_fast_17(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE18 + case 18: + sha1recompress_fast_18(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE19 + case 19: + sha1recompress_fast_19(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE20 + case 20: + sha1recompress_fast_20(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE21 + case 21: + sha1recompress_fast_21(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE22 + case 22: + sha1recompress_fast_22(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE23 + case 23: + sha1recompress_fast_23(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE24 + case 24: + sha1recompress_fast_24(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE25 + case 25: + sha1recompress_fast_25(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE26 + case 26: + sha1recompress_fast_26(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE27 + case 27: + sha1recompress_fast_27(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE28 + case 28: + sha1recompress_fast_28(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE29 + case 29: + sha1recompress_fast_29(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE30 + case 30: + sha1recompress_fast_30(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE31 + case 31: + sha1recompress_fast_31(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE32 + case 32: + sha1recompress_fast_32(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE33 + case 33: + sha1recompress_fast_33(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE34 + case 34: + sha1recompress_fast_34(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE35 + case 35: + sha1recompress_fast_35(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE36 + case 36: + sha1recompress_fast_36(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE37 + case 37: + sha1recompress_fast_37(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE38 + case 38: + sha1recompress_fast_38(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE39 + case 39: + sha1recompress_fast_39(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE40 + case 40: + sha1recompress_fast_40(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE41 + case 41: + sha1recompress_fast_41(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE42 + case 42: + sha1recompress_fast_42(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE43 + case 43: + sha1recompress_fast_43(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE44 + case 44: + sha1recompress_fast_44(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE45 + case 45: + sha1recompress_fast_45(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE46 + case 46: + sha1recompress_fast_46(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE47 + case 47: + sha1recompress_fast_47(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE48 + case 48: + sha1recompress_fast_48(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE49 + case 49: + sha1recompress_fast_49(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE50 + case 50: + sha1recompress_fast_50(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE51 + case 51: + sha1recompress_fast_51(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE52 + case 52: + sha1recompress_fast_52(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE53 + case 53: + sha1recompress_fast_53(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE54 + case 54: + sha1recompress_fast_54(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE55 + case 55: + sha1recompress_fast_55(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE56 + case 56: + sha1recompress_fast_56(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE57 + case 57: + sha1recompress_fast_57(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE58 + case 58: + sha1recompress_fast_58(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE59 + case 59: + sha1recompress_fast_59(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE60 + case 60: + sha1recompress_fast_60(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE61 + case 61: + sha1recompress_fast_61(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE62 + case 62: + sha1recompress_fast_62(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE63 + case 63: + sha1recompress_fast_63(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE64 + case 64: + sha1recompress_fast_64(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE65 + case 65: + sha1recompress_fast_65(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE66 + case 66: + sha1recompress_fast_66(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE67 + case 67: + sha1recompress_fast_67(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE68 + case 68: + sha1recompress_fast_68(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE69 + case 69: + sha1recompress_fast_69(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE70 + case 70: + sha1recompress_fast_70(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE71 + case 71: + sha1recompress_fast_71(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE72 + case 72: + sha1recompress_fast_72(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE73 + case 73: + sha1recompress_fast_73(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE74 + case 74: + sha1recompress_fast_74(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE75 + case 75: + sha1recompress_fast_75(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE76 + case 76: + sha1recompress_fast_76(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE77 + case 77: + sha1recompress_fast_77(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE78 + case 78: + sha1recompress_fast_78(ihvin, ihvout, me2, state); + break; +#endif +#ifdef DOSTORESTATE79 + case 79: + sha1recompress_fast_79(ihvin, ihvout, me2, state); + break; +#endif + default: + abort(); + } + +} + + + +static void sha1_process(SHA1_CTX* ctx, const uint32_t block[16]) +{ + unsigned i, j; + uint32_t ubc_dv_mask[DVMASKSIZE] = { 0xFFFFFFFF }; + uint32_t ihvtmp[5]; + + ctx->ihv1[0] = ctx->ihv[0]; + ctx->ihv1[1] = ctx->ihv[1]; + ctx->ihv1[2] = ctx->ihv[2]; + ctx->ihv1[3] = ctx->ihv[3]; + ctx->ihv1[4] = ctx->ihv[4]; + + sha1_compression_states(ctx->ihv, block, ctx->m1, ctx->states); + + if (ctx->detect_coll) + { + if (ctx->ubc_check) + { + ubc_check(ctx->m1, ubc_dv_mask); + } + + if (ubc_dv_mask[0] != 0) + { + for (i = 0; sha1_dvs[i].dvType != 0; ++i) + { + if (ubc_dv_mask[0] & ((uint32_t)(1) << sha1_dvs[i].maskb)) + { + for (j = 0; j < 80; ++j) + ctx->m2[j] = ctx->m1[j] ^ sha1_dvs[i].dm[j]; + + sha1_recompression_step(sha1_dvs[i].testt, ctx->ihv2, ihvtmp, ctx->m2, ctx->states[sha1_dvs[i].testt]); + + /* to verify SHA-1 collision detection code with collisions for reduced-step SHA-1 */ + if ((0 == ((ihvtmp[0] ^ ctx->ihv[0]) | (ihvtmp[1] ^ ctx->ihv[1]) | (ihvtmp[2] ^ ctx->ihv[2]) | (ihvtmp[3] ^ ctx->ihv[3]) | (ihvtmp[4] ^ ctx->ihv[4]))) + || (ctx->reduced_round_coll && 0==((ctx->ihv1[0] ^ ctx->ihv2[0]) | (ctx->ihv1[1] ^ ctx->ihv2[1]) | (ctx->ihv1[2] ^ ctx->ihv2[2]) | (ctx->ihv1[3] ^ ctx->ihv2[3]) | (ctx->ihv1[4] ^ ctx->ihv2[4])))) + { + ctx->found_collision = 1; + + if (ctx->safe_hash) + { + sha1_compression_W(ctx->ihv, ctx->m1); + sha1_compression_W(ctx->ihv, ctx->m1); + } + + break; + } + } + } + } + } +} + +void SHA1DCInit(SHA1_CTX* ctx) +{ + ctx->total = 0; + ctx->ihv[0] = 0x67452301; + ctx->ihv[1] = 0xEFCDAB89; + ctx->ihv[2] = 0x98BADCFE; + ctx->ihv[3] = 0x10325476; + ctx->ihv[4] = 0xC3D2E1F0; + ctx->found_collision = 0; + ctx->safe_hash = SHA1DC_INIT_SAFE_HASH_DEFAULT; + ctx->ubc_check = 1; + ctx->detect_coll = 1; + ctx->reduced_round_coll = 0; + ctx->callback = NULL; +} + +void SHA1DCSetSafeHash(SHA1_CTX* ctx, int safehash) +{ + if (safehash) + ctx->safe_hash = 1; + else + ctx->safe_hash = 0; +} + + +void SHA1DCSetUseUBC(SHA1_CTX* ctx, int ubc_check) +{ + if (ubc_check) + ctx->ubc_check = 1; + else + ctx->ubc_check = 0; +} + +void SHA1DCSetUseDetectColl(SHA1_CTX* ctx, int detect_coll) +{ + if (detect_coll) + ctx->detect_coll = 1; + else + ctx->detect_coll = 0; +} + +void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX* ctx, int reduced_round_coll) +{ + if (reduced_round_coll) + ctx->reduced_round_coll = 1; + else + ctx->reduced_round_coll = 0; +} + +void SHA1DCSetCallback(SHA1_CTX* ctx, collision_block_callback callback) +{ + ctx->callback = callback; +} + +void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, size_t len) +{ + unsigned left, fill; + + if (len == 0) + return; + + left = ctx->total & 63; + fill = 64 - left; + + if (left && len >= fill) + { + ctx->total += fill; + memcpy(ctx->buffer + left, buf, fill); + sha1_process(ctx, (uint32_t*)(ctx->buffer)); + buf += fill; + len -= fill; + left = 0; + } + while (len >= 64) + { + ctx->total += 64; + +#if defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) + sha1_process(ctx, (uint32_t*)(buf)); +#else + memcpy(ctx->buffer, buf, 64); + sha1_process(ctx, (uint32_t*)(ctx->buffer)); +#endif /* defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) */ + buf += 64; + len -= 64; + } + if (len > 0) + { + ctx->total += len; + memcpy(ctx->buffer + left, buf, len); + } +} + +static const unsigned char sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx) +{ + uint32_t last = ctx->total & 63; + uint32_t padn = (last < 56) ? (56 - last) : (120 - last); + uint64_t total; + SHA1DCUpdate(ctx, (const char*)(sha1_padding), padn); + + total = ctx->total - padn; + total <<= 3; + ctx->buffer[56] = (unsigned char)(total >> 56); + ctx->buffer[57] = (unsigned char)(total >> 48); + ctx->buffer[58] = (unsigned char)(total >> 40); + ctx->buffer[59] = (unsigned char)(total >> 32); + ctx->buffer[60] = (unsigned char)(total >> 24); + ctx->buffer[61] = (unsigned char)(total >> 16); + ctx->buffer[62] = (unsigned char)(total >> 8); + ctx->buffer[63] = (unsigned char)(total); + sha1_process(ctx, (uint32_t*)(ctx->buffer)); + output[0] = (unsigned char)(ctx->ihv[0] >> 24); + output[1] = (unsigned char)(ctx->ihv[0] >> 16); + output[2] = (unsigned char)(ctx->ihv[0] >> 8); + output[3] = (unsigned char)(ctx->ihv[0]); + output[4] = (unsigned char)(ctx->ihv[1] >> 24); + output[5] = (unsigned char)(ctx->ihv[1] >> 16); + output[6] = (unsigned char)(ctx->ihv[1] >> 8); + output[7] = (unsigned char)(ctx->ihv[1]); + output[8] = (unsigned char)(ctx->ihv[2] >> 24); + output[9] = (unsigned char)(ctx->ihv[2] >> 16); + output[10] = (unsigned char)(ctx->ihv[2] >> 8); + output[11] = (unsigned char)(ctx->ihv[2]); + output[12] = (unsigned char)(ctx->ihv[3] >> 24); + output[13] = (unsigned char)(ctx->ihv[3] >> 16); + output[14] = (unsigned char)(ctx->ihv[3] >> 8); + output[15] = (unsigned char)(ctx->ihv[3]); + output[16] = (unsigned char)(ctx->ihv[4] >> 24); + output[17] = (unsigned char)(ctx->ihv[4] >> 16); + output[18] = (unsigned char)(ctx->ihv[4] >> 8); + output[19] = (unsigned char)(ctx->ihv[4]); + return ctx->found_collision; +} + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C +#endif diff --git a/src/hash/sha1dc/sha1.h b/src/hash/sha1dc/sha1.h new file mode 100644 index 000000000..1e4e94be5 --- /dev/null +++ b/src/hash/sha1dc/sha1.h @@ -0,0 +1,110 @@ +/*** +* Copyright 2017 Marc Stevens , Dan Shumow +* Distributed under the MIT Software License. +* See accompanying file LICENSE.txt or copy at +* https://opensource.org/licenses/MIT +***/ + +#ifndef SHA1DC_SHA1_H +#define SHA1DC_SHA1_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include +#endif + +/* sha-1 compression function that takes an already expanded message, and additionally store intermediate states */ +/* only stores states ii (the state between step ii-1 and step ii) when DOSTORESTATEii is defined in ubc_check.h */ +void sha1_compression_states(uint32_t[5], const uint32_t[16], uint32_t[80], uint32_t[80][5]); + +/* +// Function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]). +// Where 0 <= T < 80 +// me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference.) +// state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block. +// The function will return: +// ihvin: The reconstructed input chaining value. +// ihvout: The reconstructed output chaining value. +*/ +typedef void(*sha1_recompression_type)(uint32_t*, uint32_t*, const uint32_t*, const uint32_t*); + +/* A callback function type that can be set to be called when a collision block has been found: */ +/* void collision_block_callback(uint64_t byteoffset, const uint32_t ihvin1[5], const uint32_t ihvin2[5], const uint32_t m1[80], const uint32_t m2[80]) */ +typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*); + +/* The SHA-1 context. */ +typedef struct { + uint64_t total; + uint32_t ihv[5]; + unsigned char buffer[64]; + int found_collision; + int safe_hash; + int detect_coll; + int ubc_check; + int reduced_round_coll; + collision_block_callback callback; + + uint32_t ihv1[5]; + uint32_t ihv2[5]; + uint32_t m1[80]; + uint32_t m2[80]; + uint32_t states[80][5]; +} SHA1_CTX; + +/* Initialize SHA-1 context. */ +void SHA1DCInit(SHA1_CTX*); + +/* + Function to enable safe SHA-1 hashing: + Collision attacks are thwarted by hashing a detected near-collision block 3 times. + Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks: + The best collision attacks against SHA-1 have complexity about 2^60, + thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180. + An attacker would be better off using a generic birthday search of complexity 2^80. + + Enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected, + but it will result in a different SHA-1 hash for messages where a collision attack was detected. + This will automatically invalidate SHA-1 based digital signature forgeries. + Enabled by default. +*/ +void SHA1DCSetSafeHash(SHA1_CTX*, int); + +/* + Function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up). + Enabled by default + */ +void SHA1DCSetUseUBC(SHA1_CTX*, int); + +/* + Function to disable or enable the use of Collision Detection. + Enabled by default. + */ +void SHA1DCSetUseDetectColl(SHA1_CTX*, int); + +/* function to disable or enable the detection of reduced-round SHA-1 collisions */ +/* disabled by default */ +void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX*, int); + +/* function to set a callback function, pass NULL to disable */ +/* by default no callback set */ +void SHA1DCSetCallback(SHA1_CTX*, collision_block_callback); + +/* update SHA-1 context with buffer contents */ +void SHA1DCUpdate(SHA1_CTX*, const char*, size_t); + +/* obtain SHA-1 hash from SHA-1 context */ +/* returns: 0 = no collision detected, otherwise = collision found => warn user for active attack */ +int SHA1DCFinal(unsigned char[20], SHA1_CTX*); + +#if defined(__cplusplus) +} +#endif + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H +#endif + +#endif diff --git a/src/hash/sha1dc/ubc_check.c b/src/hash/sha1dc/ubc_check.c new file mode 100644 index 000000000..b3beff2af --- /dev/null +++ b/src/hash/sha1dc/ubc_check.c @@ -0,0 +1,372 @@ +/*** +* Copyright 2017 Marc Stevens , Dan Shumow +* Distributed under the MIT Software License. +* See accompanying file LICENSE.txt or copy at +* https://opensource.org/licenses/MIT +***/ + +/* +// this file was generated by the 'parse_bitrel' program in the tools section +// using the data files from directory 'tools/data/3565' +// +// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check +// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper) +// dm[80] is the expanded message block XOR-difference defined by the DV +// testt is the step to do the recompression from for collision detection +// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check +// +// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs +// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met +// thus one needs to do the recompression check for each DV that has its bit set +// +// ubc_check is programmatically generated and the unavoidable bitconditions have been hardcoded +// a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c +// ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section +*/ + +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include +#endif +#ifdef SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C +#include SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C +#endif +#include "ubc_check.h" + +static const uint32_t DV_I_43_0_bit = (uint32_t)(1) << 0; +static const uint32_t DV_I_44_0_bit = (uint32_t)(1) << 1; +static const uint32_t DV_I_45_0_bit = (uint32_t)(1) << 2; +static const uint32_t DV_I_46_0_bit = (uint32_t)(1) << 3; +static const uint32_t DV_I_46_2_bit = (uint32_t)(1) << 4; +static const uint32_t DV_I_47_0_bit = (uint32_t)(1) << 5; +static const uint32_t DV_I_47_2_bit = (uint32_t)(1) << 6; +static const uint32_t DV_I_48_0_bit = (uint32_t)(1) << 7; +static const uint32_t DV_I_48_2_bit = (uint32_t)(1) << 8; +static const uint32_t DV_I_49_0_bit = (uint32_t)(1) << 9; +static const uint32_t DV_I_49_2_bit = (uint32_t)(1) << 10; +static const uint32_t DV_I_50_0_bit = (uint32_t)(1) << 11; +static const uint32_t DV_I_50_2_bit = (uint32_t)(1) << 12; +static const uint32_t DV_I_51_0_bit = (uint32_t)(1) << 13; +static const uint32_t DV_I_51_2_bit = (uint32_t)(1) << 14; +static const uint32_t DV_I_52_0_bit = (uint32_t)(1) << 15; +static const uint32_t DV_II_45_0_bit = (uint32_t)(1) << 16; +static const uint32_t DV_II_46_0_bit = (uint32_t)(1) << 17; +static const uint32_t DV_II_46_2_bit = (uint32_t)(1) << 18; +static const uint32_t DV_II_47_0_bit = (uint32_t)(1) << 19; +static const uint32_t DV_II_48_0_bit = (uint32_t)(1) << 20; +static const uint32_t DV_II_49_0_bit = (uint32_t)(1) << 21; +static const uint32_t DV_II_49_2_bit = (uint32_t)(1) << 22; +static const uint32_t DV_II_50_0_bit = (uint32_t)(1) << 23; +static const uint32_t DV_II_50_2_bit = (uint32_t)(1) << 24; +static const uint32_t DV_II_51_0_bit = (uint32_t)(1) << 25; +static const uint32_t DV_II_51_2_bit = (uint32_t)(1) << 26; +static const uint32_t DV_II_52_0_bit = (uint32_t)(1) << 27; +static const uint32_t DV_II_53_0_bit = (uint32_t)(1) << 28; +static const uint32_t DV_II_54_0_bit = (uint32_t)(1) << 29; +static const uint32_t DV_II_55_0_bit = (uint32_t)(1) << 30; +static const uint32_t DV_II_56_0_bit = (uint32_t)(1) << 31; + +dv_info_t sha1_dvs[] = +{ + {1,43,0,58,0,0, { 0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161,0x80000599 } } +, {1,44,0,58,0,1, { 0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161 } } +, {1,45,0,58,0,2, { 0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803 } } +, {1,46,0,58,0,3, { 0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c } } +, {1,46,2,58,0,4, { 0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a,0x00000132 } } +, {1,47,0,58,0,5, { 0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6 } } +, {1,47,2,58,0,6, { 0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a } } +, {1,48,0,58,0,7, { 0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408 } } +, {1,48,2,58,0,8, { 0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020 } } +, {1,49,0,58,0,9, { 0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164 } } +, {1,49,2,58,0,10, { 0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590 } } +, {1,50,0,65,0,11, { 0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018 } } +, {1,50,2,65,0,12, { 0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060 } } +, {1,51,0,65,0,13, { 0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202 } } +, {1,51,2,65,0,14, { 0xa0000003,0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a } } +, {1,52,0,65,0,15, { 0x04000010,0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012 } } +, {2,45,0,58,0,16, { 0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054,0x00000967 } } +, {2,46,0,58,0,17, { 0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054 } } +, {2,46,2,58,0,18, { 0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6,0x0000106a,0x00000b90,0x00000152 } } +, {2,47,0,58,0,19, { 0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4 } } +, {2,48,0,58,0,20, { 0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a } } +, {2,49,0,58,0,21, { 0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d } } +, {2,49,2,58,0,22, { 0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6 } } +, {2,50,0,65,0,23, { 0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b } } +, {2,50,2,65,0,24, { 0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c } } +, {2,51,0,65,0,25, { 0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b } } +, {2,51,2,65,0,26, { 0x00000043,0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e } } +, {2,52,0,65,0,27, { 0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014 } } +, {2,53,0,65,0,28, { 0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089 } } +, {2,54,0,65,0,29, { 0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107 } } +, {2,55,0,65,0,30, { 0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b } } +, {2,56,0,65,0,31, { 0x2600001a,0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046 } } +, {0,0,0,0,0,0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}} +}; +void ubc_check(const uint32_t W[80], uint32_t dvmask[1]) +{ + uint32_t mask = ~((uint32_t)(0)); + mask &= (((((W[44]^W[45])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_I_51_0_bit|DV_I_52_0_bit|DV_II_45_0_bit|DV_II_46_0_bit|DV_II_50_0_bit|DV_II_51_0_bit)); + mask &= (((((W[49]^W[50])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_II_45_0_bit|DV_II_50_0_bit|DV_II_51_0_bit|DV_II_55_0_bit|DV_II_56_0_bit)); + mask &= (((((W[48]^W[49])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_52_0_bit|DV_II_49_0_bit|DV_II_50_0_bit|DV_II_54_0_bit|DV_II_55_0_bit)); + mask &= ((((W[47]^(W[50]>>25))&(1<<4))-(1<<4)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)); + mask &= (((((W[47]^W[48])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_51_0_bit|DV_II_48_0_bit|DV_II_49_0_bit|DV_II_53_0_bit|DV_II_54_0_bit)); + mask &= (((((W[46]>>4)^(W[49]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_50_0_bit|DV_II_55_0_bit)); + mask &= (((((W[46]^W[47])>>29)&1)-1) | ~(DV_I_43_0_bit|DV_I_50_0_bit|DV_II_47_0_bit|DV_II_48_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)); + mask &= (((((W[45]>>4)^(W[48]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_49_0_bit|DV_II_54_0_bit)); + mask &= (((((W[45]^W[46])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_51_0_bit|DV_II_52_0_bit)); + mask &= (((((W[44]>>4)^(W[47]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_53_0_bit)); + mask &= (((((W[43]>>4)^(W[46]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_52_0_bit)); + mask &= (((((W[43]^W[44])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_I_50_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_49_0_bit|DV_II_50_0_bit)); + mask &= (((((W[42]>>4)^(W[45]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_51_0_bit)); + mask &= (((((W[41]>>4)^(W[44]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_50_0_bit)); + mask &= (((((W[40]^W[41])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_47_0_bit|DV_I_48_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_56_0_bit)); + mask &= (((((W[54]^W[55])>>29)&1)-1) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_50_0_bit|DV_II_55_0_bit|DV_II_56_0_bit)); + mask &= (((((W[53]^W[54])>>29)&1)-1) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_49_0_bit|DV_II_54_0_bit|DV_II_55_0_bit)); + mask &= (((((W[52]^W[53])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit|DV_II_53_0_bit|DV_II_54_0_bit)); + mask &= ((((W[50]^(W[53]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_48_0_bit|DV_II_54_0_bit)); + mask &= (((((W[50]^W[51])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_II_46_0_bit|DV_II_51_0_bit|DV_II_52_0_bit|DV_II_56_0_bit)); + mask &= ((((W[49]^(W[52]>>25))&(1<<4))-(1<<4)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_47_0_bit|DV_II_53_0_bit)); + mask &= ((((W[48]^(W[51]>>25))&(1<<4))-(1<<4)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_52_0_bit)); + mask &= (((((W[42]^W[43])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)); + mask &= (((((W[41]^W[42])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_48_0_bit)); + mask &= (((((W[40]>>4)^(W[43]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_50_0_bit|DV_II_49_0_bit|DV_II_56_0_bit)); + mask &= (((((W[39]>>4)^(W[42]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_49_0_bit|DV_II_48_0_bit|DV_II_55_0_bit)); + if (mask & (DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)) + mask &= (((((W[38]>>4)^(W[41]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)); + mask &= (((((W[37]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_47_0_bit|DV_II_46_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)); + if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)) + mask &= (((((W[55]^W[56])>>29)&1)-1) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)); + if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit)) + mask &= ((((W[52]^(W[55]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit)); + if (mask & (DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit)) + mask &= ((((W[51]^(W[54]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit)); + if (mask & (DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)) + mask &= (((((W[51]^W[52])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)); + if (mask & (DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit)) + mask &= (((((W[36]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit)); + if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)) + mask &= ((0-(((W[53]^W[56])>>29)&1)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)); + if (mask & (DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit)) + mask &= ((0-(((W[51]^W[54])>>29)&1)) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit)); + if (mask & (DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit)) + mask &= ((0-(((W[50]^W[52])>>29)&1)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit)); + if (mask & (DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit)) + mask &= ((0-(((W[49]^W[51])>>29)&1)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit)); + if (mask & (DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit)) + mask &= ((0-(((W[48]^W[50])>>29)&1)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit)); + if (mask & (DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit)) + mask &= ((0-(((W[47]^W[49])>>29)&1)) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit)); + if (mask & (DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit)) + mask &= ((0-(((W[46]^W[48])>>29)&1)) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit)); + mask &= ((((W[45]^W[47])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit|DV_I_51_2_bit)); + if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit)) + mask &= ((0-(((W[45]^W[47])>>29)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit)); + mask &= (((((W[44]^W[46])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit|DV_I_50_2_bit)); + if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit)) + mask &= ((0-(((W[44]^W[46])>>29)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit)); + mask &= ((0-((W[41]^(W[42]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_II_46_2_bit|DV_II_51_2_bit)); + mask &= ((0-((W[40]^(W[41]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_51_2_bit|DV_II_50_2_bit)); + if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit)) + mask &= ((0-(((W[40]^W[42])>>4)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit)); + mask &= ((0-((W[39]^(W[40]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_50_2_bit|DV_II_49_2_bit)); + if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit)) + mask &= ((0-(((W[39]^W[41])>>4)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit)); + if (mask & (DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)) + mask &= ((0-(((W[38]^W[40])>>4)&1)) | ~(DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)); + if (mask & (DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)) + mask &= ((0-(((W[37]^W[39])>>4)&1)) | ~(DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)); + mask &= ((0-((W[36]^(W[37]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_50_2_bit|DV_II_46_2_bit)); + if (mask & (DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit)) + mask &= (((((W[35]>>4)^(W[39]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit)); + if (mask & (DV_I_48_0_bit|DV_II_48_0_bit)) + mask &= ((0-((W[63]^(W[64]>>5))&(1<<0))) | ~(DV_I_48_0_bit|DV_II_48_0_bit)); + if (mask & (DV_I_45_0_bit|DV_II_45_0_bit)) + mask &= ((0-((W[63]^(W[64]>>5))&(1<<1))) | ~(DV_I_45_0_bit|DV_II_45_0_bit)); + if (mask & (DV_I_47_0_bit|DV_II_47_0_bit)) + mask &= ((0-((W[62]^(W[63]>>5))&(1<<0))) | ~(DV_I_47_0_bit|DV_II_47_0_bit)); + if (mask & (DV_I_46_0_bit|DV_II_46_0_bit)) + mask &= ((0-((W[61]^(W[62]>>5))&(1<<0))) | ~(DV_I_46_0_bit|DV_II_46_0_bit)); + mask &= ((0-((W[61]^(W[62]>>5))&(1<<2))) | ~(DV_I_46_2_bit|DV_II_46_2_bit)); + if (mask & (DV_I_45_0_bit|DV_II_45_0_bit)) + mask &= ((0-((W[60]^(W[61]>>5))&(1<<0))) | ~(DV_I_45_0_bit|DV_II_45_0_bit)); + if (mask & (DV_II_51_0_bit|DV_II_54_0_bit)) + mask &= (((((W[58]^W[59])>>29)&1)-1) | ~(DV_II_51_0_bit|DV_II_54_0_bit)); + if (mask & (DV_II_50_0_bit|DV_II_53_0_bit)) + mask &= (((((W[57]^W[58])>>29)&1)-1) | ~(DV_II_50_0_bit|DV_II_53_0_bit)); + if (mask & (DV_II_52_0_bit|DV_II_54_0_bit)) + mask &= ((((W[56]^(W[59]>>25))&(1<<4))-(1<<4)) | ~(DV_II_52_0_bit|DV_II_54_0_bit)); + if (mask & (DV_II_51_0_bit|DV_II_52_0_bit)) + mask &= ((0-(((W[56]^W[59])>>29)&1)) | ~(DV_II_51_0_bit|DV_II_52_0_bit)); + if (mask & (DV_II_49_0_bit|DV_II_52_0_bit)) + mask &= (((((W[56]^W[57])>>29)&1)-1) | ~(DV_II_49_0_bit|DV_II_52_0_bit)); + if (mask & (DV_II_51_0_bit|DV_II_53_0_bit)) + mask &= ((((W[55]^(W[58]>>25))&(1<<4))-(1<<4)) | ~(DV_II_51_0_bit|DV_II_53_0_bit)); + if (mask & (DV_II_50_0_bit|DV_II_52_0_bit)) + mask &= ((((W[54]^(W[57]>>25))&(1<<4))-(1<<4)) | ~(DV_II_50_0_bit|DV_II_52_0_bit)); + if (mask & (DV_II_49_0_bit|DV_II_51_0_bit)) + mask &= ((((W[53]^(W[56]>>25))&(1<<4))-(1<<4)) | ~(DV_II_49_0_bit|DV_II_51_0_bit)); + mask &= ((((W[51]^(W[50]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_46_2_bit)); + mask &= ((((W[48]^W[50])&(1<<6))-(1<<6)) | ~(DV_I_50_2_bit|DV_II_46_2_bit)); + if (mask & (DV_I_51_0_bit|DV_I_52_0_bit)) + mask &= ((0-(((W[48]^W[55])>>29)&1)) | ~(DV_I_51_0_bit|DV_I_52_0_bit)); + mask &= ((((W[47]^W[49])&(1<<6))-(1<<6)) | ~(DV_I_49_2_bit|DV_I_51_2_bit)); + mask &= ((((W[48]^(W[47]>>5))&(1<<1))-(1<<1)) | ~(DV_I_47_2_bit|DV_II_51_2_bit)); + mask &= ((((W[46]^W[48])&(1<<6))-(1<<6)) | ~(DV_I_48_2_bit|DV_I_50_2_bit)); + mask &= ((((W[47]^(W[46]>>5))&(1<<1))-(1<<1)) | ~(DV_I_46_2_bit|DV_II_50_2_bit)); + mask &= ((0-((W[44]^(W[45]>>5))&(1<<1))) | ~(DV_I_51_2_bit|DV_II_49_2_bit)); + mask &= ((((W[43]^W[45])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit)); + mask &= (((((W[42]^W[44])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit)); + mask &= ((((W[43]^(W[42]>>5))&(1<<1))-(1<<1)) | ~(DV_II_46_2_bit|DV_II_51_2_bit)); + mask &= ((((W[42]^(W[41]>>5))&(1<<1))-(1<<1)) | ~(DV_I_51_2_bit|DV_II_50_2_bit)); + mask &= ((((W[41]^(W[40]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_49_2_bit)); + if (mask & (DV_I_52_0_bit|DV_II_51_0_bit)) + mask &= ((((W[39]^(W[43]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_51_0_bit)); + if (mask & (DV_I_51_0_bit|DV_II_50_0_bit)) + mask &= ((((W[38]^(W[42]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_50_0_bit)); + if (mask & (DV_I_48_2_bit|DV_I_51_2_bit)) + mask &= ((0-((W[37]^(W[38]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_I_51_2_bit)); + if (mask & (DV_I_50_0_bit|DV_II_49_0_bit)) + mask &= ((((W[37]^(W[41]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_II_49_0_bit)); + if (mask & (DV_II_52_0_bit|DV_II_54_0_bit)) + mask &= ((0-((W[36]^W[38])&(1<<4))) | ~(DV_II_52_0_bit|DV_II_54_0_bit)); + mask &= ((0-((W[35]^(W[36]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_49_2_bit)); + if (mask & (DV_I_51_0_bit|DV_II_47_0_bit)) + mask &= ((((W[35]^(W[39]>>25))&(1<<3))-(1<<3)) | ~(DV_I_51_0_bit|DV_II_47_0_bit)); +if (mask) { + + if (mask & DV_I_43_0_bit) + if ( + !((W[61]^(W[62]>>5)) & (1<<1)) + || !(!((W[59]^(W[63]>>25)) & (1<<5))) + || !((W[58]^(W[63]>>30)) & (1<<0)) + ) mask &= ~DV_I_43_0_bit; + if (mask & DV_I_44_0_bit) + if ( + !((W[62]^(W[63]>>5)) & (1<<1)) + || !(!((W[60]^(W[64]>>25)) & (1<<5))) + || !((W[59]^(W[64]>>30)) & (1<<0)) + ) mask &= ~DV_I_44_0_bit; + if (mask & DV_I_46_2_bit) + mask &= ((~((W[40]^W[42])>>2)) | ~DV_I_46_2_bit); + if (mask & DV_I_47_2_bit) + if ( + !((W[62]^(W[63]>>5)) & (1<<2)) + || !(!((W[41]^W[43]) & (1<<6))) + ) mask &= ~DV_I_47_2_bit; + if (mask & DV_I_48_2_bit) + if ( + !((W[63]^(W[64]>>5)) & (1<<2)) + || !(!((W[48]^(W[49]<<5)) & (1<<6))) + ) mask &= ~DV_I_48_2_bit; + if (mask & DV_I_49_2_bit) + if ( + !(!((W[49]^(W[50]<<5)) & (1<<6))) + || !((W[42]^W[50]) & (1<<1)) + || !(!((W[39]^(W[40]<<5)) & (1<<6))) + || !((W[38]^W[40]) & (1<<1)) + ) mask &= ~DV_I_49_2_bit; + if (mask & DV_I_50_0_bit) + mask &= ((((W[36]^W[37])<<7)) | ~DV_I_50_0_bit); + if (mask & DV_I_50_2_bit) + mask &= ((((W[43]^W[51])<<11)) | ~DV_I_50_2_bit); + if (mask & DV_I_51_0_bit) + mask &= ((((W[37]^W[38])<<9)) | ~DV_I_51_0_bit); + if (mask & DV_I_51_2_bit) + if ( + !(!((W[51]^(W[52]<<5)) & (1<<6))) + || !(!((W[49]^W[51]) & (1<<6))) + || !(!((W[37]^(W[37]>>5)) & (1<<1))) + || !(!((W[35]^(W[39]>>25)) & (1<<5))) + ) mask &= ~DV_I_51_2_bit; + if (mask & DV_I_52_0_bit) + mask &= ((((W[38]^W[39])<<11)) | ~DV_I_52_0_bit); + if (mask & DV_II_46_2_bit) + mask &= ((((W[47]^W[51])<<17)) | ~DV_II_46_2_bit); + if (mask & DV_II_48_0_bit) + if ( + !(!((W[36]^(W[40]>>25)) & (1<<3))) + || !((W[35]^(W[40]<<2)) & (1<<30)) + ) mask &= ~DV_II_48_0_bit; + if (mask & DV_II_49_0_bit) + if ( + !(!((W[37]^(W[41]>>25)) & (1<<3))) + || !((W[36]^(W[41]<<2)) & (1<<30)) + ) mask &= ~DV_II_49_0_bit; + if (mask & DV_II_49_2_bit) + if ( + !(!((W[53]^(W[54]<<5)) & (1<<6))) + || !(!((W[51]^W[53]) & (1<<6))) + || !((W[50]^W[54]) & (1<<1)) + || !(!((W[45]^(W[46]<<5)) & (1<<6))) + || !(!((W[37]^(W[41]>>25)) & (1<<5))) + || !((W[36]^(W[41]>>30)) & (1<<0)) + ) mask &= ~DV_II_49_2_bit; + if (mask & DV_II_50_0_bit) + if ( + !((W[55]^W[58]) & (1<<29)) + || !(!((W[38]^(W[42]>>25)) & (1<<3))) + || !((W[37]^(W[42]<<2)) & (1<<30)) + ) mask &= ~DV_II_50_0_bit; + if (mask & DV_II_50_2_bit) + if ( + !(!((W[54]^(W[55]<<5)) & (1<<6))) + || !(!((W[52]^W[54]) & (1<<6))) + || !((W[51]^W[55]) & (1<<1)) + || !((W[45]^W[47]) & (1<<1)) + || !(!((W[38]^(W[42]>>25)) & (1<<5))) + || !((W[37]^(W[42]>>30)) & (1<<0)) + ) mask &= ~DV_II_50_2_bit; + if (mask & DV_II_51_0_bit) + if ( + !(!((W[39]^(W[43]>>25)) & (1<<3))) + || !((W[38]^(W[43]<<2)) & (1<<30)) + ) mask &= ~DV_II_51_0_bit; + if (mask & DV_II_51_2_bit) + if ( + !(!((W[55]^(W[56]<<5)) & (1<<6))) + || !(!((W[53]^W[55]) & (1<<6))) + || !((W[52]^W[56]) & (1<<1)) + || !((W[46]^W[48]) & (1<<1)) + || !(!((W[39]^(W[43]>>25)) & (1<<5))) + || !((W[38]^(W[43]>>30)) & (1<<0)) + ) mask &= ~DV_II_51_2_bit; + if (mask & DV_II_52_0_bit) + if ( + !(!((W[59]^W[60]) & (1<<29))) + || !(!((W[40]^(W[44]>>25)) & (1<<3))) + || !(!((W[40]^(W[44]>>25)) & (1<<4))) + || !((W[39]^(W[44]<<2)) & (1<<30)) + ) mask &= ~DV_II_52_0_bit; + if (mask & DV_II_53_0_bit) + if ( + !((W[58]^W[61]) & (1<<29)) + || !(!((W[57]^(W[61]>>25)) & (1<<4))) + || !(!((W[41]^(W[45]>>25)) & (1<<3))) + || !(!((W[41]^(W[45]>>25)) & (1<<4))) + ) mask &= ~DV_II_53_0_bit; + if (mask & DV_II_54_0_bit) + if ( + !(!((W[58]^(W[62]>>25)) & (1<<4))) + || !(!((W[42]^(W[46]>>25)) & (1<<3))) + || !(!((W[42]^(W[46]>>25)) & (1<<4))) + ) mask &= ~DV_II_54_0_bit; + if (mask & DV_II_55_0_bit) + if ( + !(!((W[59]^(W[63]>>25)) & (1<<4))) + || !(!((W[57]^(W[59]>>25)) & (1<<4))) + || !(!((W[43]^(W[47]>>25)) & (1<<3))) + || !(!((W[43]^(W[47]>>25)) & (1<<4))) + ) mask &= ~DV_II_55_0_bit; + if (mask & DV_II_56_0_bit) + if ( + !(!((W[60]^(W[64]>>25)) & (1<<4))) + || !(!((W[44]^(W[48]>>25)) & (1<<3))) + || !(!((W[44]^(W[48]>>25)) & (1<<4))) + ) mask &= ~DV_II_56_0_bit; +} + + dvmask[0]=mask; +} + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C +#endif diff --git a/src/hash/sha1dc/ubc_check.h b/src/hash/sha1dc/ubc_check.h new file mode 100644 index 000000000..d7e17dc73 --- /dev/null +++ b/src/hash/sha1dc/ubc_check.h @@ -0,0 +1,52 @@ +/*** +* Copyright 2017 Marc Stevens , Dan Shumow +* Distributed under the MIT Software License. +* See accompanying file LICENSE.txt or copy at +* https://opensource.org/licenses/MIT +***/ + +/* +// this file was generated by the 'parse_bitrel' program in the tools section +// using the data files from directory 'tools/data/3565' +// +// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check +// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper) +// dm[80] is the expanded message block XOR-difference defined by the DV +// testt is the step to do the recompression from for collision detection +// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check +// +// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs +// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met +// thus one needs to do the recompression check for each DV that has its bit set +*/ + +#ifndef SHA1DC_UBC_CHECK_H +#define SHA1DC_UBC_CHECK_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include +#endif + +#define DVMASKSIZE 1 +typedef struct { int dvType; int dvK; int dvB; int testt; int maski; int maskb; uint32_t dm[80]; } dv_info_t; +extern dv_info_t sha1_dvs[]; +void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]); + +#define DOSTORESTATE58 +#define DOSTORESTATE65 + +#define CHECK_DVMASK(_DVMASK) (0 != _DVMASK[0]) + +#if defined(__cplusplus) +} +#endif + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H +#endif + +#endif diff --git a/src/hashsig.c b/src/hashsig.c index e99637d8b..bea538349 100644 --- a/src/hashsig.c +++ b/src/hashsig.c @@ -214,7 +214,7 @@ static int hashsig_finalize_hashes(git_hashsig *sig) if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE && !(sig->opt & GIT_HASHSIG_ALLOW_SMALL_FILES)) { giterr_set(GITERR_INVALID, - "File too small for similarity signature calculation"); + "file too small for similarity signature calculation"); return GIT_EBUFS; } @@ -286,7 +286,7 @@ int git_hashsig_create_fromfile( if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) { if ((error = (int)buflen) < 0) giterr_set(GITERR_OS, - "Read error on '%s' calculating similarity hashes", path); + "read error on '%s' calculating similarity hashes", path); break; } diff --git a/src/idxmap.c b/src/idxmap.c new file mode 100644 index 000000000..45f0c2204 --- /dev/null +++ b/src/idxmap.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "idxmap.h" + +/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */ +static kh_inline khint_t idxentry_hash(const git_index_entry *e) +{ + const char *s = e->path; + khint_t h = (khint_t)git__tolower(*s); + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s); + return h + GIT_IDXENTRY_STAGE(e); +} + +#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0) +#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0) + +__KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal) +__KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal) + +int git_idxmap_alloc(git_idxmap **map) +{ + if ((*map = kh_init(idx)) == NULL) { + giterr_set_oom(); + return -1; + } + + return 0; +} + +int git_idxmap_icase_alloc(git_idxmap_icase **map) +{ + if ((*map = kh_init(idxicase)) == NULL) { + giterr_set_oom(); + return -1; + } + + return 0; +} + +void git_idxmap_insert(git_idxmap *map, const git_index_entry *key, void *value, int *rval) +{ + khiter_t idx = kh_put(idx, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +void git_idxmap_icase_insert(git_idxmap_icase *map, const git_index_entry *key, void *value, int *rval) +{ + khiter_t idx = kh_put(idxicase, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +size_t git_idxmap_lookup_index(git_idxmap *map, const git_index_entry *key) +{ + return kh_get(idx, map, key); +} + +size_t git_idxmap_icase_lookup_index(git_idxmap_icase *map, const git_index_entry *key) +{ + return kh_get(idxicase, map, key); +} + +void *git_idxmap_value_at(git_idxmap *map, size_t idx) +{ + return kh_val(map, idx); +} + +int git_idxmap_valid_index(git_idxmap *map, size_t idx) +{ + return idx != kh_end(map); +} + +int git_idxmap_has_data(git_idxmap *map, size_t idx) +{ + return kh_exist(map, idx); +} + +void git_idxmap_resize(git_idxmap *map, size_t size) +{ + kh_resize(idx, map, size); +} + +void git_idxmap_icase_resize(git_idxmap_icase *map, size_t size) +{ + kh_resize(idxicase, map, size); +} + +void git_idxmap_free(git_idxmap *map) +{ + kh_destroy(idx, map); +} + +void git_idxmap_clear(git_idxmap *map) +{ + kh_clear(idx, map); +} + +void git_idxmap_delete_at(git_idxmap *map, size_t idx) +{ + kh_del(idx, map, idx); +} + +void git_idxmap_icase_delete_at(git_idxmap_icase *map, size_t idx) +{ + kh_del(idxicase, map, idx); +} + +void git_idxmap_delete(git_idxmap *map, const git_index_entry *key) +{ + khiter_t idx = git_idxmap_lookup_index(map, key); + if (git_idxmap_valid_index(map, idx)) + git_idxmap_delete_at(map, idx); +} +void git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key) +{ + khiter_t idx = git_idxmap_icase_lookup_index(map, key); + if (git_idxmap_valid_index((git_idxmap *)map, idx)) + git_idxmap_icase_delete_at(map, idx); +} diff --git a/src/idxmap.h b/src/idxmap.h index 4122a89fe..dc702c36e 100644 --- a/src/idxmap.h +++ b/src/idxmap.h @@ -26,66 +26,27 @@ typedef khash_t(idxicase) git_idxmap_icase; typedef khiter_t git_idxmap_iter; -/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */ -static kh_inline khint_t idxentry_hash(const git_index_entry *e) -{ - const char *s = e->path; - khint_t h = (khint_t)git__tolower(*s); - if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s); - return h + GIT_IDXENTRY_STAGE(e); -} +int git_idxmap_alloc(git_idxmap **map); +int git_idxmap_icase_alloc(git_idxmap_icase **map); +void git_idxmap_insert(git_idxmap *map, const git_index_entry *key, void *value, int *rval); +void git_idxmap_icase_insert(git_idxmap_icase *map, const git_index_entry *key, void *value, int *rval); -#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0) -#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0) +size_t git_idxmap_lookup_index(git_idxmap *map, const git_index_entry *key); +size_t git_idxmap_icase_lookup_index(git_idxmap_icase *map, const git_index_entry *key); +void *git_idxmap_value_at(git_idxmap *map, size_t idx); +int git_idxmap_valid_index(git_idxmap *map, size_t idx); +int git_idxmap_has_data(git_idxmap *map, size_t idx); -#define GIT__USE_IDXMAP \ - __KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal) +void git_idxmap_resize(git_idxmap *map, size_t size); +void git_idxmap_icase_resize(git_idxmap_icase *map, size_t size); +void git_idxmap_free(git_idxmap *map); +void git_idxmap_clear(git_idxmap *map); -#define GIT__USE_IDXMAP_ICASE \ - __KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal) +void git_idxmap_delete_at(git_idxmap *map, size_t idx); +void git_idxmap_icase_delete_at(git_idxmap_icase *map, size_t idx); -#define git_idxmap_alloc(hp) \ - ((*(hp) = kh_init(idx)) == NULL) ? giterr_set_oom(), -1 : 0 - -#define git_idxmap_icase_alloc(hp) \ - ((*(hp) = kh_init(idxicase)) == NULL) ? giterr_set_oom(), -1 : 0 - -#define git_idxmap_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(idx, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_idxmap_icase_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(idxicase, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_idxmap_lookup_index(h, k) kh_get(idx, h, k) -#define git_idxmap_icase_lookup_index(h, k) kh_get(idxicase, h, k) -#define git_idxmap_value_at(h, idx) kh_val(h, idx) -#define git_idxmap_valid_index(h, idx) (idx != kh_end(h)) -#define git_idxmap_has_data(h, idx) kh_exist(h, idx) - -#define git_idxmap_resize(h,s) kh_resize(idx, h, s) -#define git_idxmap_free(h) kh_destroy(idx, h), h = NULL -#define git_idxmap_clear(h) kh_clear(idx, h) - -#define git_idxmap_delete_at(h, id) kh_del(idx, h, id) -#define git_idxmap_icase_delete_at(h, id) kh_del(idxicase, h, id) - -#define git_idxmap_delete(h, key) do { \ - khiter_t __pos = git_idxmap_lookup_index(h, key); \ - if (git_idxmap_valid_index(h, __pos)) \ - git_idxmap_delete_at(h, __pos); } while (0) - -#define git_idxmap_icase_delete(h, key) do { \ - khiter_t __pos = git_idxmap_icase_lookup_index(h, key); \ - if (git_idxmap_valid_index(h, __pos)) \ - git_idxmap_icase_delete_at(h, __pos); } while (0) +void git_idxmap_delete(git_idxmap *map, const git_index_entry *key); +void git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key); #define git_idxmap_begin kh_begin #define git_idxmap_end kh_end diff --git a/src/ignore.c b/src/ignore.c index dcbd5c1ca..c324d4dd4 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -175,7 +175,7 @@ static int parse_ignore_file( context = attrs->entry->path; if (git_mutex_lock(&attrs->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock ignore file"); + giterr_set(GITERR_OS, "failed to lock ignore file"); return -1; } @@ -277,8 +277,9 @@ int git_ignore__for_path( { int error = 0; const char *workdir = git_repository_workdir(repo); + git_buf infopath = GIT_BUF_INIT; - assert(ignores && path); + assert(repo && ignores && path); memset(ignores, 0, sizeof(*ignores)); ignores->repo = repo; @@ -322,10 +323,14 @@ int git_ignore__for_path( goto cleanup; } + if ((error = git_repository_item_path(&infopath, + repo, GIT_REPOSITORY_ITEM_INFO)) < 0) + goto cleanup; + /* load .git/info/exclude */ error = push_ignore_file( ignores, &ignores->ign_global, - git_repository_path(repo), GIT_IGNORE_FILE_INREPO); + infopath.ptr, GIT_IGNORE_FILE_INREPO); if (error < 0) goto cleanup; @@ -336,6 +341,7 @@ int git_ignore__for_path( git_repository_attr_cache(repo)->cfg_excl_file); cleanup: + git_buf_free(&infopath); if (error < 0) git_ignore__free(ignores); @@ -503,9 +509,9 @@ int git_ignore_path_is_ignored( unsigned int i; git_attr_file *file; - assert(ignored && pathname); + assert(repo && ignored && pathname); - workdir = repo ? git_repository_workdir(repo) : NULL; + workdir = git_repository_workdir(repo); memset(&path, 0, sizeof(path)); memset(&ignores, 0, sizeof(ignores)); diff --git a/src/ignore.h b/src/ignore.h index d40bd60f9..876c8e0ea 100644 --- a/src/ignore.h +++ b/src/ignore.h @@ -12,7 +12,7 @@ #include "attr_file.h" #define GIT_IGNORE_FILE ".gitignore" -#define GIT_IGNORE_FILE_INREPO "info/exclude" +#define GIT_IGNORE_FILE_INREPO "exclude" #define GIT_IGNORE_FILE_XDG "ignore" /* The git_ignores structure maintains three sets of ignores: diff --git a/src/index.c b/src/index.c index 42579f19a..c29e90fb0 100644 --- a/src/index.c +++ b/src/index.c @@ -27,9 +27,6 @@ #include "git2/config.h" #include "git2/sys/index.h" -GIT__USE_IDXMAP -GIT__USE_IDXMAP_ICASE - #define INSERT_IN_MAP_EX(idx, map, e, err) do { \ if ((idx)->ignore_case) \ git_idxmap_icase_insert((khash_t(idxicase) *) (map), (e), (e), (err)); \ @@ -57,10 +54,6 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr unsigned int flags, git_index_matched_path_cb cb, void *payload); -#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7) -#define short_entry_size(len) entry_size(struct entry_short, len) -#define long_entry_size(len) entry_size(struct entry_long, len) - #define minimal_entry_size (offsetof(struct entry_short, path)) static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ; @@ -570,7 +563,7 @@ int git_index_set_caps(git_index *index, int caps) if (!repo) return create_index_error( - -1, "Cannot access repository to set index caps"); + -1, "cannot access repository to set index caps"); if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORECASE)) index->ignore_case = (val != 0); @@ -639,7 +632,7 @@ int git_index_read(git_index *index, int force) if (!index->index_file_path) return create_index_error(-1, - "Failed to read index: The index is in-memory only"); + "failed to read index: The index is in-memory only"); index->on_disk = git_path_exists(index->index_file_path); @@ -653,7 +646,7 @@ int git_index_read(git_index *index, int force) ((updated = compare_checksum(index)) < 0)) { giterr_set( GITERR_INDEX, - "Failed to read index: '%s' no longer exists", + "failed to read index: '%s' no longer exists", index->index_file_path); return updated; } @@ -765,7 +758,7 @@ int git_index_set_version(git_index *index, unsigned int version) if (version < INDEX_VERSION_NUMBER_LB || version > INDEX_VERSION_NUMBER_UB) { - giterr_set(GITERR_INDEX, "Invalid version number"); + giterr_set(GITERR_INDEX, "invalid version number"); return -1; } @@ -805,7 +798,7 @@ int git_index_write_tree(git_oid *oid, git_index *index) if (repo == NULL) return create_index_error(-1, "Failed to write tree. " - "The index file is not backed up by an existing repository"); + "the index file is not backed up by an existing repository"); return git_tree__write_index(oid, index, repo); } @@ -847,7 +840,7 @@ const git_index_entry *git_index_get_bypath( if (git_idxmap_valid_index(index->entries_map, pos)) return git_idxmap_value_at(index->entries_map, pos); - giterr_set(GITERR_INDEX, "Index does not contain %s", path); + giterr_set(GITERR_INDEX, "index does not contain '%s'", path); return NULL; } @@ -934,7 +927,7 @@ static int index_entry_init( if (INDEX_OWNER(index) == NULL) return create_index_error(-1, - "Could not initialize index entry. " + "could not initialize index entry. " "Index is not backed up by an existing repository."); if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, true) < 0) @@ -1365,7 +1358,7 @@ static int index_insert( error = git_vector_insert_sorted(&index->entries, entry, index_no_dups); if (error == 0) { - INSERT_IN_MAP(index, entry, error); + INSERT_IN_MAP(index, entry, &error); } } @@ -1423,7 +1416,7 @@ int git_index_add_frombuffer( if (INDEX_OWNER(index) == NULL) return create_index_error(-1, - "Could not initialize index entry. " + "could not initialize index entry. " "Index is not backed up by an existing repository."); if (!valid_filemode(source_entry->mode)) { @@ -1592,7 +1585,7 @@ int git_index__fill(git_index *index, const git_vector *source_entries) if ((ret = git_vector_insert(&index->entries, entry)) < 0) break; - INSERT_IN_MAP(index, entry, ret); + INSERT_IN_MAP(index, entry, &ret); if (ret < 0) break; } @@ -1637,7 +1630,7 @@ int git_index_remove(git_index *index, const char *path, int stage) if (index_find(&position, index, path, 0, stage) < 0) { giterr_set( - GITERR_INDEX, "Index does not contain %s at stage %d", path, stage); + GITERR_INDEX, "index does not contain %s at stage %d", path, stage); error = GIT_ENOTFOUND; } else { error = index_remove_entry(index, position); @@ -1709,7 +1702,7 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path) if (git_vector_bsearch2( &pos, &index->entries, index->entries_search_path, path) < 0) { - giterr_set(GITERR_INDEX, "Index does not contain %s", path); + giterr_set(GITERR_INDEX, "index does not contain %s", path); return GIT_ENOTFOUND; } @@ -2153,7 +2146,7 @@ void git_index_reuc_clear(git_index *index) static int index_error_invalid(const char *message) { - giterr_set(GITERR_INDEX, "Invalid data in index - %s", message); + giterr_set(GITERR_INDEX, "invalid data in index - %s", message); return -1; } @@ -2285,12 +2278,29 @@ out_err: return 0; } +static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flags) +{ + if (varint_len) { + if (flags & GIT_IDXENTRY_EXTENDED) + return offsetof(struct entry_long, path) + path_len + 1 + varint_len; + else + return offsetof(struct entry_short, path) + path_len + 1 + varint_len; + } else { +#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7) + if (flags & GIT_IDXENTRY_EXTENDED) + return entry_size(struct entry_long, path_len); + else + return entry_size(struct entry_short, path_len); +#undef entry_size + } +} + static size_t read_entry( git_index_entry **out, git_index *index, const void *buffer, size_t buffer_size, - const char **last) + const char *last) { size_t path_length, entry_size; const char *path_ptr; @@ -2347,35 +2357,34 @@ static size_t read_entry( path_length = path_end - path_ptr; } - if (entry.flags & GIT_IDXENTRY_EXTENDED) - entry_size = long_entry_size(path_length); - else - entry_size = short_entry_size(path_length); - - if (INDEX_FOOTER_SIZE + entry_size > buffer_size) - return 0; - + entry_size = index_entry_size(path_length, 0, entry.flags); entry.path = (char *)path_ptr; } else { size_t varint_len; - size_t shared = git_decode_varint((const unsigned char *)path_ptr, - &varint_len); - size_t len = strlen(path_ptr + varint_len); - size_t last_len = strlen(*last); - size_t tmp_path_len; + size_t strip_len = git_decode_varint((const unsigned char *)path_ptr, + &varint_len); + size_t last_len = strlen(last); + size_t prefix_len = last_len - strip_len; + size_t suffix_len = strlen(path_ptr + varint_len); + size_t path_len; if (varint_len == 0) return index_error_invalid("incorrect prefix length"); - GITERR_CHECK_ALLOC_ADD(&tmp_path_len, shared, len + 1); - tmp_path = git__malloc(tmp_path_len); + GITERR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len); + GITERR_CHECK_ALLOC_ADD(&path_len, path_len, 1); + tmp_path = git__malloc(path_len); GITERR_CHECK_ALLOC(tmp_path); - memcpy(tmp_path, last, last_len); - memcpy(tmp_path + last_len, path_ptr + varint_len, len); - entry_size = long_entry_size(shared + len); + + memcpy(tmp_path, last, prefix_len); + memcpy(tmp_path + prefix_len, path_ptr + varint_len, suffix_len + 1); + entry_size = index_entry_size(suffix_len, varint_len, entry.flags); entry.path = tmp_path; } + if (INDEX_FOOTER_SIZE + entry_size > buffer_size) + return 0; + if (index_entry_dup(out, index, &entry) < 0) { git__free(tmp_path); return 0; @@ -2448,7 +2457,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) unsigned int i; struct index_header header = { 0 }; git_oid checksum_calculated, checksum_expected; - const char **last = NULL; + const char *last = NULL; const char *empty = ""; #define seek_forward(_increase) { \ @@ -2472,16 +2481,16 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) index->version = header.version; if (index->version >= INDEX_VERSION_NUMBER_COMP) - last = ∅ + last = empty; seek_forward(INDEX_HEADER_SIZE); assert(!index->entries.length); if (index->ignore_case) - kh_resize(idxicase, (khash_t(idxicase) *) index->entries_map, header.entry_count); + git_idxmap_icase_resize((khash_t(idxicase) *) index->entries_map, header.entry_count); else - kh_resize(idx, index->entries_map, header.entry_count); + git_idxmap_resize(index->entries_map, header.entry_count); /* Parse all the entries */ for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { @@ -2499,7 +2508,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) goto done; } - INSERT_IN_MAP(index, entry, error); + INSERT_IN_MAP(index, entry, &error); if (error < 0) { index_entry_free(entry); @@ -2507,6 +2516,9 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) } error = 0; + if (index->version >= INDEX_VERSION_NUMBER_COMP) + last = entry->path; + seek_forward(entry_size); } @@ -2577,11 +2589,12 @@ static bool is_index_extended(git_index *index) return (extended > 0); } -static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char **last) +static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char *last) { void *mem = NULL; struct entry_short *ondisk; size_t path_len, disk_size; + int varint_len = 0; char *path; const char *path_start = entry->path; size_t same_len = 0; @@ -2589,7 +2602,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha path_len = ((struct entry_internal *)entry)->pathlen; if (last) { - const char *last_c = *last; + const char *last_c = last; while (*path_start == *last_c) { if (!*path_start || !*last_c) @@ -2599,13 +2612,10 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha ++same_len; } path_len -= same_len; - *last = entry->path; + varint_len = git_encode_varint(NULL, 0, same_len); } - if (entry->flags & GIT_IDXENTRY_EXTENDED) - disk_size = long_entry_size(path_len); - else - disk_size = short_entry_size(path_len); + disk_size = index_entry_size(path_len, varint_len, entry->flags); if (git_filebuf_reserve(file, &mem, disk_size) < 0) return -1; @@ -2645,16 +2655,34 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha ondisk_ext->flags_extended = htons(entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS); path = ondisk_ext->path; - } - else + disk_size -= offsetof(struct entry_long, path); + } else { path = ondisk->path; + disk_size -= offsetof(struct entry_short, path); + } if (last) { - path += git_encode_varint((unsigned char *) path, - disk_size, - path_len - same_len); + varint_len = git_encode_varint((unsigned char *) path, + disk_size, same_len); + assert(varint_len > 0); + path += varint_len; + disk_size -= varint_len; + + /* + * If using path compression, we are not allowed + * to have additional trailing NULs. + */ + assert(disk_size == path_len + 1); + } else { + /* + * If no path compression is used, we do have + * NULs as padding. As such, simply assert that + * we have enough space left to write the path. + */ + assert(disk_size > path_len); } - memcpy(path, path_start, path_len); + + memcpy(path, path_start, path_len + 1); return 0; } @@ -2665,8 +2693,7 @@ static int write_entries(git_index *index, git_filebuf *file) size_t i; git_vector case_sorted, *entries; git_index_entry *entry; - const char **last = NULL; - const char *empty = ""; + const char *last = NULL; /* If index->entries is sorted case-insensitively, then we need * to re-sort it case-sensitively before writing */ @@ -2679,11 +2706,14 @@ static int write_entries(git_index *index, git_filebuf *file) } if (index->version >= INDEX_VERSION_NUMBER_COMP) - last = ∅ + last = ""; - git_vector_foreach(entries, i, entry) + git_vector_foreach(entries, i, entry) { if ((error = write_disk_entry(file, entry, last)) < 0) break; + if (index->version >= INDEX_VERSION_NUMBER_COMP) + last = entry->path; + } if (index->ignore_case) git_vector_free(&case_sorted); @@ -2979,12 +3009,12 @@ int git_index_read_tree(git_index *index, const git_tree *tree) goto cleanup; if (index->ignore_case) - kh_resize(idxicase, (khash_t(idxicase) *) entries_map, entries.length); + git_idxmap_icase_resize((khash_t(idxicase) *) entries_map, entries.length); else - kh_resize(idx, entries_map, entries.length); + git_idxmap_resize(entries_map, entries.length); git_vector_foreach(&entries, i, e) { - INSERT_IN_MAP_EX(index, entries_map, e, error); + INSERT_IN_MAP_EX(index, entries_map, e, &error); if (error < 0) { giterr_set(GITERR_INDEX, "failed to insert entry into map"); @@ -3037,9 +3067,9 @@ static int git_index_read_iterator( goto done; if (index->ignore_case && new_length_hint) - kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_length_hint); + git_idxmap_icase_resize((khash_t(idxicase) *) new_entries_map, new_length_hint); else if (new_length_hint) - kh_resize(idx, new_entries_map, new_length_hint); + git_idxmap_resize(new_entries_map, new_length_hint); opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_CONFLICTS; @@ -3103,7 +3133,7 @@ static int git_index_read_iterator( if (add_entry) { if ((error = git_vector_insert(&new_entries, add_entry)) == 0) - INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error); + INSERT_IN_MAP_EX(index, new_entries_map, add_entry, &error); } if (remove_entry && error >= 0) @@ -3390,7 +3420,7 @@ static int index_apply_to_all( i--; /* back up foreach if we removed this */ break; default: - giterr_set(GITERR_INVALID, "Unknown index action %d", action); + giterr_set(GITERR_INVALID, "unknown index action %d", action); error = -1; break; } @@ -3475,13 +3505,13 @@ int git_indexwriter_init( if (!index->index_file_path) return create_index_error(-1, - "Failed to write index: The index is in-memory only"); + "failed to write index: The index is in-memory only"); if ((error = git_filebuf_open( &writer->file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) { if (error == GIT_ELOCKED) - giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrent or crashed process"); + giterr_set(GITERR_INDEX, "the index is locked; this might be due to a concurrent or crashed process"); return error; } @@ -3530,7 +3560,7 @@ int git_indexwriter_commit(git_indexwriter *writer) if ((error = git_futils_filestamp_check( &writer->index->stamp, writer->index->index_file_path)) < 0) { - giterr_set(GITERR_OS, "Could not read index timestamp"); + giterr_set(GITERR_OS, "could not read index timestamp"); return -1; } diff --git a/src/indexer.c b/src/indexer.c index a3a866989..15f6cc2c4 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -17,8 +17,7 @@ #include "oid.h" #include "oidmap.h" #include "zstream.h" - -GIT__USE_OIDMAP +#include "object.h" extern git_mutex git__mwindow_mutex; @@ -33,9 +32,10 @@ struct entry { struct git_indexer { unsigned int parsed_header :1, - opened_pack :1, + pack_committed :1, have_stream :1, - have_delta :1; + have_delta :1, + do_fsync :1; struct git_pack_header hdr; struct git_pack_file *pack; unsigned int mode; @@ -83,12 +83,12 @@ static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack) /* Verify we recognize this pack file format. */ if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) { - giterr_set(GITERR_INDEXER, "Wrong pack signature"); + giterr_set(GITERR_INDEXER, "wrong pack signature"); return -1; } if (!pack_version_ok(hdr->hdr_version)) { - giterr_set(GITERR_INDEXER, "Wrong pack version"); + giterr_set(GITERR_INDEXER, "wrong pack version"); return -1; } @@ -125,6 +125,9 @@ int git_indexer_new( git_hash_ctx_init(&idx->hash_ctx); git_hash_ctx_init(&idx->trailer); + if (git_repository__fsync_gitdir) + idx->do_fsync = 1; + error = git_buf_joinpath(&path, prefix, suff); if (error < 0) goto cleanup; @@ -151,12 +154,23 @@ cleanup: if (fd != -1) p_close(fd); + if (git_buf_len(&tmp_path) > 0) + p_unlink(git_buf_cstr(&tmp_path)); + + if (idx->pack != NULL) + p_unlink(idx->pack->pack_name); + git_buf_free(&path); git_buf_free(&tmp_path); git__free(idx); return -1; } +void git_indexer__set_fsync(git_indexer *idx, int do_fsync) +{ + idx->do_fsync = !!do_fsync; +} + /* Try to store the delta so we can try to resolve it later */ static int store_delta(git_indexer *idx) { @@ -288,7 +302,7 @@ static int store_object(git_indexer *idx) git_oid_cpy(&pentry->sha1, &oid); pentry->offset = entry_start; - k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error); + k = git_oidmap_put(idx->pack->idx_cache, &pentry->sha1, &error); if (error == -1) { git__free(pentry); giterr_set_oom(); @@ -302,7 +316,7 @@ static int store_object(git_indexer *idx) } - kh_value(idx->pack->idx_cache, k) = pentry; + git_oidmap_set_value_at(idx->pack->idx_cache, k, pentry); git_oid_cpy(&entry->oid, &oid); @@ -327,9 +341,7 @@ on_error: GIT_INLINE(bool) has_entry(git_indexer *idx, git_oid *id) { - khiter_t k; - k = kh_get(oid, idx->pack->idx_cache, id); - return (k != kh_end(idx->pack->idx_cache)); + return git_oidmap_exists(idx->pack->idx_cache, id); } static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, git_off_t entry_start) @@ -345,14 +357,14 @@ static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_ent } pentry->offset = entry_start; - k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error); + k = git_oidmap_put(idx->pack->idx_cache, &pentry->sha1, &error); if (error <= 0) { giterr_set(GITERR_INDEXER, "cannot insert object into pack"); return -1; } - kh_value(idx->pack->idx_cache, k) = pentry; + git_oidmap_set_value_at(idx->pack->idx_cache, k, pentry); /* Add the object to the list */ if (git_vector_insert(&idx->objects, entry) < 0) @@ -376,7 +388,7 @@ static int hash_and_save(git_indexer *idx, git_rawobj *obj, git_off_t entry_star GITERR_CHECK_ALLOC(entry); if (git_odb__hashobj(&oid, obj) < 0) { - giterr_set(GITERR_INDEXER, "Failed to hash object"); + giterr_set(GITERR_INDEXER, "failed to hash object"); goto on_error; } @@ -477,13 +489,29 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t static int append_to_pack(git_indexer *idx, const void *data, size_t size) { + git_off_t new_size; + size_t mmap_alignment; + size_t page_offset; + git_off_t page_start; git_off_t current_size = idx->pack->mwf.size; int fd = idx->pack->mwf.fd; + int error; if (!size) return 0; - if (p_lseek(fd, current_size + size - 1, SEEK_SET) < 0 || + if ((error = git__mmap_alignment(&mmap_alignment)) < 0) + return error; + + /* Write a single byte to force the file system to allocate space now or + * report an error, since we can't report errors when writing using mmap. + * Round the size up to the nearest page so that we only need to perform file + * I/O when we add a page, instead of whenever we write even a single byte. */ + new_size = current_size + size; + page_offset = new_size % mmap_alignment; + page_start = new_size - page_offset; + + if (p_lseek(fd, page_start + mmap_alignment - 1, SEEK_SET) < 0 || p_write(idx->pack->mwf.fd, data, 1) < 0) { giterr_set(GITERR_OS, "cannot extend packfile '%s'", idx->pack->pack_name); return -1; @@ -909,7 +937,6 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) git_buf filename = GIT_BUF_INIT; struct entry *entry; git_oid trailer_hash, file_hash; - git_hash_ctx ctx; git_filebuf index_file = {0}; void *packfile_trailer; @@ -918,9 +945,6 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) return -1; } - if (git_hash_ctx_init(&ctx) < 0) - return -1; - /* Test for this before resolve_deltas(), as it plays with idx->off */ if (idx->off + 20 < idx->pack->mwf.size) { giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack"); @@ -964,6 +988,10 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) git_vector_sort(&idx->objects); + /* Use the trailer hash as the pack file name to ensure + * files with different contents have different names */ + git_oid_cpy(&idx->hash, &trailer_hash); + git_buf_sets(&filename, idx->pack->pack_name); git_buf_shorten(&filename, strlen("pack")); git_buf_puts(&filename, "idx"); @@ -971,7 +999,9 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) return -1; if (git_filebuf_open(&index_file, filename.ptr, - GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0) + GIT_FILEBUF_HASH_CONTENTS | + (idx->do_fsync ? GIT_FILEBUF_FSYNC : 0), + idx->mode) < 0) goto on_error; /* Write out the header */ @@ -988,9 +1018,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) /* Write out the object names (SHA-1 hashes) */ git_vector_foreach(&idx->objects, i, entry) { git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid)); - git_hash_update(&ctx, &entry->oid, GIT_OID_RAWSZ); } - git_hash_final(&idx->hash, &ctx); /* Write out the CRC32 values */ git_vector_foreach(&idx->objects, i, entry) { @@ -1041,6 +1069,18 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) goto on_error; git_mwindow_free_all(&idx->pack->mwf); + + /* Truncate file to undo rounding up to next page_size in append_to_pack */ + if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) { + giterr_set(GITERR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name); + return -1; + } + + if (idx->do_fsync && p_fsync(idx->pack->mwf.fd) < 0) { + giterr_set(GITERR_OS, "failed to fsync packfile"); + goto on_error; + } + /* We need to close the descriptor here so Windows doesn't choke on commit_at */ if (p_close(idx->pack->mwf.fd) < 0) { giterr_set(GITERR_OS, "failed to close packfile"); @@ -1053,17 +1093,23 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) goto on_error; /* And don't forget to rename the packfile to its new place. */ - p_rename(idx->pack->pack_name, git_buf_cstr(&filename)); + if (p_rename(idx->pack->pack_name, git_buf_cstr(&filename)) < 0) + goto on_error; + + /* And fsync the parent directory if we're asked to. */ + if (idx->do_fsync && + git_futils_fsync_parent(git_buf_cstr(&filename)) < 0) + goto on_error; + + idx->pack_committed = 1; git_buf_free(&filename); - git_hash_ctx_cleanup(&ctx); return 0; on_error: git_mwindow_free_all(&idx->pack->mwf); git_filebuf_cleanup(&index_file); git_buf_free(&filename); - git_hash_ctx_cleanup(&ctx); return -1; } @@ -1074,10 +1120,11 @@ void git_indexer_free(git_indexer *idx) git_vector_free_deep(&idx->objects); - if (idx->pack && idx->pack->idx_cache) { + if (idx->pack->idx_cache) { struct git_pack_entry *pentry; - kh_foreach_value( - idx->pack->idx_cache, pentry, { git__free(pentry); }); + git_oidmap_foreach_value(idx->pack->idx_cache, pentry, { + git__free(pentry); + }); git_oidmap_free(idx->pack->idx_cache); } @@ -1085,6 +1132,9 @@ void git_indexer_free(git_indexer *idx) git_vector_free_deep(&idx->deltas); if (!git_mutex_lock(&git__mwindow_mutex)) { + if (!idx->pack_committed) + git_packfile_close(idx->pack, true); + git_packfile_free(idx->pack); git_mutex_unlock(&git__mwindow_mutex); } diff --git a/src/indexer.h b/src/indexer.h new file mode 100644 index 000000000..702694bbf --- /dev/null +++ b/src/indexer.h @@ -0,0 +1,12 @@ +/* + * 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_indexer_h__ +#define INCLUDE_indexer_h__ + +extern int git_indexer__set_fsync(git_indexer *idx, int do_fsync); + +#endif diff --git a/src/integer.h b/src/integer.h index b08094c2f..61712cebf 100644 --- a/src/integer.h +++ b/src/integer.h @@ -55,16 +55,16 @@ GIT_INLINE(bool) git__add_uint64_overflow(uint64_t *out, uint64_t one, uint64_t } /* Use clang/gcc compiler intrinsics whenever possible */ -#if (SIZE_MAX == UINT_MAX) && __has_builtin(__builtin_uadd_overflow) -# define git__add_sizet_overflow(out, one, two) \ - __builtin_uadd_overflow(one, two, out) -# define git__multiply_sizet_overflow(out, one, two) \ - __builtin_umul_overflow(one, two, out) -#elif (SIZE_MAX == ULONG_MAX) && __has_builtin(__builtin_uaddl_overflow) +#if (SIZE_MAX == ULONG_MAX) && __has_builtin(__builtin_uaddl_overflow) # define git__add_sizet_overflow(out, one, two) \ __builtin_uaddl_overflow(one, two, out) # define git__multiply_sizet_overflow(out, one, two) \ __builtin_umull_overflow(one, two, out) +#elif (SIZE_MAX == UINT_MAX) && __has_builtin(__builtin_uadd_overflow) +# define git__add_sizet_overflow(out, one, two) \ + __builtin_uadd_overflow(one, two, out) +# define git__multiply_sizet_overflow(out, one, two) \ + __builtin_umul_overflow(one, two, out) #else /** diff --git a/src/merge.c b/src/merge.c index 1142917bd..6e00b5adb 100644 --- a/src/merge.c +++ b/src/merge.c @@ -78,7 +78,7 @@ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_reposito unsigned int i; if (length < 2) { - giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length); + giterr_set(GITERR_INVALID, "at least two commits are required to find an ancestor"); return -1; } @@ -104,7 +104,7 @@ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_reposito goto on_error; if (!result) { - giterr_set(GITERR_MERGE, "No merge base found"); + giterr_set(GITERR_MERGE, "no merge base found"); error = GIT_ENOTFOUND; goto on_error; } @@ -184,7 +184,7 @@ int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, co assert(out && repo && input_array); if (length < 2) { - giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length); + giterr_set(GITERR_INVALID, "at least two commits are required to find an ancestor"); return -1; } @@ -230,7 +230,7 @@ static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_reposi if (!result) { git_revwalk_free(walk); - giterr_set(GITERR_MERGE, "No merge base found"); + giterr_set(GITERR_MERGE, "no merge base found"); return GIT_ENOTFOUND; } @@ -562,7 +562,7 @@ int git_repository_mergehead_foreach( assert(repo && cb); - if ((error = git_buf_joinpath(&merge_head_path, repo->path_repository, + if ((error = git_buf_joinpath(&merge_head_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0) return error; @@ -574,7 +574,7 @@ int git_repository_mergehead_foreach( while ((line = git__strsep(&buffer, "\n")) != NULL) { if (strlen(line) != GIT_OID_HEXSZ) { - giterr_set(GITERR_INVALID, "Unable to parse OID - invalid length"); + giterr_set(GITERR_INVALID, "unable to parse OID - invalid length"); error = -1; goto cleanup; } @@ -591,7 +591,7 @@ int git_repository_mergehead_foreach( } if (*buffer) { - giterr_set(GITERR_MERGE, "No EOL at line %"PRIuZ, line_num); + giterr_set(GITERR_MERGE, "no EOL at line %"PRIuZ, line_num); error = -1; goto cleanup; } @@ -1075,7 +1075,7 @@ static int index_entry_similarity_inexact( int score = 0; int error = 0; - if (GIT_MODE_TYPE(a->mode) != GIT_MODE_TYPE(b->mode)) + if (!GIT_MODE_ISBLOB(a->mode) || !GIT_MODE_ISBLOB(b->mode)) return 0; /* update signature cache if needed */ @@ -1713,16 +1713,16 @@ static int merge_normalize_opts( if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; - if (given != NULL) + if (given != NULL) { memcpy(opts, given, sizeof(git_merge_options)); - else { + } else { git_merge_options init = GIT_MERGE_OPTIONS_INIT; memcpy(opts, &init, sizeof(init)); - - opts->flags = GIT_MERGE_FIND_RENAMES; - opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD; } + if ((opts->flags & GIT_MERGE_FIND_RENAMES) && !opts->rename_threshold) + opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD; + if (given && given->default_driver) { opts->default_driver = git__strdup(given->default_driver); GITERR_CHECK_ALLOC(opts->default_driver); @@ -2018,6 +2018,26 @@ int git_merge_trees( git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error; + assert(out && repo); + + /* if one side is treesame to the ancestor, take the other side */ + if (ancestor_tree && merge_opts && (merge_opts->flags & GIT_MERGE_SKIP_REUC)) { + const git_tree *result = NULL; + const git_oid *ancestor_tree_id = git_tree_id(ancestor_tree); + + if (our_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(our_tree))) + result = their_tree; + else if (their_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(their_tree))) + result = our_tree; + + if (result) { + if ((error = git_index_new(out)) == 0) + error = git_index_read_tree(*out, result); + + return error; + } + } + iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree( @@ -2277,7 +2297,7 @@ static int write_merge_head( assert(repo && heads); - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_HEAD_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; @@ -2305,7 +2325,7 @@ static int write_merge_mode(git_repository *repo) assert(repo); - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MODE_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; @@ -2536,7 +2556,7 @@ static int write_merge_msg( for (i = 0; i < heads_len; i++) entries[i].merge_head = heads[i]; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0 || (error = git_filebuf_write(&file, "Merge ", 6)) < 0) goto cleanup; @@ -2914,7 +2934,7 @@ int git_merge__append_conflicts_to_merge_msg( if (!git_index_has_conflicts(index)) return 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; @@ -3043,7 +3063,7 @@ int git_merge_analysis( assert(analysis_out && preference_out && repo && their_heads); if (their_heads_len != 1) { - giterr_set(GITERR_MERGE, "Can only merge a single branch"); + giterr_set(GITERR_MERGE, "can only merge a single branch"); error = -1; goto done; } @@ -3099,7 +3119,7 @@ int git_merge( assert(repo && their_heads); if (their_heads_len != 1) { - giterr_set(GITERR_MERGE, "Can only merge a single branch"); + giterr_set(GITERR_MERGE, "can only merge a single branch"); return -1; } diff --git a/src/merge_driver.c b/src/merge_driver.c index 88a53ecdb..0f35d23c2 100644 --- a/src/merge_driver.c +++ b/src/merge_driver.c @@ -32,6 +32,35 @@ static struct merge_driver_registry merge_driver_registry; static void git_merge_driver_global_shutdown(void); +const git_repository* git_merge_driver_source_repo(const git_merge_driver_source *src) +{ + assert(src); + return src->repo; +} + +const git_index_entry* git_merge_driver_source_ancestor(const git_merge_driver_source *src) +{ + assert(src); + return src->ancestor; +} + +const git_index_entry* git_merge_driver_source_ours(const git_merge_driver_source *src) +{ + assert(src); + return src->ours; +} + +const git_index_entry* git_merge_driver_source_theirs(const git_merge_driver_source *src) +{ + assert(src); + return src->theirs; +} + +const git_merge_file_options* git_merge_driver_source_file_options(const git_merge_driver_source *src) +{ + assert(src); + return src->file_opts; +} int git_merge_driver__builtin_apply( git_merge_driver *self, diff --git a/src/merge_file.c b/src/merge_file.c index 3f14a4f63..5ecd8f4d0 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -127,7 +127,7 @@ static int merge_file__xdiff( if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile, &their_mmfile, &xmparam, &mmbuffer)) < 0) { - giterr_set(GITERR_MERGE, "Failed to merge files."); + giterr_set(GITERR_MERGE, "failed to merge files"); error = -1; goto done; } diff --git a/src/mwindow.c b/src/mwindow.c index 8a5b5caee..7bb9dbbe2 100644 --- a/src/mwindow.c +++ b/src/mwindow.c @@ -14,8 +14,6 @@ #include "strmap.h" #include "pack.h" -GIT__USE_STRMAP - #define DEFAULT_WINDOW_SIZE \ (sizeof(void*) >= 8 \ ? 1 * 1024 * 1024 * 1024 \ @@ -84,7 +82,7 @@ int git_mwindow_get_pack(struct git_pack_file **out, const char *path) git_atomic_inc(&pack->refcount); - git_strmap_insert(git__pack_cache, pack->pack_name, pack, error); + git_strmap_insert(git__pack_cache, pack->pack_name, pack, &error); git_mutex_unlock(&git__mwindow_mutex); if (error < 0) { @@ -231,7 +229,7 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf) } if (!lru_w) { - giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU"); + giterr_set(GITERR_OS, "failed to close memory window; couldn't find LRU"); return -1; } diff --git a/src/netops.c b/src/netops.c index 90326ea59..4b73baa0e 100644 --- a/src/netops.c +++ b/src/netops.c @@ -144,7 +144,7 @@ int gitno_connection_data_from_url( default_port = "80"; if (data->use_ssl) { - giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed"); + giterr_set(GITERR_NET, "redirect from HTTPS to HTTP is not allowed"); goto cleanup; } } else if (!git__prefixcmp(url, prefix_https)) { @@ -155,7 +155,7 @@ int gitno_connection_data_from_url( default_port = data->use_ssl ? "443" : "80"; if (!default_port) { - giterr_set(GITERR_NET, "Unrecognized URL prefix"); + giterr_set(GITERR_NET, "unrecognized URL prefix"); goto cleanup; } @@ -187,7 +187,7 @@ int gitno_connection_data_from_url( /* Check for errors in the resulting data */ if (original_host && url[0] != '/' && strcmp(original_host, data->host)) { - giterr_set(GITERR_NET, "Cross host redirect not allowed"); + giterr_set(GITERR_NET, "cross host redirect not allowed"); error = -1; } } @@ -237,7 +237,7 @@ int gitno_extract_url_parts( const char *_host, *_port, *_path, *_userinfo; if (http_parser_parse_url(url, strlen(url), false, &u)) { - giterr_set(GITERR_NET, "Malformed URL '%s'", url); + giterr_set(GITERR_NET, "malformed URL '%s'", url); return GIT_EINVALIDSPEC; } diff --git a/src/notes.c b/src/notes.c index fe8d2164f..75108b9c9 100644 --- a/src/notes.c +++ b/src/notes.c @@ -15,7 +15,7 @@ static int note_error_notfound(void) { - giterr_set(GITERR_INVALID, "Note could not be found"); + giterr_set(GITERR_INVALID, "note could not be found"); return GIT_ENOTFOUND; } @@ -226,7 +226,7 @@ static int remove_note_in_tree_enotfound_cb( GIT_UNUSED(note_oid); GIT_UNUSED(fanout); - giterr_set(GITERR_REPOSITORY, "Object '%s' has no note", annotated_object_sha); + giterr_set(GITERR_REPOSITORY, "object '%s' has no note", annotated_object_sha); return current_error; } @@ -244,7 +244,7 @@ static int insert_note_in_tree_eexists_cb(git_tree **out, GIT_UNUSED(note_oid); GIT_UNUSED(fanout); - giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", annotated_object_sha); + giterr_set(GITERR_REPOSITORY, "note for '%s' exists already", annotated_object_sha); return current_error; } diff --git a/src/object.c b/src/object.c index 1d45f9f1b..2da36a2ee 100644 --- a/src/object.c +++ b/src/object.c @@ -66,12 +66,12 @@ int git_object__from_odb_object( /* Validate type match */ if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) { giterr_set(GITERR_INVALID, - "The requested type does not match the type in the ODB"); + "the requested type does not match the type in the ODB"); return GIT_ENOTFOUND; } if ((object_size = git_object__size(odb_obj->cached.type)) == 0) { - giterr_set(GITERR_INVALID, "The requested type is invalid"); + giterr_set(GITERR_INVALID, "the requested type is invalid"); return GIT_ENOTFOUND; } @@ -122,7 +122,7 @@ int git_object_lookup_prefix( assert(repo && object_out && id); if (len < GIT_OID_MINPREFIXLEN) { - giterr_set(GITERR_OBJECT, "Ambiguous lookup - OID prefix is too short"); + giterr_set(GITERR_OBJECT, "ambiguous lookup - OID prefix is too short"); return GIT_EAMBIGUOUS; } @@ -147,7 +147,7 @@ int git_object_lookup_prefix( if (type != GIT_OBJ_ANY && type != object->cached.type) { git_object_free(object); giterr_set(GITERR_INVALID, - "The requested type does not match the type in ODB"); + "the requested type does not match the type in ODB"); return GIT_ENOTFOUND; } @@ -292,7 +292,7 @@ static int peel_error(int error, const git_oid *oid, git_otype type) git_oid_fmt(hex_oid, oid); hex_oid[GIT_OID_HEXSZ] = '\0'; - giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be " + giterr_set(GITERR_OBJECT, "the git_object of id '%s' can not be " "successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type); return error; diff --git a/src/odb.c b/src/odb.c index 7b194c70f..ae8f247e3 100644 --- a/src/odb.c +++ b/src/odb.c @@ -31,6 +31,8 @@ #define GIT_ALTERNATES_MAX_DEPTH 5 +bool git_odb__strict_hash_verification = true; + typedef struct { git_odb_backend *backend; @@ -176,7 +178,7 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) int error = 0; if (!git_object_typeisloose(type)) { - giterr_set(GITERR_INVALID, "Invalid object type for hash"); + giterr_set(GITERR_INVALID, "invalid object type for hash"); return -1; } @@ -199,7 +201,7 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) * If size is not zero, the file was truncated after we originally * stat'd it, so we consider this a read failure too */ if (read_len < 0 || size > 0) { - giterr_set(GITERR_OS, "Error reading file for hashing"); + giterr_set(GITERR_OS, "error reading file for hashing"); error = -1; goto done; @@ -251,7 +253,7 @@ int git_odb__hashlink(git_oid *out, const char *path) return -1; if (!git__is_int(st.st_size) || (int)st.st_size < 0) { - giterr_set(GITERR_FILESYSTEM, "File size overflow for 32-bit systems"); + giterr_set(GITERR_FILESYSTEM, "file size overflow for 32-bit systems"); return -1; } @@ -269,7 +271,7 @@ int git_odb__hashlink(git_oid *out, const char *path) read_len = p_readlink(path, link_data, size); link_data[size] = '\0'; if (read_len != size) { - giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path); + giterr_set(GITERR_OS, "failed to read symlink data for '%s'", path); git__free(link_data); return -1; } @@ -295,7 +297,7 @@ int git_odb_hashfile(git_oid *out, const char *path, git_otype type) return fd; if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) { - giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); + giterr_set(GITERR_OS, "file size overflow for 32-bit systems"); p_close(fd); return -1; } @@ -475,7 +477,7 @@ size_t git_odb_num_backends(git_odb *odb) static int git_odb__error_unsupported_in_backend(const char *action) { giterr_set(GITERR_ODB, - "Cannot %s - unsupported in the loaded odb backends", action); + "cannot %s - unsupported in the loaded odb backends", action); return -1; } @@ -492,11 +494,11 @@ int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) return 0; } - giterr_set(GITERR_ODB, "No ODB backend loaded at index %" PRIuZ, pos); + giterr_set(GITERR_ODB, "no ODB backend loaded at index %" PRIuZ, pos); return GIT_ENOTFOUND; } -static int add_default_backends( +int git_odb__add_default_backends( git_odb *db, const char *objects_dir, bool as_alternates, int alternate_depth) { @@ -517,7 +519,7 @@ static int add_default_backends( if (as_alternates) return 0; - giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir); + giterr_set(GITERR_ODB, "failed to load object database in '%s'", objects_dir); return -1; } @@ -531,7 +533,7 @@ static int add_default_backends( #endif /* add the loose object backend */ - if (git_odb_backend_loose(&loose, objects_dir, -1, 0, 0, 0) < 0 || + if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 || add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0) return -1; @@ -586,7 +588,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ alternate = git_buf_cstr(&alternates_path); } - if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) + if ((result = git_odb__add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) break; } @@ -598,7 +600,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ int git_odb_add_disk_alternate(git_odb *odb, const char *path) { - return add_default_backends(odb, path, true, 0); + return git_odb__add_default_backends(odb, path, true, 0); } int git_odb_open(git_odb **out, const char *objects_dir) @@ -612,7 +614,7 @@ int git_odb_open(git_odb **out, const char *objects_dir) if (git_odb_new(&db) < 0) return -1; - if (add_default_backends(db, objects_dir, 0, 0) < 0) { + if (git_odb__add_default_backends(db, objects_dir, 0, 0) < 0) { git_odb_free(db); return -1; } @@ -621,6 +623,24 @@ int git_odb_open(git_odb **out, const char *objects_dir) return 0; } +int git_odb__set_caps(git_odb *odb, int caps) +{ + if (caps == GIT_ODB_CAP_FROM_OWNER) { + git_repository *repo = odb->rc.owner; + int val; + + if (!repo) { + giterr_set(GITERR_ODB, "cannot access repository to set odb caps"); + return -1; + } + + if (!git_repository__cvar(&val, repo, GIT_CVAR_FSYNCOBJECTFILES)) + odb->do_fsync = !!val; + } + + return 0; +} + static void odb_free(git_odb *db) { size_t i; @@ -695,7 +715,7 @@ static int odb_freshen_1( return (int)found; } -static int odb_freshen(git_odb *db, const git_oid *id) +int git_odb__freshen(git_odb *db, const git_oid *id) { assert(db && id); @@ -980,7 +1000,9 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, size_t i; git_rawobj raw; git_odb_object *object; + git_oid hashed; bool found = false; + int error = 0; if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0) found = true; @@ -993,7 +1015,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, continue; if (b->read != NULL) { - int error = b->read(&raw.data, &raw.len, &raw.type, b, id); + error = b->read(&raw.data, &raw.len, &raw.type, b, id); if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND) continue; @@ -1007,12 +1029,26 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, if (!found) return GIT_ENOTFOUND; + if (git_odb__strict_hash_verification) { + if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) + goto out; + + if (!git_oid_equal(id, &hashed)) { + error = git_odb__error_mismatch(id, &hashed); + goto out; + } + } + giterr_clear(); if ((object = odb_object__alloc(id, &raw)) == NULL) - return -1; + goto out; *out = git_cache_store_raw(odb_cache(db), object); - return 0; + +out: + if (error) + git__free(raw.data); + return error; } int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) @@ -1063,9 +1099,9 @@ 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; + int error = 0; git_oid found_full_oid = {{0}}; - git_rawobj raw; + git_rawobj raw = {0}; void *data = NULL; bool found = false; git_odb_object *object; @@ -1080,18 +1116,29 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, if (b->read_prefix != NULL) { git_oid full_oid; error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len); - if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) + + if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) { + error = 0; continue; + } if (error) - return error; + goto out; git__free(data); data = raw.data; if (found && git_oid__cmp(&full_oid, &found_full_oid)) { - git__free(raw.data); - return git_odb__error_ambiguous("multiple matches for prefix"); + git_buf buf = GIT_BUF_INIT; + + git_buf_printf(&buf, "multiple matches for prefix: %s", + git_oid_tostr_s(&full_oid)); + git_buf_printf(&buf, " %s", + git_oid_tostr_s(&found_full_oid)); + + error = git_odb__error_ambiguous(buf.ptr); + git_buf_free(&buf); + goto out; } found_full_oid = full_oid; @@ -1102,11 +1149,28 @@ static int read_prefix_1(git_odb_object **out, git_odb *db, if (!found) return GIT_ENOTFOUND; + if (git_odb__strict_hash_verification) { + git_oid hash; + + if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0) + goto out; + + if (!git_oid_equal(&found_full_oid, &hash)) { + error = git_odb__error_mismatch(&found_full_oid, &hash); + goto out; + } + } + if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) - return -1; + goto out; *out = git_cache_store_raw(odb_cache(db), object); - return 0; + +out: + if (error) + git__free(raw.data); + + return error; } int git_odb_read_prefix( @@ -1167,7 +1231,7 @@ int git_odb_write( assert(oid && db); git_odb_hash(oid, data, len, type); - if (odb_freshen(db, oid)) + if (git_odb__freshen(db, oid)) return 0; for (i = 0; i < db->backends.length && error < 0; ++i) { @@ -1264,10 +1328,10 @@ static int git_odb_stream__invalid_length( const char *action) { giterr_set(GITERR_ODB, - "Cannot %s - " - "Invalid length. %"PRIuZ" was expected. The " - "total size of the received chunks amounts to %"PRIuZ".", - action, stream->declared_size, stream->received_bytes); + "cannot %s - " + "Invalid length. %"PRIdZ" was expected. The " + "total size of the received chunks amounts to %"PRIdZ".", + action, stream->declared_size, stream->received_bytes); return -1; } @@ -1293,7 +1357,7 @@ int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream) git_hash_final(out, stream->hash_ctx); - if (odb_freshen(stream->backend->odb, out)) + if (git_odb__freshen(stream->backend->odb, out)) return 0; return stream->finalize_write(stream, out); @@ -1393,23 +1457,36 @@ int git_odb_refresh(struct git_odb *db) return 0; } +int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual) +{ + char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(expected_oid, sizeof(expected_oid), expected); + git_oid_tostr(actual_oid, sizeof(actual_oid), actual); + + giterr_set(GITERR_ODB, "object hash mismatch - expected %s but got %s", + expected_oid, actual_oid); + + return GIT_EMISMATCH; +} + int git_odb__error_notfound( const char *message, const git_oid *oid, size_t oid_len) { if (oid != NULL) { char oid_str[GIT_OID_HEXSZ + 1]; git_oid_tostr(oid_str, oid_len+1, oid); - giterr_set(GITERR_ODB, "Object not found - %s (%.*s)", + giterr_set(GITERR_ODB, "object not found - %s (%.*s)", message, (int) oid_len, oid_str); } else - giterr_set(GITERR_ODB, "Object not found - %s", message); + giterr_set(GITERR_ODB, "object not found - %s", message); return GIT_ENOTFOUND; } int git_odb__error_ambiguous(const char *message) { - giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message); + giterr_set(GITERR_ODB, "ambiguous SHA1 prefix - %s", message); return GIT_EAMBIGUOUS; } diff --git a/src/odb.h b/src/odb.h index 31a9fd1b9..61d687abf 100644 --- a/src/odb.h +++ b/src/odb.h @@ -20,6 +20,8 @@ #define GIT_OBJECT_DIR_MODE 0777 #define GIT_OBJECT_FILE_MODE 0444 +extern bool git_odb__strict_hash_verification; + /* DO NOT EXPORT */ typedef struct { void *data; /**< Raw, decompressed object data. */ @@ -38,8 +40,25 @@ struct git_odb { git_refcount rc; git_vector backends; git_cache own_cache; + unsigned int do_fsync :1; }; +typedef enum { + GIT_ODB_CAP_FROM_OWNER = -1, +} git_odb_cap_t; + +/* + * Set the capabilities for the object database. + */ +int git_odb__set_caps(git_odb *odb, int caps); + +/* + * Add the default loose and packed backends for a database. + */ +int git_odb__add_default_backends( + git_odb *db, const char *objects_dir, + bool as_alternates, int alternate_depth); + /* * Hash a git_rawobj internally. * The `git_rawobj` is supposed to be previously initialized @@ -79,6 +98,12 @@ int git_odb__hashfd_filtered( */ int git_odb__hashlink(git_oid *out, const char *path); +/** + * Generate a GIT_EMISMATCH error for the ODB. + */ +int git_odb__error_mismatch( + const git_oid *expected, const git_oid *actual); + /* * Generate a GIT_ENOTFOUND error for the ODB. */ @@ -98,6 +123,9 @@ int git_odb__read_header_or_object( git_odb_object **out, size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id); +/* freshen an entry in the object database */ +int git_odb__freshen(git_odb *db, const git_oid *id); + /* fully free the object; internal method, DO NOT EXPORT */ void git_odb_object__free(void *object); diff --git a/src/odb_loose.c b/src/odb_loose.c index f312b9c9c..99fdcb44f 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -14,6 +14,7 @@ #include "odb.h" #include "delta.h" #include "filebuf.h" +#include "object.h" #include "git2/odb_backend.h" #include "git2/types.h" @@ -204,6 +205,11 @@ static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len) return inflate(s, 0); } +static void abort_inflate(z_stream *s) +{ + inflateEnd(s); +} + static int finish_inflate(z_stream *s) { int status = Z_OK; @@ -214,7 +220,7 @@ static int finish_inflate(z_stream *s) inflateEnd(s); if ((status != Z_STREAM_END) || (s->avail_in != 0)) { - giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely"); + giterr_set(GITERR_ZLIB, "failed to finish zlib inflation; stream aborted prematurely"); return -1; } @@ -243,7 +249,7 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) zs.avail_in = (uInt)inlen; if (inflateInit(&zs) < Z_OK) { - giterr_set(GITERR_ZLIB, "Failed to inflate buffer"); + giterr_set(GITERR_ZLIB, "failed to inflate buffer"); return -1; } @@ -255,7 +261,7 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) if (status != Z_STREAM_END /* || zs.avail_in != 0 */ || zs.total_out != outlen) { - giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely"); + giterr_set(GITERR_ZLIB, "failed to inflate buffer; stream aborted prematurely"); return -1; } @@ -319,7 +325,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj) */ if ((used = get_binary_object_header(&hdr, obj)) == 0 || !git_object_typeisloose(hdr.type)) { - giterr_set(GITERR_ODB, "Failed to inflate loose object."); + giterr_set(GITERR_ODB, "failed to inflate loose object"); return -1; } @@ -366,7 +372,8 @@ static int inflate_disk_obj(git_rawobj *out, git_buf *obj) (used = get_object_header(&hdr, head)) == 0 || !git_object_typeisloose(hdr.type)) { - giterr_set(GITERR_ODB, "Failed to inflate disk object."); + abort_inflate(&zs); + giterr_set(GITERR_ODB, "failed to inflate disk object"); return -1; } @@ -455,7 +462,7 @@ static int read_header_loose(git_rawobj *out, git_buf *loc) || get_object_header(&header_obj, inflated_buffer) == 0 || git_object_typeisloose(header_obj.type) == 0) { - giterr_set(GITERR_ZLIB, "Failed to read loose object header"); + giterr_set(GITERR_ZLIB, "failed to read loose object header"); error = -1; } else { out->len = header_obj.size; @@ -838,6 +845,17 @@ static void loose_backend__stream_free(git_odb_stream *_stream) git__free(stream); } +static int filebuf_flags(loose_backend *backend) +{ + int flags = GIT_FILEBUF_TEMPORARY | + (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT); + + if (backend->fsync_object_files || git_repository__fsync_gitdir) + flags |= GIT_FILEBUF_FSYNC; + + return flags; +} + static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type) { loose_backend *backend; @@ -864,9 +882,7 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_ stream->stream.mode = GIT_STREAM_WRONLY; if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 || - git_filebuf_open(&stream->fbuf, tmp_path.ptr, - GIT_FILEBUF_TEMPORARY | - (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT), + git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend), backend->object_file_mode) < 0 || stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0) { @@ -894,9 +910,7 @@ static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, c header_len = git_odb__format_object_header(header, sizeof(header), len, type); if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 || - git_filebuf_open(&fbuf, final_path.ptr, - GIT_FILEBUF_TEMPORARY | - (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT), + git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend), backend->object_file_mode) < 0) { error = -1; diff --git a/src/odb_mempack.c b/src/odb_mempack.c index 68db3bc32..d6f2fb4af 100644 --- a/src/odb_mempack.c +++ b/src/odb_mempack.c @@ -18,8 +18,6 @@ #include "git2/types.h" #include "git2/pack.h" -GIT__USE_OIDMAP - struct memobject { git_oid oid; size_t len; @@ -41,7 +39,7 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void size_t alloc_len; int rval; - pos = kh_put(oid, db->objects, oid, &rval); + pos = git_oidmap_put(db->objects, oid, &rval); if (rval < 0) return -1; @@ -57,8 +55,8 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void obj->len = len; obj->type = type; - kh_key(db->objects, pos) = &obj->oid; - kh_val(db->objects, pos) = obj; + git_oidmap_set_key_at(db->objects, pos, &obj->oid); + git_oidmap_set_value_at(db->objects, pos, obj); if (type == GIT_OBJ_COMMIT) { struct memobject **store = git_array_alloc(db->commits); @@ -72,13 +70,8 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void static int impl__exists(git_odb_backend *backend, const git_oid *oid) { struct memory_packer_db *db = (struct memory_packer_db *)backend; - khiter_t pos; - pos = kh_get(oid, db->objects, oid); - if (pos != kh_end(db->objects)) - return 1; - - return 0; + return git_oidmap_exists(db->objects, oid); } static int impl__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) @@ -87,11 +80,11 @@ static int impl__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb struct memobject *obj = NULL; khiter_t pos; - pos = kh_get(oid, db->objects, oid); - if (pos == kh_end(db->objects)) + pos = git_oidmap_lookup_index(db->objects, oid); + if (!git_oidmap_valid_index(db->objects, pos)) return GIT_ENOTFOUND; - obj = kh_val(db->objects, pos); + obj = git_oidmap_value_at(db->objects, pos); *len_p = obj->len; *type_p = obj->type; @@ -108,11 +101,11 @@ static int impl__read_header(size_t *len_p, git_otype *type_p, git_odb_backend * struct memobject *obj = NULL; khiter_t pos; - pos = kh_get(oid, db->objects, oid); - if (pos == kh_end(db->objects)) + pos = git_oidmap_lookup_index(db->objects, oid); + if (!git_oidmap_valid_index(db->objects, pos)) return GIT_ENOTFOUND; - obj = kh_val(db->objects, pos); + obj = git_oidmap_value_at(db->objects, pos); *len_p = obj->len; *type_p = obj->type; @@ -149,7 +142,7 @@ void git_mempack_reset(git_odb_backend *_backend) struct memory_packer_db *db = (struct memory_packer_db *)_backend; struct memobject *object = NULL; - kh_foreach_value(db->objects, object, { + git_oidmap_foreach_value(db->objects, object, { git__free(object); }); @@ -177,6 +170,7 @@ int git_mempack_new(git_odb_backend **out) db->objects = git_oidmap_alloc(); + db->parent.version = GIT_ODB_BACKEND_VERSION; db->parent.read = &impl__read; db->parent.write = &impl__write; db->parent.read_header = &impl__read_header; diff --git a/src/odb_pack.c b/src/odb_pack.c index b80d0337a..51770a88e 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -428,7 +428,7 @@ static int pack_backend__read_prefix( git_oid_cpy(out_oid, short_oid); } else { struct git_pack_entry e; - git_rawobj raw; + git_rawobj raw = {NULL}; if ((error = pack_entry_find_prefix( &e, (struct pack_backend *)backend, short_oid, len)) == 0 && diff --git a/src/offmap.c b/src/offmap.c new file mode 100644 index 000000000..ab6649697 --- /dev/null +++ b/src/offmap.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "offmap.h" + +__KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal) + +git_offmap *git_offmap_alloc(void) +{ + return kh_init(off); +} + +void git_offmap_free(git_offmap *map) +{ + kh_destroy(off, map); +} + +void git_offmap_clear(git_offmap *map) +{ + kh_clear(off, map); +} + +size_t git_offmap_num_entries(git_offmap *map) +{ + return kh_size(map); +} + +size_t git_offmap_lookup_index(git_offmap *map, const git_off_t key) +{ + return kh_get(off, map, key); +} + +int git_offmap_valid_index(git_offmap *map, size_t idx) +{ + return idx != kh_end(map); +} + +int git_offmap_exists(git_offmap *map, const git_off_t key) +{ + return kh_get(off, map, key) != kh_end(map); +} + +void *git_offmap_value_at(git_offmap *map, size_t idx) +{ + return kh_val(map, idx); +} + +void git_offmap_set_value_at(git_offmap *map, size_t idx, void *value) +{ + kh_val(map, idx) = value; +} + +void git_offmap_delete_at(git_offmap *map, size_t idx) +{ + kh_del(off, map, idx); +} + +int git_offmap_put(git_offmap *map, const git_off_t key, int *err) +{ + return kh_put(off, map, key, err); +} + +void git_offmap_insert(git_offmap *map, const git_off_t key, void *value, int *rval) +{ + khiter_t idx = kh_put(off, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +void git_offmap_delete(git_offmap *map, const git_off_t key) +{ + khiter_t idx = git_offmap_lookup_index(map, key); + if (git_offmap_valid_index(map, idx)) + git_offmap_delete_at(map, idx); +} diff --git a/src/offmap.h b/src/offmap.h index 0d0e51272..f9d2483a6 100644 --- a/src/offmap.h +++ b/src/offmap.h @@ -20,45 +20,24 @@ __KHASH_TYPE(off, git_off_t, void *) typedef khash_t(off) git_offmap; -#define GIT__USE_OFFMAP \ - __KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal) +git_offmap *git_offmap_alloc(void); +void git_offmap_free(git_offmap *map); +void git_offmap_clear(git_offmap *map); -#define git_offmap_alloc() kh_init(off) -#define git_offmap_free(h) kh_destroy(off, h), h = NULL -#define git_offmap_clear(h) kh_clear(off, h) +size_t git_offmap_num_entries(git_offmap *map); -#define git_offmap_num_entries(h) kh_size(h) +size_t git_offmap_lookup_index(git_offmap *map, const git_off_t key); +int git_offmap_valid_index(git_offmap *map, size_t idx); -#define git_offmap_lookup_index(h, k) kh_get(off, h, k) -#define git_offmap_valid_index(h, idx) (idx != kh_end(h)) +int git_offmap_exists(git_offmap *map, const git_off_t key); -#define git_offmap_exists(h, k) (kh_get(off, h, k) != kh_end(h)) +void *git_offmap_value_at(git_offmap *map, size_t idx); +void git_offmap_set_value_at(git_offmap *map, size_t idx, void *value); +void git_offmap_delete_at(git_offmap *map, size_t idx); -#define git_offmap_value_at(h, idx) kh_val(h, idx) -#define git_offmap_set_value_at(h, idx, v) kh_val(h, idx) = v -#define git_offmap_delete_at(h, idx) kh_del(off, h, idx) - -#define git_offmap_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(off, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_offmap_insert2(h, key, val, oldv, rval) do { \ - khiter_t __pos = kh_put(off, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) { \ - oldv = kh_val(h, __pos); \ - kh_key(h, __pos) = key; \ - } else { oldv = NULL; } \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_offmap_delete(h, key) do { \ - khiter_t __pos = git_offmap_lookup_index(h, key); \ - if (git_offmap_valid_index(h, __pos)) \ - git_offmap_delete_at(h, __pos); } while (0) +int git_offmap_put(git_offmap *map, const git_off_t key, int *err); +void git_offmap_insert(git_offmap *map, const git_off_t key, void *value, int *rval); +void git_offmap_delete(git_offmap *map, const git_off_t key); #define git_offmap_foreach kh_foreach #define git_offmap_foreach_value kh_foreach_value diff --git a/src/oid.c b/src/oid.c index 9fe2ebb65..9dc719194 100644 --- a/src/oid.c +++ b/src/oid.c @@ -16,7 +16,7 @@ static char to_hex[] = "0123456789abcdef"; static int oid_error_invalid(const char *msg) { - giterr_set(GITERR_INVALID, "Unable to parse OID - %s", msg); + giterr_set(GITERR_INVALID, "unable to parse OID - %s", msg); return -1; } @@ -380,7 +380,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) node_index idx; if (os->full) { - giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full"); + giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full"); return -1; } @@ -395,7 +395,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) trie_node *node; if (c == -1) { - giterr_set(GITERR_INVALID, "Unable to shorten OID - invalid hex value"); + giterr_set(GITERR_INVALID, "unable to shorten OID - invalid hex value"); return -1; } @@ -410,7 +410,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]); if (node == NULL) { if (os->full) - giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full"); + giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full"); return -1; } } @@ -418,7 +418,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) if (node->children[c] == 0) { if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) { if (os->full) - giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full"); + giterr_set(GITERR_INVALID, "unable to shorten OID - OID set full"); return -1; } break; diff --git a/src/oidmap.c b/src/oidmap.c new file mode 100644 index 000000000..5f156a18e --- /dev/null +++ b/src/oidmap.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "oidmap.h" + +GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) +{ + khint_t h; + memcpy(&h, oid, sizeof(khint_t)); + return h; +} + +__KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal) + +git_oidmap *git_oidmap_alloc() +{ + return kh_init(oid); +} + +void git_oidmap_free(git_oidmap *map) +{ + kh_destroy(oid, map); +} + +void git_oidmap_clear(git_oidmap *map) +{ + kh_clear(oid, map); +} + +size_t git_oidmap_size(git_oidmap *map) +{ + return kh_size(map); +} + +size_t git_oidmap_lookup_index(git_oidmap *map, const git_oid *key) +{ + return kh_get(oid, map, key); +} + +int git_oidmap_valid_index(git_oidmap *map, size_t idx) +{ + return idx != kh_end(map); +} + +int git_oidmap_exists(git_oidmap *map, const git_oid *key) +{ + return kh_get(oid, map, key) != kh_end(map); +} + +int git_oidmap_has_data(git_oidmap *map, size_t idx) +{ + return kh_exist(map, idx); +} + +const git_oid *git_oidmap_key(git_oidmap *map, size_t idx) +{ + return kh_key(map, idx); +} + +void git_oidmap_set_key_at(git_oidmap *map, size_t idx, git_oid *key) +{ + kh_key(map, idx) = key; +} + +void *git_oidmap_value_at(git_oidmap *map, size_t idx) +{ + return kh_val(map, idx); +} + +void git_oidmap_set_value_at(git_oidmap *map, size_t idx, void *value) +{ + kh_val(map, idx) = value; +} + +void git_oidmap_delete_at(git_oidmap *map, size_t idx) +{ + kh_del(oid, map, idx); +} + +int git_oidmap_put(git_oidmap *map, const git_oid *key, int *err) +{ + return kh_put(oid, map, key, err); +} + +void git_oidmap_insert(git_oidmap *map, const git_oid *key, void *value, int *rval) +{ + khiter_t idx = kh_put(oid, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +void git_oidmap_delete(git_oidmap *map, const git_oid *key) +{ + khiter_t idx = git_oidmap_lookup_index(map, key); + if (git_oidmap_valid_index(map, idx)) + git_oidmap_delete_at(map, idx); +} diff --git a/src/oidmap.h b/src/oidmap.h index 2cf208f53..563222494 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -20,35 +20,31 @@ __KHASH_TYPE(oid, const git_oid *, void *) typedef khash_t(oid) git_oidmap; -GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) -{ - khint_t h; - memcpy(&h, oid, sizeof(khint_t)); - return h; -} +git_oidmap *git_oidmap_alloc(void); +void git_oidmap_free(git_oidmap *map); +void git_oidmap_clear(git_oidmap *map); -#define GIT__USE_OIDMAP \ - __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal) +size_t git_oidmap_size(git_oidmap *map); -#define git_oidmap_alloc() kh_init(oid) -#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL +size_t git_oidmap_lookup_index(git_oidmap *map, const git_oid *key); +int git_oidmap_valid_index(git_oidmap *map, size_t idx); -#define git_oidmap_lookup_index(h, k) kh_get(oid, h, k) -#define git_oidmap_valid_index(h, idx) (idx != kh_end(h)) +int git_oidmap_exists(git_oidmap *map, const git_oid *key); +int git_oidmap_has_data(git_oidmap *map, size_t idx); -#define git_oidmap_value_at(h, idx) kh_val(h, idx) +const git_oid *git_oidmap_key(git_oidmap *map, size_t idx); +void git_oidmap_set_key_at(git_oidmap *map, size_t idx, git_oid *key); +void *git_oidmap_value_at(git_oidmap *map, size_t idx); +void git_oidmap_set_value_at(git_oidmap *map, size_t idx, void *value); +void git_oidmap_delete_at(git_oidmap *map, size_t idx); -#define git_oidmap_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(oid, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) +int git_oidmap_put(git_oidmap *map, const git_oid *key, int *err); +void git_oidmap_insert(git_oidmap *map, const git_oid *key, void *value, int *rval); +void git_oidmap_delete(git_oidmap *map, const git_oid *key); #define git_oidmap_foreach_value kh_foreach_value -#define git_oidmap_size(h) kh_size(h) - -#define git_oidmap_clear(h) kh_clear(oid, h) +#define git_oidmap_begin kh_begin +#define git_oidmap_end kh_end #endif diff --git a/src/openssl_stream.c b/src/openssl_stream.c index ddb9c4ab9..759c5015f 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -37,7 +37,7 @@ SSL_CTX *git__ssl_ctx; #define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA" -#ifdef GIT_THREADS +#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L static git_mutex *openssl_locks; @@ -66,11 +66,11 @@ static void shutdown_ssl_locking(void) CRYPTO_set_locking_callback(NULL); for (i = 0; i < num_locks; ++i) - git_mutex_free(openssl_locks); + git_mutex_free(&openssl_locks[i]); git__free(openssl_locks); } -#endif /* GIT_THREADS */ +#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */ static BIO_METHOD *git_stream_bio_method; static int init_bio_method(void); @@ -103,8 +103,13 @@ int git_openssl_stream_global_init(void) ssl_opts |= SSL_OP_NO_COMPRESSION; #endif +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); +#else + OPENSSL_init_ssl(0, NULL); +#endif + /* * Load SSLv{2,3} and TLSv1 so that we can talk with servers * which use the SSL hellos, which are often used for @@ -146,7 +151,7 @@ int git_openssl_stream_global_init(void) int git_openssl_set_locking(void) { -#ifdef GIT_THREADS +#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L int num_locks, i; num_locks = CRYPTO_num_locks(); @@ -163,6 +168,8 @@ int git_openssl_set_locking(void) CRYPTO_set_locking_callback(openssl_locking_function); git__on_shutdown(shutdown_ssl_locking); return 0; +#elif OPENSSL_VERSION_NUMBER >= 0x10100000L + return 0; #else giterr_set(GITERR_THREAD, "libgit2 was not built with threads"); return -1; @@ -257,10 +264,10 @@ static int ssl_set_error(SSL *ssl, int error) switch (err) { case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: - giterr_set(GITERR_NET, "SSL error: connection failure\n"); + giterr_set(GITERR_NET, "SSL error: connection failure"); break; case SSL_ERROR_WANT_X509_LOOKUP: - giterr_set(GITERR_NET, "SSL error: x509 error\n"); + giterr_set(GITERR_NET, "SSL error: x509 error"); break; case SSL_ERROR_SYSCALL: e = ERR_get_error(); @@ -327,7 +334,7 @@ static int verify_server_cert(SSL *ssl, const char *host) int i = -1,j; if (SSL_get_verify_result(ssl) != X509_V_OK) { - giterr_set(GITERR_SSL, "The SSL certificate is invalid"); + giterr_set(GITERR_SSL, "the SSL certificate is invalid"); return GIT_ECERTIFICATE; } diff --git a/src/openssl_stream.h b/src/openssl_stream.h index b769437ae..f5e59dab1 100644 --- a/src/openssl_stream.h +++ b/src/openssl_stream.h @@ -27,7 +27,7 @@ extern int git_openssl_stream_new(git_stream **out, const char *host, const char -# if OPENSSL_VERSION_NUMBER < 0x10100000L +# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name) { diff --git a/src/pack-objects.c b/src/pack-objects.c index 9f62322f7..ef272e8f5 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -41,8 +41,6 @@ struct pack_write_context { git_transfer_progress *stats; }; -GIT__USE_OIDMAP - #ifdef GIT_THREADS #define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) do { \ @@ -162,7 +160,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo) git_mutex_init(&pb->progress_mutex) || git_cond_init(&pb->progress_cond)) { - giterr_set(GITERR_OS, "Failed to initialize packbuilder mutex"); + giterr_set(GITERR_OS, "failed to initialize packbuilder mutex"); goto on_error; } @@ -197,10 +195,10 @@ static void rehash(git_packbuilder *pb) size_t i; int ret; - kh_clear(oid, pb->object_ix); + git_oidmap_clear(pb->object_ix); for (i = 0, po = pb->object_list; i < pb->nr_objects; i++, po++) { - pos = kh_put(oid, pb->object_ix, &po->id, &ret); - kh_value(pb->object_ix, pos) = po; + pos = git_oidmap_put(pb->object_ix, &po->id, &ret); + git_oidmap_set_value_at(pb->object_ix, pos, po); } } @@ -216,8 +214,7 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, /* If the object already exists in the hash table, then we don't * have any work to do */ - pos = kh_get(oid, pb->object_ix, oid); - if (pos != kh_end(pb->object_ix)) + if (git_oidmap_exists(pb->object_ix, oid)) return 0; if (pb->nr_objects >= pb->nr_alloc) { @@ -225,7 +222,7 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, GITERR_CHECK_ALLOC_MULTIPLY(&newsize, newsize, 3 / 2); if (!git__is_uint32(newsize)) { - giterr_set(GITERR_NOMEMORY, "Packfile too large to fit in memory."); + giterr_set(GITERR_NOMEMORY, "packfile too large to fit in memory."); return -1; } @@ -247,13 +244,13 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, git_oid_cpy(&po->id, oid); po->hash = name_hash(name); - pos = kh_put(oid, pb->object_ix, &po->id, &ret); + pos = git_oidmap_put(pb->object_ix, &po->id, &ret); if (ret < 0) { giterr_set_oom(); return ret; } assert(ret != 0); - kh_value(pb->object_ix, pos) = po; + git_oidmap_set_value_at(pb->object_ix, pos, po); pb->done = false; @@ -298,7 +295,7 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po) goto on_error; if (error == GIT_EBUFS || delta_size != po->delta_size) { - giterr_set(GITERR_INVALID, "Delta size changed"); + giterr_set(GITERR_INVALID, "delta size changed"); goto on_error; } @@ -517,11 +514,11 @@ static int cb_tag_foreach(const char *name, git_oid *oid, void *data) GIT_UNUSED(name); - pos = kh_get(oid, pb->object_ix, oid); - if (pos == kh_end(pb->object_ix)) + pos = git_oidmap_lookup_index(pb->object_ix, oid); + if (!git_oidmap_valid_index(pb->object_ix, pos)) return 0; - po = kh_value(pb->object_ix, pos); + po = git_oidmap_value_at(pb->object_ix, pos); po->tagged = 1; /* TODO: peel objects */ @@ -808,7 +805,7 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg, if (sz != trg_size) { giterr_set(GITERR_INVALID, - "Inconsistent target object length"); + "inconsistent target object length"); return -1; } @@ -830,7 +827,7 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg, if (sz != src_size) { giterr_set(GITERR_INVALID, - "Inconsistent source object length"); + "inconsistent source object length"); return -1; } @@ -1388,6 +1385,7 @@ int git_packbuilder_write( git_indexer *indexer; git_transfer_progress stats; struct pack_write_context ctx; + int t; PREPARE_PACK; @@ -1395,6 +1393,9 @@ int git_packbuilder_write( &indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0) return -1; + if (!git_repository__cvar(&t, pb->repo, GIT_CVAR_FSYNCOBJECTFILES) && t) + git_indexer__set_fsync(indexer, 1); + ctx.indexer = indexer; ctx.stats = &stats; @@ -1541,7 +1542,7 @@ static int retrieve_object(git_walk_object **out, git_packbuilder *pb, const git if ((error = lookup_walk_object(&obj, pb, id)) < 0) return error; - git_oidmap_insert(pb->walk_objects, &obj->id, obj, error); + git_oidmap_insert(pb->walk_objects, &obj->id, obj, &error); } *out = obj; @@ -1738,7 +1739,7 @@ int git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk) if (error == GIT_ITEROVER) error = 0; - return 0; + return error; } int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload) diff --git a/src/pack-objects.h b/src/pack-objects.h index 5a84f4158..e1e0ee3c8 100644 --- a/src/pack-objects.h +++ b/src/pack-objects.h @@ -16,6 +16,7 @@ #include "netops.h" #include "zstream.h" #include "pool.h" +#include "indexer.h" #include "git2/oid.h" #include "git2/pack.h" diff --git a/src/pack.c b/src/pack.c index 2e19b3b62..f8d0dc9ac 100644 --- a/src/pack.c +++ b/src/pack.c @@ -16,9 +16,6 @@ #include -GIT__USE_OFFMAP -GIT__USE_OIDMAP - static int packfile_open(struct git_pack_file *p); static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n); static int packfile_unpack_compressed( @@ -45,7 +42,7 @@ static int pack_entry_find_offset( static int packfile_error(const char *message) { - giterr_set(GITERR_ODB, "Invalid pack file - %s", message); + giterr_set(GITERR_ODB, "invalid pack file - %s", message); return -1; } @@ -78,13 +75,12 @@ static void free_cache_object(void *o) static void cache_free(git_pack_cache *cache) { - khiter_t k; + git_pack_cache_entry *entry; if (cache->entries) { - for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) { - if (kh_exist(cache->entries, k)) - free_cache_object(kh_value(cache->entries, k)); - } + git_offmap_foreach_value(cache->entries, entry, { + free_cache_object(entry); + }); git_offmap_free(cache->entries); cache->entries = NULL; @@ -99,7 +95,7 @@ static int cache_init(git_pack_cache *cache) cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT; if (git_mutex_init(&cache->lock)) { - giterr_set(GITERR_OS, "Failed to initialize pack cache mutex"); + giterr_set(GITERR_OS, "failed to initialize pack cache mutex"); git__free(cache->entries); cache->entries = NULL; @@ -118,9 +114,9 @@ static git_pack_cache_entry *cache_get(git_pack_cache *cache, git_off_t offset) if (git_mutex_lock(&cache->lock) < 0) return NULL; - k = kh_get(off, cache->entries, offset); - if (k != kh_end(cache->entries)) { /* found it */ - entry = kh_value(cache->entries, k); + k = git_offmap_lookup_index(cache->entries, offset); + if (git_offmap_valid_index(cache->entries, k)) { /* found it */ + entry = git_offmap_value_at(cache->entries, k); git_atomic_inc(&entry->refcount); entry->last_usage = cache->use_ctr++; } @@ -132,21 +128,16 @@ static git_pack_cache_entry *cache_get(git_pack_cache *cache, git_off_t offset) /* Run with the cache lock held */ static void free_lowest_entry(git_pack_cache *cache) { + git_off_t offset; git_pack_cache_entry *entry; - khiter_t k; - - for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) { - if (!kh_exist(cache->entries, k)) - continue; - - entry = kh_value(cache->entries, k); + git_offmap_foreach(cache->entries, offset, entry, { if (entry && entry->refcount.val == 0) { cache->memory_used -= entry->raw.len; - kh_del(off, cache->entries, k); + git_offmap_delete(cache->entries, offset); free_cache_object(entry); } - } + }); } static int cache_add( @@ -170,14 +161,14 @@ static int cache_add( return -1; } /* Add it to the cache if nobody else has */ - exists = kh_get(off, cache->entries, offset) != kh_end(cache->entries); + exists = git_offmap_exists(cache->entries, offset); if (!exists) { while (cache->memory_used + base->len > cache->memory_limit) free_lowest_entry(cache); - k = kh_put(off, cache->entries, offset, &error); + k = git_offmap_put(cache->entries, offset, &error); assert(error != 0); - kh_value(cache->entries, k) = entry; + git_offmap_set_value_at(cache->entries, k, entry); cache->memory_used += entry->raw.len; *cached_out = entry; @@ -226,7 +217,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p) if (p_fstat(fd, &st) < 0) { p_close(fd); - giterr_set(GITERR_OS, "Unable to stat pack index '%s'", path); + giterr_set(GITERR_OS, "unable to stat pack index '%s'", path); return -1; } @@ -235,7 +226,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p) (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20) { p_close(fd); - giterr_set(GITERR_ODB, "Invalid pack index '%s'", path); + giterr_set(GITERR_ODB, "invalid pack index '%s'", path); return -1; } @@ -321,7 +312,7 @@ static int pack_index_open(struct git_pack_file *p) { int error = 0; size_t name_len; - git_buf idx_name = GIT_BUF_INIT; + git_buf idx_name; if (p->index_version > -1) return 0; @@ -329,11 +320,13 @@ static int pack_index_open(struct git_pack_file *p) name_len = strlen(p->pack_name); assert(name_len > strlen(".pack")); /* checked by git_pack_file alloc */ - git_buf_grow(&idx_name, name_len); + if (git_buf_init(&idx_name, name_len) < 0) + return -1; + git_buf_put(&idx_name, p->pack_name, name_len - strlen(".pack")); git_buf_puts(&idx_name, ".idx"); if (git_buf_oom(&idx_name)) { - giterr_set_oom(); + git_buf_free(&idx_name); return -1; } @@ -962,10 +955,10 @@ git_off_t get_delta_base( git_oid oid; git_oid_fromraw(&oid, base_info); - k = kh_get(oid, p->idx_cache, &oid); - if (k != kh_end(p->idx_cache)) { + k = git_oidmap_lookup_index(p->idx_cache, &oid); + if (git_oidmap_valid_index(p->idx_cache, k)) { *curpos += 20; - return ((struct git_pack_entry *)kh_value(p->idx_cache, k))->offset; + return ((struct git_pack_entry *)git_oidmap_value_at(p->idx_cache, k))->offset; } else { /* If we're building an index, don't try to find the pack * entry; we just haven't seen it yet. We'll make @@ -991,6 +984,18 @@ git_off_t get_delta_base( * ***********************************************************/ +void git_packfile_close(struct git_pack_file *p, bool unlink_packfile) +{ + if (p->mwf.fd >= 0) { + git_mwindow_free_all_locked(&p->mwf); + p_close(p->mwf.fd); + p->mwf.fd = -1; + } + + if (unlink_packfile) + p_unlink(p->pack_name); +} + void git_packfile_free(struct git_pack_file *p) { if (!p) @@ -998,10 +1003,7 @@ void git_packfile_free(struct git_pack_file *p) cache_free(&p->bases); - if (p->mwf.fd >= 0) { - git_mwindow_free_all_locked(&p->mwf); - p_close(p->mwf.fd); - } + git_packfile_close(p, false); pack_index_free(p); @@ -1082,7 +1084,7 @@ static int packfile_open(struct git_pack_file *p) return 0; cleanup: - giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name); + giterr_set(GITERR_OS, "invalid packfile '%s'", p->pack_name); if (p->mwf.fd >= 0) p_close(p->mwf.fd); @@ -1158,7 +1160,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) p->index_version = -1; if (git_mutex_init(&p->lock)) { - giterr_set(GITERR_OS, "Failed to initialize packfile mutex"); + giterr_set(GITERR_OS, "failed to initialize packfile mutex"); git__free(p); return -1; } diff --git a/src/pack.h b/src/pack.h index 5302db5b7..e2bf165f4 100644 --- a/src/pack.h +++ b/src/pack.h @@ -149,6 +149,7 @@ git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs, git_off_t *curpos, git_otype type, git_off_t delta_obj_offset); +void git_packfile_close(struct git_pack_file *p, bool unlink_packfile); void git_packfile_free(struct git_pack_file *p); int git_packfile_alloc(struct git_pack_file **pack_out, const char *path); diff --git a/src/patch_generate.c b/src/patch_generate.c index 0e5d1db31..804fc0e09 100644 --- a/src/patch_generate.c +++ b/src/patch_generate.c @@ -206,35 +206,14 @@ static int patch_generated_load(git_patch_generated *patch, git_patch_generated_ ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 || (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0)); - /* always try to load workdir content first because filtering may - * need 2x data size and this minimizes peak memory footprint - */ - if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = git_diff_file_content__load( - &patch->ofile, &patch->base.diff_opts)) < 0 || - should_skip_binary(patch, patch->ofile.file)) - goto cleanup; - } - if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = git_diff_file_content__load( - &patch->nfile, &patch->base.diff_opts)) < 0 || - should_skip_binary(patch, patch->nfile.file)) - goto cleanup; - } - - /* once workdir has been tried, load other data as needed */ - if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = git_diff_file_content__load( - &patch->ofile, &patch->base.diff_opts)) < 0 || - should_skip_binary(patch, patch->ofile.file)) - goto cleanup; - } - if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = git_diff_file_content__load( - &patch->nfile, &patch->base.diff_opts)) < 0 || - should_skip_binary(patch, patch->nfile.file)) - goto cleanup; - } + if ((error = git_diff_file_content__load( + &patch->ofile, &patch->base.diff_opts)) < 0 || + should_skip_binary(patch, patch->ofile.file)) + goto cleanup; + if ((error = git_diff_file_content__load( + &patch->nfile, &patch->base.diff_opts)) < 0 || + should_skip_binary(patch, patch->nfile.file)) + goto cleanup; /* if previously missing an oid, and now that we have it the two sides * are the same (and not submodules), update MODIFIED -> UNMODIFIED @@ -417,58 +396,10 @@ static int diff_required(git_diff *diff, const char *action) { if (diff) return 0; - giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action); + giterr_set(GITERR_INVALID, "must provide valid diff to %s", action); return -1; } -int git_diff_foreach( - git_diff *diff, - git_diff_file_cb file_cb, - git_diff_binary_cb binary_cb, - git_diff_hunk_cb hunk_cb, - git_diff_line_cb data_cb, - void *payload) -{ - int error = 0; - git_xdiff_output xo; - size_t idx; - git_patch_generated patch; - - if ((error = diff_required(diff, "git_diff_foreach")) < 0) - return error; - - memset(&xo, 0, sizeof(xo)); - memset(&patch, 0, sizeof(patch)); - diff_output_init( - &xo.output, &diff->opts, file_cb, binary_cb, hunk_cb, data_cb, payload); - git_xdiff_init(&xo, &diff->opts); - - git_vector_foreach(&diff->deltas, idx, patch.base.delta) { - - /* check flags against patch status */ - if (git_diff_delta__should_skip(&diff->opts, patch.base.delta)) - continue; - - if (binary_cb || hunk_cb || data_cb) { - if ((error = patch_generated_init(&patch, diff, idx)) != 0 || - (error = patch_generated_load(&patch, &xo.output)) != 0) - return error; - } - - if ((error = patch_generated_invoke_file_callback(&patch, &xo.output)) == 0) { - if (binary_cb || hunk_cb || data_cb) - error = patch_generated_create(&patch, &xo.output); - } - - git_patch_free(&patch.base); - - if (error) - break; - } - - return error; -} - typedef struct { git_patch_generated patch; git_diff_delta delta; @@ -776,7 +707,7 @@ int git_patch_generated_from_diff( delta = git_vector_get(&diff->deltas, idx); if (!delta) { - giterr_set(GITERR_INVALID, "Index out of range for delta in diff"); + giterr_set(GITERR_INVALID, "index out of range for delta in diff"); return GIT_ENOTFOUND; } diff --git a/src/patch_parse.c b/src/patch_parse.c index f5275947d..0a9edcd18 100644 --- a/src/patch_parse.c +++ b/src/patch_parse.c @@ -444,9 +444,9 @@ static int parse_header_git( goto done; parse_advance_ws(ctx); - parse_advance_expected_str(ctx, "\n"); - if (ctx->line_len > 0) { + if (parse_advance_expected_str(ctx, "\n") < 0 || + ctx->line_len > 0) { error = parse_err("trailing data at line %"PRIuZ, ctx->line_num); goto done; } @@ -562,8 +562,9 @@ static int parse_hunk_body( int newlines = hunk->hunk.new_lines; for (; - ctx->remain_len > 4 && (oldlines || newlines) && - memcmp(ctx->line, "@@ -", 4) != 0; + ctx->remain_len > 1 && + (oldlines || newlines) && + (ctx->remain_len <= 4 || memcmp(ctx->line, "@@ -", 4) != 0); parse_advance_line(ctx)) { int origin; diff --git a/src/path.c b/src/path.c index 767552778..5fc7a055b 100644 --- a/src/path.c +++ b/src/path.c @@ -110,6 +110,34 @@ Exit: return result; } +/* + * Determine if the path is a Windows prefix and, if so, returns + * its actual lentgh. If it is not a prefix, returns -1. + */ +static int win32_prefix_length(const char *path, int len) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(path); + GIT_UNUSED(len); +#else + /* + * Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return + * 'C:/' here + */ + if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) + return 2; + + /* + * Similarly checks if we're dealing with a network computer name + * '//computername/.git' will return '//computername/' + */ + if (looks_like_network_computer_name(path, len)) + return len; +#endif + + return -1; +} + /* * Based on the Android implementation, BSD licensed. * Check http://android.git.kernel.org/ @@ -117,7 +145,7 @@ Exit: int git_path_dirname_r(git_buf *buffer, const char *path) { const char *endp; - int result, len; + int is_prefix = 0, len; /* Empty or NULL string gets treated as "." */ if (path == NULL || *path == '\0') { @@ -131,6 +159,11 @@ int git_path_dirname_r(git_buf *buffer, const char *path) while (endp > path && *endp == '/') endp--; + if ((len = win32_prefix_length(path, endp - path + 1)) > 0) { + is_prefix = 1; + goto Exit; + } + /* Find the start of the dir */ while (endp > path && *endp != '/') endp--; @@ -146,35 +179,23 @@ int git_path_dirname_r(git_buf *buffer, const char *path) endp--; } while (endp > path && *endp == '/'); + if ((len = win32_prefix_length(path, endp - path + 1)) > 0) { + is_prefix = 1; + goto Exit; + } + /* Cast is safe because max path < max int */ len = (int)(endp - path + 1); -#ifdef GIT_WIN32 - /* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return - 'C:/' here */ - - if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) { - len = 3; - goto Exit; - } - - /* Similarly checks if we're dealing with a network computer name - '//computername/.git' will return '//computername/' */ - - if (looks_like_network_computer_name(path, len)) { - len++; - goto Exit; - } - -#endif - Exit: - result = len; + if (buffer) { + if (git_buf_set(buffer, path, len) < 0) + return -1; + if (is_prefix && git_buf_putc(buffer, '/') < 0) + return -1; + } - if (buffer != NULL && git_buf_set(buffer, path, len) < 0) - return -1; - - return result; + return len; } @@ -341,7 +362,7 @@ int git_path_prettify(git_buf *path_out, const char *path, const char *base) if (p_realpath(path, buf) == NULL) { /* giterr_set resets the errno when dealing with a GITERR_OS kind of error */ int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1; - giterr_set(GITERR_OS, "Failed to resolve path '%s'", path); + giterr_set(GITERR_OS, "failed to resolve path '%s'", path); git_buf_clear(path_out); @@ -632,24 +653,24 @@ int git_path_set_error(int errno_value, const char *path, const char *action) switch (errno_value) { case ENOENT: case ENOTDIR: - giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action); + giterr_set(GITERR_OS, "could not find '%s' to %s", path, action); return GIT_ENOTFOUND; case EINVAL: case ENAMETOOLONG: - giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path); + giterr_set(GITERR_OS, "invalid path for filesystem '%s'", path); return GIT_EINVALIDSPEC; case EEXIST: - giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path); + giterr_set(GITERR_OS, "failed %s - '%s' already exists", action, path); return GIT_EEXISTS; case EACCES: - giterr_set(GITERR_OS, "Failed %s - '%s' is locked", action, path); + giterr_set(GITERR_OS, "failed %s - '%s' is locked", action, path); return GIT_ELOCKED; default: - giterr_set(GITERR_OS, "Could not %s '%s'", action, path); + giterr_set(GITERR_OS, "could not %s '%s'", action, path); return -1; } } @@ -679,7 +700,8 @@ static bool _check_dir_contents( return false; /* save excursion */ - git_buf_joinpath(dir, dir->ptr, sub); + if (git_buf_joinpath(dir, dir->ptr, sub) < 0) + return false; result = predicate(dir->ptr); @@ -758,7 +780,7 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling) /* error out if trying to up one from a hard base */ if (to == base && ceiling != 0) { giterr_set(GITERR_INVALID, - "Cannot strip root component off url"); + "cannot strip root component off url"); return -1; } @@ -804,8 +826,8 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling) int git_path_apply_relative(git_buf *target, const char *relpath) { - git_buf_joinpath(target, git_buf_cstr(target), relpath); - return git_path_resolve_relative(target, 0); + return git_buf_joinpath(target, git_buf_cstr(target), relpath) || + git_path_resolve_relative(target, 0); } int git_path_cmp( @@ -987,7 +1009,7 @@ int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen) return 0; fail: - giterr_set(GITERR_OS, "Unable to convert unicode path data"); + giterr_set(GITERR_OS, "unable to convert unicode path data"); return -1; } @@ -1080,7 +1102,7 @@ int git_path_direach( wd_len = git_buf_len(path); if ((dir = opendir(path->ptr)) == NULL) { - giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr); + giterr_set(GITERR_OS, "failed to open directory '%s'", path->ptr); if (errno == ENOENT) return GIT_ENOTFOUND; @@ -1161,13 +1183,13 @@ int git_path_diriter_init( git_path_trim_slashes(&diriter->path_utf8); if (diriter->path_utf8.size == 0) { - giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path); + giterr_set(GITERR_FILESYSTEM, "could not open directory '%s'", path); return -1; } if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 || !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) { - giterr_set(GITERR_OS, "Could not parse the directory path '%s'", path); + giterr_set(GITERR_OS, "could not parse the directory path '%s'", path); return -1; } @@ -1180,7 +1202,7 @@ int git_path_diriter_init( is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0); if (diriter->handle == INVALID_HANDLE_VALUE) { - giterr_set(GITERR_OS, "Could not open directory '%s'", path); + giterr_set(GITERR_OS, "could not open directory '%s'", path); return -1; } @@ -1310,14 +1332,14 @@ int git_path_diriter_init( git_path_trim_slashes(&diriter->path); if (diriter->path.size == 0) { - giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path); + giterr_set(GITERR_FILESYSTEM, "could not open directory '%s'", path); return -1; } if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) { git_buf_free(&diriter->path); - giterr_set(GITERR_OS, "Failed to open directory '%s'", path); + giterr_set(GITERR_OS, "failed to open directory '%s'", path); return -1; } @@ -1350,7 +1372,7 @@ int git_path_diriter_next(git_path_diriter *diriter) return GIT_ITEROVER; giterr_set(GITERR_OS, - "Could not read directory '%s'", diriter->path.ptr); + "could not read directory '%s'", diriter->path.ptr); return -1; } } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name)); @@ -1686,6 +1708,7 @@ GIT_INLINE(unsigned int) dotgit_flags( unsigned int flags) { int protectHFS = 0, protectNTFS = 0; + int error = 0; flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL; @@ -1698,13 +1721,13 @@ GIT_INLINE(unsigned int) dotgit_flags( #endif if (repo && !protectHFS) - git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS); - if (protectHFS) + error = git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS); + if (!error && protectHFS) flags |= GIT_PATH_REJECT_DOT_GIT_HFS; if (repo && !protectNTFS) - git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS); - if (protectNTFS) + error = git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS); + if (!error && protectNTFS) flags |= GIT_PATH_REJECT_DOT_GIT_NTFS; return flags; diff --git a/src/pathspec.c b/src/pathspec.c index 361b398b8..00dba4f6b 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -487,7 +487,7 @@ static int pathspec_match_from_iterator( /* if every pattern failed to match, then we have failed */ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) { - giterr_set(GITERR_INVALID, "No matching files were found"); + giterr_set(GITERR_INVALID, "no matching files were found"); error = GIT_ENOTFOUND; } @@ -658,7 +658,7 @@ int git_pathspec_match_diff( /* if every pattern failed to match, then we have failed */ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) { - giterr_set(GITERR_INVALID, "No matching deltas were found"); + giterr_set(GITERR_INVALID, "no matching deltas were found"); error = GIT_ENOTFOUND; } diff --git a/src/posix.c b/src/posix.c index b3f1a1cd3..94deb6ab0 100644 --- a/src/posix.c +++ b/src/posix.c @@ -10,6 +10,8 @@ #include #include +size_t p_fsync__cnt = 0; + #ifndef GIT_WIN32 #ifdef NO_ADDRINFO @@ -240,7 +242,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs out->len = 0; if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { - giterr_set(GITERR_OS, "Trying to map shared-writeable"); + giterr_set(GITERR_OS, "trying to map shared-writeable"); return -1; } diff --git a/src/posix.h b/src/posix.h index f204751cf..d26371bca 100644 --- a/src/posix.h +++ b/src/posix.h @@ -24,6 +24,10 @@ #define _S_IFLNK S_IFLNK #endif +#ifndef S_IWUSR +#define S_IWUSR 00200 +#endif + #ifndef S_IXUSR #define S_IXUSR 00100 #endif @@ -111,6 +115,12 @@ extern int p_rename(const char *from, const char *to); extern int git__page_size(size_t *page_size); extern int git__mmap_alignment(size_t *page_size); +/* The number of times `p_fsync` has been called. Note that this is for + * test code only; it it not necessarily thread-safe and should not be + * relied upon in production. + */ +extern size_t p_fsync__cnt; + /** * Platform-dependent methods */ diff --git a/src/push.c b/src/push.c index b4901388b..09c234034 100644 --- a/src/push.c +++ b/src/push.c @@ -90,7 +90,7 @@ static void free_refspec(push_spec *spec) static int check_rref(char *ref) { if (git__prefixcmp(ref, "refs/")) { - giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref); + giterr_set(GITERR_INVALID, "not a valid reference '%s'", ref); return -1; } @@ -111,7 +111,7 @@ static int check_lref(git_push *push, char *ref) giterr_set(GITERR_REFERENCE, "src refspec '%s' does not match any existing object", ref); else - giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref); + giterr_set(GITERR_INVALID, "not a valid reference '%s'", ref); return -1; } @@ -321,7 +321,7 @@ static int revwalk(git_vector *commits, git_push *push) if (!git_odb_exists(push->repo->_odb, &spec->roid)) { giterr_set(GITERR_REFERENCE, - "Cannot push because a reference that you are trying to update on the remote contains commits that are not present locally."); + "cannot push because a reference that you are trying to update on the remote contains commits that are not present locally."); error = GIT_ENONFASTFORWARD; goto on_error; } @@ -332,7 +332,7 @@ static int revwalk(git_vector *commits, git_push *push) if (error == GIT_ENOTFOUND || (!error && !git_oid_equal(&base, &spec->roid))) { giterr_set(GITERR_REFERENCE, - "Cannot push non-fastforwardable reference"); + "cannot push non-fastforwardable reference"); error = GIT_ENONFASTFORWARD; goto on_error; } @@ -553,7 +553,7 @@ static int calculate_work(git_push *push) /* This is a create or update. Local ref must exist. */ if (git_reference_name_to_id( &spec->loid, push->repo, spec->refspec.src) < 0) { - giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->refspec.src); + giterr_set(GITERR_REFERENCE, "no such reference '%s'", spec->refspec.src); return -1; } } @@ -579,7 +579,7 @@ static int do_push(git_push *push, const git_remote_callbacks *callbacks) git_transport *transport = push->remote->transport; if (!transport->push) { - giterr_set(GITERR_NET, "Remote transport doesn't support push"); + giterr_set(GITERR_NET, "remote transport doesn't support push"); error = -1; goto on_error; } diff --git a/src/rebase.c b/src/rebase.c index 0af2b3cf4..f528031b3 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -92,7 +92,7 @@ static int rebase_state_type( git_buf path = GIT_BUF_INIT; git_rebase_type_t type = GIT_REBASE_TYPE_NONE; - if (git_buf_joinpath(&path, repo->path_repository, REBASE_APPLY_DIR) < 0) + if (git_buf_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0) return -1; if (git_path_isdir(git_buf_cstr(&path))) { @@ -101,7 +101,7 @@ static int rebase_state_type( } git_buf_clear(&path); - if (git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR) < 0) + if (git_buf_joinpath(&path, repo->gitdir, REBASE_MERGE_DIR) < 0) return -1; if (git_path_isdir(git_buf_cstr(&path))) { @@ -152,7 +152,7 @@ GIT_INLINE(int) rebase_readint( return error; if (git__strtol32(&num, asc_out->ptr, &eol, 10) < 0 || num < 0 || *eol) { - giterr_set(GITERR_REBASE, "The file '%s' contains an invalid numeric value", filename); + giterr_set(GITERR_REBASE, "the file '%s' contains an invalid numeric value", filename); return -1; } @@ -170,7 +170,7 @@ GIT_INLINE(int) rebase_readoid( return error; if (str_out->size != GIT_OID_HEXSZ || git_oid_fromstr(out, str_out->ptr) < 0) { - giterr_set(GITERR_REBASE, "The file '%s' contains an invalid object ID", filename); + giterr_set(GITERR_REBASE, "the file '%s' contains an invalid object ID", filename); return -1; } @@ -316,7 +316,7 @@ int git_rebase_open( goto done; if (rebase->type == GIT_REBASE_TYPE_NONE) { - giterr_set(GITERR_REBASE, "There is no rebase in progress"); + giterr_set(GITERR_REBASE, "there is no rebase in progress"); error = GIT_ENOTFOUND; goto done; } @@ -372,14 +372,14 @@ int git_rebase_open( switch (rebase->type) { case GIT_REBASE_TYPE_INTERACTIVE: - giterr_set(GITERR_REBASE, "Interactive rebase is not supported"); + giterr_set(GITERR_REBASE, "interactive rebase is not supported"); error = -1; break; case GIT_REBASE_TYPE_MERGE: error = rebase_open_merge(rebase); break; case GIT_REBASE_TYPE_APPLY: - giterr_set(GITERR_REBASE, "Patch application rebase is not supported"); + giterr_set(GITERR_REBASE, "patch application rebase is not supported"); error = -1; break; default: @@ -447,8 +447,8 @@ static int rebase_setupfiles_merge(git_rebase *rebase) size_t i; int error = 0; - if ((error = rebase_setupfile(rebase, END_FILE, -1, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 || - (error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0) + if ((error = rebase_setupfile(rebase, END_FILE, 0, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 || + (error = rebase_setupfile(rebase, ONTO_NAME_FILE, 0, "%s\n", rebase->onto_name)) < 0) goto done; for (i = 0; i < git_array_size(rebase->operations); i++) { @@ -459,7 +459,7 @@ static int rebase_setupfiles_merge(git_rebase *rebase) git_oid_fmt(id_str, &operation->id); - if ((error = rebase_setupfile(rebase, commit_filename.ptr, -1, + if ((error = rebase_setupfile(rebase, commit_filename.ptr, 0, "%.*s\n", GIT_OID_HEXSZ, id_str)) < 0) goto done; } @@ -478,7 +478,7 @@ static int rebase_setupfiles(git_rebase *rebase) git_oid_fmt(orig_head, &rebase->orig_head_id); if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) { - giterr_set(GITERR_OS, "Failed to create rebase directory '%s'", rebase->state_path); + giterr_set(GITERR_OS, "failed to create rebase directory '%s'", rebase->state_path); return -1; } @@ -486,10 +486,10 @@ static int rebase_setupfiles(git_rebase *rebase) rebase->orig_head_name; if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 || - rebase_setupfile(rebase, HEAD_NAME_FILE, -1, "%s\n", orig_head_name) < 0 || - rebase_setupfile(rebase, ONTO_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 || - rebase_setupfile(rebase, ORIG_HEAD_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 || - rebase_setupfile(rebase, QUIET_FILE, -1, rebase->quiet ? "t\n" : "\n") < 0) + rebase_setupfile(rebase, HEAD_NAME_FILE, 0, "%s\n", orig_head_name) < 0 || + rebase_setupfile(rebase, ONTO_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 || + rebase_setupfile(rebase, ORIG_HEAD_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 || + rebase_setupfile(rebase, QUIET_FILE, 0, rebase->quiet ? "t\n" : "\n") < 0) return -1; return rebase_setupfiles_merge(rebase); @@ -511,7 +511,7 @@ static int rebase_ensure_not_in_progress(git_repository *repo) return error; if (type != GIT_REBASE_TYPE_NONE) { - giterr_set(GITERR_REBASE, "There is an existing rebase in progress"); + giterr_set(GITERR_REBASE, "there is an existing rebase in progress"); return -1; } @@ -536,7 +536,7 @@ static int rebase_ensure_not_dirty( goto done; if (git_diff_num_deltas(diff) > 0) { - giterr_set(GITERR_REBASE, "Uncommitted changes exist in index"); + giterr_set(GITERR_REBASE, "uncommitted changes exist in index"); error = fail_with; goto done; } @@ -546,11 +546,13 @@ static int rebase_ensure_not_dirty( } if (check_workdir) { - if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0) + git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; + diff_opts.ignore_submodules = GIT_SUBMODULE_IGNORE_UNTRACKED; + if ((error = git_diff_index_to_workdir(&diff, repo, index, &diff_opts)) < 0) goto done; if (git_diff_num_deltas(diff) > 0) { - giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir"); + giterr_set(GITERR_REBASE, "unstaged changes exist in workdir"); error = fail_with; goto done; } @@ -624,7 +626,7 @@ static int rebase_init_merge( GIT_UNUSED(upstream); - if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0) + if ((error = git_buf_joinpath(&state_path, repo->gitdir, REBASE_MERGE_DIR)) < 0) goto done; rebase->state_path = git_buf_detach(&state_path); @@ -731,7 +733,7 @@ int git_rebase_init( if (inmemory) error = rebase_init_inmemory(rebase, repo, branch, upstream, onto); else - rebase_init_merge(rebase, repo, branch ,upstream, onto); + error = rebase_init_merge(rebase, repo, branch ,upstream, onto); if (error == 0) *out = rebase; @@ -807,7 +809,7 @@ static int rebase_next_merge( goto done; if ((parent_count = git_commit_parentcount(current_commit)) > 1) { - giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); + giterr_set(GITERR_REBASE, "cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { @@ -821,8 +823,8 @@ static int rebase_next_merge( normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit); if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || - (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 || - (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || + (error = rebase_setupfile(rebase, MSGNUM_FILE, 0, "%" PRIuZ "\n", rebase->current+1)) < 0 || + (error = rebase_setupfile(rebase, CURRENT_FILE, 0, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 || (error = git_merge__check_result(rebase->repo, index)) < 0 || (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 || @@ -864,7 +866,7 @@ static int rebase_next_inmemory( goto done; if ((parent_count = git_commit_parentcount(current_commit)) > 1) { - giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); + giterr_set(GITERR_REBASE, "cannot rebase a merge commit"); error = -1; goto done; } else if (parent_count) { @@ -1259,7 +1261,7 @@ static int rebase_copy_notes( goto done; on_error: - giterr_set(GITERR_REBASE, "Invalid rewritten file at line %d", linenum); + giterr_set(GITERR_REBASE, "invalid rewritten file at line %d", linenum); error = -1; done: diff --git a/src/refdb.c b/src/refdb.c index 85c84892b..1ee0efb31 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -128,7 +128,7 @@ int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char * int error; if (!db->backend || !db->backend->iterator) { - giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators"); + giterr_set(GITERR_REFERENCE, "this backend doesn't support iterators"); return -1; } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 8739d5b89..eb135dc01 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -26,8 +26,6 @@ #include #include -GIT__USE_STRMAP - #define DEFAULT_NESTING_LEVEL 5 #define MAX_NESTING_LEVEL 10 @@ -55,12 +53,16 @@ typedef struct refdb_fs_backend { git_refdb_backend parent; git_repository *repo; - char *path; + /* path to git directory */ + char *gitpath; + /* path to common objects' directory */ + char *commonpath; git_sortedcache *refcache; int peeling_mode; git_iterator_flag_t iterator_flags; uint32_t direach_flags; + int fsync; } refdb_fs_backend; static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name); @@ -77,7 +79,7 @@ static int packed_reload(refdb_fs_backend *backend) git_buf packedrefs = GIT_BUF_INIT; char *scan, *eof, *eol; - if (!backend->path) + if (!backend->gitpath) return 0; error = git_sortedcache_lockandload(backend->refcache, &packedrefs); @@ -185,7 +187,7 @@ static int packed_reload(refdb_fs_backend *backend) return 0; parse_failed: - giterr_set(GITERR_REFERENCE, "Corrupted packed references file"); + giterr_set(GITERR_REFERENCE, "corrupted packed references file"); git_sortedcache_clear(backend->refcache, false); git_sortedcache_wunlock(backend->refcache); @@ -212,7 +214,7 @@ static int loose_parse_oid( return 0; corrupted: - giterr_set(GITERR_REFERENCE, "Corrupted loose reference file: %s", filename); + giterr_set(GITERR_REFERENCE, "corrupted loose reference file: %s", filename); return -1; } @@ -238,7 +240,7 @@ static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name) /* if we fail to load the loose reference, assume someone changed * the filesystem under us and skip it... */ - if (loose_readbuffer(&ref_file, backend->path, name) < 0) { + if (loose_readbuffer(&ref_file, backend->gitpath, name) < 0) { giterr_clear(); goto done; } @@ -287,7 +289,7 @@ static int _dirent_loose_load(void *payload, git_buf *full_path) return error; } - file_path = full_path->ptr + strlen(backend->path); + file_path = full_path->ptr + strlen(backend->gitpath); return loose_lookup_to_packfile(backend, file_path); } @@ -303,7 +305,7 @@ static int packed_loadloose(refdb_fs_backend *backend) int error; git_buf refs_path = GIT_BUF_INIT; - if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0) + if (git_buf_joinpath(&refs_path, backend->gitpath, GIT_REFS_DIR) < 0) return -1; /* @@ -331,7 +333,7 @@ static int refdb_fs_backend__exists( assert(backend); if ((error = packed_reload(backend)) < 0 || - (error = git_buf_joinpath(&ref_path, backend->path, ref_name)) < 0) + (error = git_buf_joinpath(&ref_path, backend->gitpath, ref_name)) < 0) return error; *exists = git_path_isfile(ref_path.ptr) || @@ -349,7 +351,7 @@ static const char *loose_parse_symbolic(git_buf *file_content) refname_start = (const char *)file_content->ptr; if (git_buf_len(file_content) < header_len + 1) { - giterr_set(GITERR_REFERENCE, "Corrupted loose reference file"); + giterr_set(GITERR_REFERENCE, "corrupted loose reference file"); return NULL; } @@ -362,6 +364,19 @@ static const char *loose_parse_symbolic(git_buf *file_content) return refname_start; } +/* + * Returns whether a reference is stored per worktree or not. + * Per-worktree references are: + * + * - all pseudorefs, e.g. HEAD and MERGE_HEAD + * - all references stored inside of "refs/bisect/" + */ +static bool is_per_worktree_ref(const char *ref_name) +{ + return git__prefixcmp(ref_name, "refs/") != 0 || + git__prefixcmp(ref_name, "refs/bisect/") == 0; +} + static int loose_lookup( git_reference **out, refdb_fs_backend *backend, @@ -369,11 +384,17 @@ static int loose_lookup( { git_buf ref_file = GIT_BUF_INIT; int error = 0; + const char *ref_dir; if (out) *out = NULL; - if ((error = loose_readbuffer(&ref_file, backend->path, ref_name)) < 0) + if (is_per_worktree_ref(ref_name)) + ref_dir = backend->gitpath; + else + ref_dir = backend->commonpath; + + if ((error = loose_readbuffer(&ref_file, ref_dir, ref_name)) < 0) /* cannot read loose ref file - gah */; else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) { const char *target; @@ -398,7 +419,7 @@ static int loose_lookup( static int ref_error_notfound(const char *name) { - giterr_set(GITERR_REFERENCE, "Reference '%s' not found", name); + giterr_set(GITERR_REFERENCE, "reference '%s' not found", name); return GIT_ENOTFOUND; } @@ -484,12 +505,12 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry = NULL; - if (!backend->path) /* do nothing if no path for loose refs */ + if (!backend->commonpath) /* do nothing if no commonpath for loose refs */ return 0; fsit_opts.flags = backend->iterator_flags; - if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 || + if ((error = git_buf_printf(&path, "%s/refs", backend->commonpath)) < 0 || (error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) { git_buf_free(&path); return error; @@ -691,7 +712,7 @@ static int reference_path_available( if (exists) { giterr_set(GITERR_REFERENCE, - "Failed to write reference '%s': a reference with " + "failed to write reference '%s': a reference with " "that name already exists.", new_ref); return GIT_EEXISTS; } @@ -705,7 +726,7 @@ static int reference_path_available( if (ref && !ref_is_available(old_ref, new_ref, ref->name)) { git_sortedcache_runlock(backend->refcache); giterr_set(GITERR_REFERENCE, - "Path to reference '%s' collides with existing one", new_ref); + "path to reference '%s' collides with existing one", new_ref); return -1; } } @@ -716,26 +737,36 @@ static int reference_path_available( static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name) { - int error; + int error, filebuf_flags; git_buf ref_path = GIT_BUF_INIT; + const char *basedir; assert(file && backend && name); if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { - giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name); + giterr_set(GITERR_INVALID, "invalid reference name '%s'", name); return GIT_EINVALIDSPEC; } + if (is_per_worktree_ref(name)) + basedir = backend->gitpath; + else + basedir = backend->commonpath; + /* Remove a possibly existing empty directory hierarchy * which name would collide with the reference name */ - if ((error = git_futils_rmdir_r(name, backend->path, GIT_RMDIR_SKIP_NONEMPTY)) < 0) + if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0) return error; - if (git_buf_joinpath(&ref_path, backend->path, name) < 0) + if (git_buf_joinpath(&ref_path, basedir, name) < 0) return -1; - error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE); + filebuf_flags = GIT_FILEBUF_FORCE; + if (backend->fsync) + filebuf_flags |= GIT_FILEBUF_FSYNC; + + error = git_filebuf_open(file, ref_path.ptr, filebuf_flags, GIT_REFS_FILE_MODE); if (error == GIT_EDIRECTORY) giterr_set(GITERR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name); @@ -970,15 +1001,18 @@ static int packed_write(refdb_fs_backend *backend) { git_sortedcache *refcache = backend->refcache; git_filebuf pack_file = GIT_FILEBUF_INIT; - int error; + int error, open_flags = 0; size_t i; /* lock the cache to updates while we do this */ if ((error = git_sortedcache_wlock(refcache)) < 0) return error; + if (backend->fsync) + open_flags = GIT_FILEBUF_FSYNC; + /* Open the file! */ - if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), 0, GIT_PACKEDREFS_FILE_MODE)) < 0) + if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), open_flags, GIT_PACKEDREFS_FILE_MODE)) < 0) goto fail; /* Packfiles have a header... apparently @@ -1106,7 +1140,7 @@ out: static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message) { int error; - git_oid old_id = {{0}}; + git_oid old_id; git_reference *tmp = NULL, *head = NULL, *peeled = NULL; const char *name; @@ -1114,7 +1148,8 @@ static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref return 0; /* if we can't resolve, we use {0}*40 as old id */ - git_reference_name_to_id(&old_id, backend->repo, ref->name); + if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0) + memset(&old_id, 0, sizeof(old_id)); if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0) return error; @@ -1283,7 +1318,7 @@ static int refdb_fs_backend__delete_tail( } /* If a loose reference exists, remove it from the filesystem */ - if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0) + if (git_buf_joinpath(&loose_path, backend->gitpath, ref_name) < 0) return -1; @@ -1408,28 +1443,30 @@ static void refdb_fs_backend__free(git_refdb_backend *_backend) assert(backend); git_sortedcache_free(backend->refcache); - git__free(backend->path); + git__free(backend->gitpath); + git__free(backend->commonpath); git__free(backend); } -static int setup_namespace(git_buf *path, git_repository *repo) +static char *setup_namespace(git_repository *repo, const char *in) { - char *parts, *start, *end; + git_buf path = GIT_BUF_INIT; + char *parts, *start, *end, *out = NULL; - /* Not all repositories have a path */ - if (repo->path_repository == NULL) - return 0; + if (!in) + goto done; - /* Load the path to the repo first */ - git_buf_puts(path, repo->path_repository); + git_buf_puts(&path, in); /* if the repo is not namespaced, nothing else to do */ - if (repo->namespace == NULL) - return 0; + if (repo->namespace == NULL) { + out = git_buf_detach(&path); + goto done; + } parts = end = git__strdup(repo->namespace); if (parts == NULL) - return -1; + goto done; /* * From `man gitnamespaces`: @@ -1437,21 +1474,24 @@ static int setup_namespace(git_buf *path, git_repository *repo) * of namespaces; for example, GIT_NAMESPACE=foo/bar will store * refs under refs/namespaces/foo/refs/namespaces/bar/ */ - while ((start = git__strsep(&end, "/")) != NULL) { - git_buf_printf(path, "refs/namespaces/%s/", start); - } + while ((start = git__strsep(&end, "/")) != NULL) + git_buf_printf(&path, "refs/namespaces/%s/", start); - git_buf_printf(path, "refs/namespaces/%s/refs", end); + git_buf_printf(&path, "refs/namespaces/%s/refs", end); git__free(parts); /* Make sure that the folder with the namespace exists */ - if (git_futils_mkdir_relative(git_buf_cstr(path), repo->path_repository, - 0777, GIT_MKDIR_PATH, NULL) < 0) - return -1; + if (git_futils_mkdir_relative(git_buf_cstr(&path), in, 0777, + GIT_MKDIR_PATH, NULL) < 0) + goto done; - /* Return root of the namespaced path, i.e. without the trailing '/refs' */ - git_buf_rtruncate_at_char(path, '/'); - return 0; + /* Return root of the namespaced gitpath, i.e. without the trailing '/refs' */ + git_buf_rtruncate_at_char(&path, '/'); + out = git_buf_detach(&path); + +done: + git_buf_free(&path); + return out; } static int reflog_alloc(git_reflog **reflog, const char *name) @@ -1484,7 +1524,7 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) #define seek_forward(_increase) do { \ if (_increase >= buf_size) { \ - giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \ + giterr_set(GITERR_INVALID, "ran out of data while parsing reflog"); \ goto fail; \ } \ buf += _increase; \ @@ -1562,7 +1602,7 @@ static int create_new_reflog_file(const char *filepath) GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name) { - return git_buf_join3(path, '/', repo->path_repository, GIT_REFLOG_DIR, name); + return git_buf_join3(path, '/', repo->commondir, GIT_REFLOG_DIR, name); } static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name) @@ -1700,7 +1740,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char repo = backend->repo; if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { - giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname); + giterr_set(GITERR_INVALID, "invalid reference name '%s'", refname); return GIT_EINVALIDSPEC; } @@ -1709,7 +1749,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char if (!git_path_isfile(git_buf_cstr(&log_path))) { giterr_set(GITERR_INVALID, - "Log file for reference '%s' doesn't exist.", refname); + "log file for reference '%s' doesn't exist", refname); error = -1; goto cleanup; } @@ -1761,7 +1801,7 @@ success: /* Append to the reflog, must be called under reference lock */ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message) { - int error, is_symbolic; + int error, is_symbolic, open_flags; git_oid old_id = {{0}}, new_id = {{0}}; git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; git_repository *repo = backend->repo; @@ -1829,7 +1869,12 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co goto cleanup; } - error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE); + open_flags = O_WRONLY | O_CREAT | O_APPEND; + + if (backend->fsync) + open_flags |= O_FSYNC; + + error = git_futils_writebuffer(&buf, git_buf_cstr(&path), open_flags, GIT_REFLOG_FILE_MODE); cleanup: git_buf_free(&buf); @@ -1857,7 +1902,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ &normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0) return error; - if (git_buf_joinpath(&temp_path, repo->path_repository, GIT_REFLOG_DIR) < 0) + if (git_buf_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0) return -1; if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0) @@ -1889,7 +1934,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ p_close(fd); if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) { - giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name); + giterr_set(GITERR_OS, "failed to rename reflog for %s", new_name); error = -1; goto cleanup; } @@ -1906,7 +1951,7 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ } if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) { - giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name); + giterr_set(GITERR_OS, "failed to rename reflog for %s", new_name); error = -1; } @@ -1948,7 +1993,7 @@ int git_refdb_backend_fs( git_repository *repository) { int t = 0; - git_buf path = GIT_BUF_INIT; + git_buf gitpath = GIT_BUF_INIT; refdb_fs_backend *backend; backend = git__calloc(1, sizeof(refdb_fs_backend)); @@ -1956,18 +2001,27 @@ int git_refdb_backend_fs( backend->repo = repository; - if (setup_namespace(&path, repository) < 0) - goto fail; + if (repository->gitdir) { + backend->gitpath = setup_namespace(repository, repository->gitdir); - backend->path = git_buf_detach(&path); + if (backend->gitpath == NULL) + goto fail; + } - if (git_buf_joinpath(&path, backend->path, GIT_PACKEDREFS_FILE) < 0 || + if (repository->commondir) { + backend->commonpath = setup_namespace(repository, repository->commondir); + + if (backend->commonpath == NULL) + goto fail; + } + + if (git_buf_joinpath(&gitpath, backend->commonpath, GIT_PACKEDREFS_FILE) < 0 || git_sortedcache_new( &backend->refcache, offsetof(struct packref, name), - NULL, NULL, packref_cmp, git_buf_cstr(&path)) < 0) + NULL, NULL, packref_cmp, git_buf_cstr(&gitpath)) < 0) goto fail; - git_buf_free(&path); + git_buf_free(&gitpath); if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) { backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE; @@ -1977,6 +2031,9 @@ int git_refdb_backend_fs( backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE; backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE; } + if ((!git_repository__cvar(&t, backend->repo, GIT_CVAR_FSYNCOBJECTFILES) && t) || + git_repository__fsync_gitdir) + backend->fsync = 1; backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; @@ -1999,8 +2056,9 @@ int git_refdb_backend_fs( return 0; fail: - git_buf_free(&path); - git__free(backend->path); + git_buf_free(&gitpath); + git__free(backend->gitpath); + git__free(backend->commonpath); git__free(backend); return -1; } diff --git a/src/reflog.c b/src/reflog.c index 9ce9aee6f..98ef1b669 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -93,7 +93,7 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_sign if (newline) { if (newline[1] != '\0') { - giterr_set(GITERR_INVALID, "Reflog message cannot contain newline"); + giterr_set(GITERR_INVALID, "reflog message cannot contain newline"); goto cleanup; } @@ -193,7 +193,7 @@ int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry) entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx); if (entry == NULL) { - giterr_set(GITERR_REFERENCE, "No reflog entry at index %"PRIuZ, idx); + giterr_set(GITERR_REFERENCE, "no reflog entry at index %"PRIuZ, idx); return GIT_ENOTFOUND; } diff --git a/src/refs.c b/src/refs.c index bff443ac9..f7120d9ee 100644 --- a/src/refs.c +++ b/src/refs.c @@ -24,7 +24,7 @@ #include #include -GIT__USE_STRMAP +bool git_reference__enable_symbolic_ref_target_validation = true; #define DEFAULT_NESTING_LEVEL 5 #define MAX_NESTING_LEVEL 10 @@ -178,7 +178,8 @@ int git_reference_name_to_id( static int reference_normalize_for_repo( git_refname_t out, git_repository *repo, - const char *name) + const char *name, + bool validate) { int precompose; unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL; @@ -187,6 +188,9 @@ static int reference_normalize_for_repo( precompose) flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE; + if (!validate) + flags |= GIT_REF_FORMAT__VALIDATION_DISABLE; + return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags); } @@ -213,7 +217,7 @@ int git_reference_lookup_resolved( scan_type = GIT_REF_SYMBOLIC; - if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0) + if ((error = reference_normalize_for_repo(scan_name, repo, name, true)) < 0) return error; if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) @@ -236,7 +240,7 @@ int git_reference_lookup_resolved( if (scan_type != GIT_REF_OID && max_nesting != 0) { giterr_set(GITERR_REFERENCE, - "Cannot resolve reference (>%u levels deep)", max_nesting); + "cannot resolve reference (>%u levels deep)", max_nesting); git_reference_free(ref); return -1; } @@ -245,6 +249,40 @@ int git_reference_lookup_resolved( return 0; } +int git_reference__read_head( + git_reference **out, + git_repository *repo, + const char *path) +{ + git_buf reference = GIT_BUF_INIT; + char *name = NULL; + int error; + + if ((error = git_futils_readbuffer(&reference, path)) < 0) + goto out; + git_buf_rtrim(&reference); + + if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) == 0) { + git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF)); + + name = git_path_basename(path); + + if ((*out = git_reference__alloc_symbolic(name, reference.ptr)) == NULL) { + error = -1; + goto out; + } + } else { + if ((error = git_reference_lookup(out, repo, reference.ptr)) < 0) + goto out; + } + +out: + git__free(name); + git_buf_free(&reference); + + return error; +} + int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname) { int error = 0, i; @@ -298,7 +336,7 @@ cleanup: if (error && !foundvalid) { /* never found a valid reference name */ giterr_set(GITERR_REFERENCE, - "Could not use '%s' as valid reference name", git_buf_cstr(&name)); + "could not use '%s' as valid reference name", git_buf_cstr(&name)); } if (error == GIT_ENOTFOUND) @@ -383,7 +421,7 @@ static int reference__create( if (ref_out) *ref_out = NULL; - error = reference_normalize_for_repo(normalized, repo, name); + error = reference_normalize_for_repo(normalized, repo, name, true); if (error < 0) return error; @@ -396,7 +434,7 @@ static int reference__create( if (!git_object__is_valid(repo, oid, GIT_OBJ_ANY)) { giterr_set(GITERR_REFERENCE, - "Target OID for the reference doesn't exist on the repository"); + "target OID for the reference doesn't exist on the repository"); return -1; } @@ -404,7 +442,10 @@ static int reference__create( } else { git_refname_t normalized_target; - if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0) + error = reference_normalize_for_repo(normalized_target, repo, + symbolic, git_reference__enable_symbolic_ref_target_validation); + + if (error < 0) return error; ref = git_reference__alloc_symbolic(normalized, normalized_target); @@ -524,7 +565,7 @@ static int ensure_is_an_updatable_direct_reference(git_reference *ref) if (ref->type == GIT_REF_OID) return 0; - giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference"); + giterr_set(GITERR_REFERENCE, "cannot set OID on symbolic reference"); return -1; } @@ -552,7 +593,7 @@ static int ensure_is_an_updatable_symbolic_reference(git_reference *ref) if (ref->type == GIT_REF_SYMBOLIC) return 0; - giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference"); + giterr_set(GITERR_REFERENCE, "cannot set symbolic target on a direct reference"); return -1; } @@ -573,19 +614,62 @@ int git_reference_symbolic_set_target( out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message); } +typedef struct { + const char *old_name; + git_refname_t new_name; +} rename_cb_data; + +static int update_wt_heads(git_repository *repo, const char *path, void *payload) +{ + rename_cb_data *data = (rename_cb_data *) payload; + git_reference *head = NULL; + char *gitdir = NULL; + int error; + + if ((error = git_reference__read_head(&head, repo, path)) < 0) { + giterr_set(GITERR_REFERENCE, "could not read HEAD when renaming references"); + goto out; + } + + if ((gitdir = git_path_dirname(path)) == NULL) { + error = -1; + goto out; + } + + if (git_reference_type(head) != GIT_REF_SYMBOLIC || + git__strcmp(head->target.symbolic, data->old_name) != 0) { + error = 0; + goto out; + } + + /* Update HEAD it was pointing to the reference being renamed */ + if ((error = git_repository_create_head(gitdir, data->new_name)) < 0) { + giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference"); + goto out; + } + +out: + git_reference_free(head); + git__free(gitdir); + + return error; +} + static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force, const git_signature *signature, const char *message) { + git_repository *repo; git_refname_t normalized; bool should_head_be_updated = false; int error = 0; assert(ref && new_name && signature); - if ((error = reference_normalize_for_repo( - normalized, git_reference_owner(ref), new_name)) < 0) - return error; + repo = git_reference_owner(ref); + if ((error = reference_normalize_for_repo( + normalized, repo, new_name, true)) < 0) + return error; /* Check if we have to update HEAD. */ if ((error = git_branch_is_head(ref)) < 0) @@ -596,14 +680,18 @@ static int reference__rename(git_reference **out, git_reference *ref, const char if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0) return error; - /* Update HEAD it was pointing to the reference being renamed */ - if (should_head_be_updated && - (error = git_repository_set_head(ref->db->repo, normalized)) < 0) { - giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference"); - return error; + /* Update HEAD if it was pointing to the reference being renamed */ + if (should_head_be_updated) { + error = git_repository_set_head(ref->db->repo, normalized); + } else { + rename_cb_data payload; + payload.old_name = ref->name; + memcpy(&payload.new_name, &normalized, sizeof(normalized)); + + error = git_repository_foreach_head(repo, update_wt_heads, &payload); } - return 0; + return error; } @@ -636,7 +724,7 @@ int git_reference_resolve(git_reference **ref_out, const git_reference *ref) return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1); default: - giterr_set(GITERR_REFERENCE, "Invalid reference"); + giterr_set(GITERR_REFERENCE, "invalid reference"); return -1; } } @@ -876,6 +964,7 @@ int git_reference__normalize_name( int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC; unsigned int process_flags; bool normalize = (buf != NULL); + bool validate = (flags & GIT_REF_FORMAT__VALIDATION_DISABLE) == 0; #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; @@ -886,7 +975,7 @@ int git_reference__normalize_name( process_flags = flags; current = (char *)name; - if (*current == '/') + if (validate && *current == '/') goto cleanup; if (normalize) @@ -902,6 +991,13 @@ int git_reference__normalize_name( } #endif + if (!validate) { + git_buf_sets(buf, current); + + error = git_buf_oom(buf) ? -1 : 0; + goto cleanup; + } + while (true) { segment_len = ensure_segment_validity(current); if (segment_len < 0) { @@ -973,7 +1069,7 @@ cleanup: if (error == GIT_EINVALIDSPEC) giterr_set( GITERR_REFERENCE, - "The given reference name '%s' is not valid", name); + "the given reference name '%s' is not valid", name); if (error && normalize) git_buf_free(buf); @@ -1000,7 +1096,7 @@ int git_reference_normalize_name( if (git_buf_len(&buf) > buffer_size - 1) { giterr_set( GITERR_REFERENCE, - "The provided buffer is too short to hold the normalization of '%s'", name); + "the provided buffer is too short to hold the normalization of '%s'", name); error = GIT_EBUFS; goto cleanup; } @@ -1046,7 +1142,7 @@ static int get_terminal(git_reference **out, git_repository *repo, const char *r int error = 0; if (nesting > MAX_NESTING_LEVEL) { - giterr_set(GITERR_REFERENCE, "Reference chain too deep (%d)", nesting); + giterr_set(GITERR_REFERENCE, "reference chain too deep (%d)", nesting); return GIT_ENOTFOUND; } @@ -1115,6 +1211,18 @@ int git_reference__update_terminal( return error; } +static const char *commit_type(const git_commit *commit) +{ + unsigned int count = git_commit_parentcount(commit); + + if (count >= 2) + return " (merge)"; + else if (count == 0) + return " (initial)"; + else + return ""; +} + int git_reference__update_for_commit( git_repository *repo, git_reference *ref, @@ -1131,7 +1239,7 @@ int git_reference__update_for_commit( if ((error = git_commit_lookup(&commit, repo, id)) < 0 || (error = git_buf_printf(&reflog_msg, "%s%s: %s", operation ? operation : "commit", - git_commit_parentcount(commit) == 0 ? " (initial)" : "", + commit_type(commit), git_commit_summary(commit))) < 0) goto done; @@ -1229,7 +1337,7 @@ static int peel_error(int error, git_reference *ref, const char* msg) { giterr_set( GITERR_INVALID, - "The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg); + "the reference '%s' cannot be peeled - %s", git_reference_name(ref), msg); return error; } diff --git a/src/refs.h b/src/refs.h index fda9532de..0c90db3af 100644 --- a/src/refs.h +++ b/src/refs.h @@ -15,6 +15,8 @@ #include "buffer.h" #include "oid.h" +extern bool git_reference__enable_symbolic_ref_target_validation; + #define GIT_REFS_DIR "refs/" #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" @@ -53,6 +55,7 @@ #define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE #define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16) +#define GIT_REF_FORMAT__VALIDATION_DISABLE (1u << 15) #define GIT_REFNAME_MAX 1024 @@ -104,6 +107,20 @@ int git_reference_lookup_resolved( const char *name, int max_deref); +/** + * Read reference from a file. + * + * This function will read in the file at `path`. If it is a + * symref, it will return a new unresolved symbolic reference + * with the given name pointing to the reference pointed to by + * the file. If it is not a symbolic reference, it will return + * the resolved reference. + */ +int git_reference__read_head( + git_reference **out, + git_repository *repo, + const char *path); + int git_reference__log_signature(git_signature **out, git_repository *repo); /** Update a reference after a commit. */ diff --git a/src/remote.c b/src/remote.c index c1d7d59ea..bd8b3cfbc 100644 --- a/src/remote.c +++ b/src/remote.c @@ -192,7 +192,7 @@ static int canonicalize_url(git_buf *out, const char *in) static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) { git_remote *remote; - git_config *config = NULL; + git_config *config_ro = NULL, *config_rw; git_buf canonical_url = GIT_BUF_INIT; git_buf var = GIT_BUF_INIT; int error = -1; @@ -200,7 +200,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n /* name is optional */ assert(out && repo && url); - if ((error = git_repository_config__weakptr(&config, repo)) < 0) + if ((error = git_repository_config_snapshot(&config_ro, repo)) < 0) return error; remote = git__calloc(1, sizeof(git_remote)); @@ -212,7 +212,8 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n (error = canonicalize_url(&canonical_url, url)) < 0) goto on_error; - remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH); + remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH); + GITERR_CHECK_ALLOC(remote->url); if (name != NULL) { remote->name = git__strdup(name); @@ -221,7 +222,8 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0) goto on_error; - if ((error = git_config_set_string(config, var.ptr, canonical_url.ptr)) < 0) + if ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 || + (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0) goto on_error; } @@ -233,10 +235,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0) goto on_error; - if ((error = git_repository_config_snapshot(&config, repo)) < 0) - goto on_error; - - if ((error = lookup_remote_prune_config(remote, config, name)) < 0) + if ((error = lookup_remote_prune_config(remote, config_ro, name)) < 0) goto on_error; /* Move the data over to where the matching functions can find them */ @@ -260,7 +259,7 @@ on_error: if (error) git_remote_free(remote); - git_config_free(config); + git_config_free(config_ro); git_buf_free(&canonical_url); git_buf_free(&var); return error; @@ -283,7 +282,7 @@ static int ensure_remote_doesnot_exist(git_repository *repo, const char *name) giterr_set( GITERR_CONFIG, - "Remote '%s' already exists.", name); + "remote '%s' already exists", name); return GIT_EEXISTS; } @@ -476,7 +475,7 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) if (!optional_setting_found) { error = GIT_ENOTFOUND; - giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name); + giterr_set(GITERR_CONFIG, "remote '%s' does not exist", name); goto cleanup; } @@ -770,8 +769,12 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur goto found; } - /* HTTP_PROXY / HTTPS_PROXY environment variables */ - error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); + /* http_proxy / https_proxy environment variables */ + error = git__getenv(&val, use_ssl ? "https_proxy" : "http_proxy"); + + /* try uppercase environment variables */ + if (error == GIT_ENOTFOUND) + error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); if (error < 0) { if (error == GIT_ENOTFOUND) { @@ -1718,7 +1721,7 @@ int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_ error = 0; break; default: - giterr_set(GITERR_INVALID, "Invalid value for the tagopt setting"); + giterr_set(GITERR_INVALID, "invalid value for the tagopt setting"); error = -1; } @@ -2408,7 +2411,7 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ proxy = &opts->proxy_opts; } - assert(remote && refspecs); + assert(remote); if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0) return error; diff --git a/src/repository.c b/src/repository.c index 5c4442360..7ecb00ed8 100644 --- a/src/repository.c +++ b/src/repository.c @@ -27,13 +27,43 @@ #include "merge.h" #include "diff_driver.h" #include "annotated_commit.h" +#include "submodule.h" +#include "worktree.h" + +#include "strmap.h" #ifdef GIT_WIN32 # include "win32/w32_util.h" #endif +bool git_repository__fsync_gitdir = false; + +static const struct { + git_repository_item_t parent; + const char *name; + bool directory; +} items[] = { + { GIT_REPOSITORY_ITEM_GITDIR, NULL, true }, + { GIT_REPOSITORY_ITEM_WORKDIR, NULL, true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, NULL, true }, + { GIT_REPOSITORY_ITEM_GITDIR, "index", false }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "objects", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "refs", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "packed-refs", false }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "remotes", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "config", false }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "info", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "hooks", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "logs", true }, + { GIT_REPOSITORY_ITEM_GITDIR, "modules", true }, + { GIT_REPOSITORY_ITEM_COMMONDIR, "worktrees", true } +}; + static int check_repositoryformatversion(git_config *config); +#define GIT_COMMONDIR_FILE "commondir" +#define GIT_GITDIR_FILE "gitdir" + #define GIT_FILE_CONTENT_PREFIX "gitdir:" #define GIT_BRANCH_MASTER "master" @@ -109,6 +139,7 @@ void git_repository__cleanup(git_repository *repo) { assert(repo); + git_repository_submodule_cache_clear(repo); git_cache_clear(&repo->objects); git_attr_cache_flush(repo); @@ -136,8 +167,9 @@ void git_repository_free(git_repository *repo) git_buf_free(git_array_get(repo->reserved_names, i)); git_array_clear(repo->reserved_names); - git__free(repo->path_gitlink); - git__free(repo->path_repository); + git__free(repo->gitlink); + git__free(repo->gitdir); + git__free(repo->commondir); git__free(repo->workdir); git__free(repo->namespace); git__free(repo->ident_name); @@ -152,17 +184,41 @@ void git_repository_free(git_repository *repo) * * Open a repository object from its path */ -static bool valid_repository_path(git_buf *repository_path) +static bool valid_repository_path(git_buf *repository_path, git_buf *common_path) { - /* Check OBJECTS_DIR first, since it will generate the longest path name */ - if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false) - return false; + /* Check if we have a separate commondir (e.g. we have a + * worktree) */ + if (git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) { + git_buf common_link = GIT_BUF_INIT; + git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE); + + git_futils_readbuffer(&common_link, common_link.ptr); + git_buf_rtrim(&common_link); + + if (git_path_is_relative(common_link.ptr)) { + git_buf_joinpath(common_path, repository_path->ptr, common_link.ptr); + } else { + git_buf_swap(common_path, &common_link); + } + + git_buf_free(&common_link); + } + else { + git_buf_set(common_path, repository_path->ptr, repository_path->size); + } + + /* Make sure the commondir path always has a trailing * slash */ + if (git_buf_rfind(common_path, '/') != (ssize_t)common_path->size - 1) + git_buf_putc(common_path, '/'); /* Ensure HEAD file exists */ if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false) return false; - if (git_path_contains_dir(repository_path, GIT_REFS_DIR) == false) + /* Check files in common dir */ + if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false) + return false; + if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false) return false; return true; @@ -201,6 +257,7 @@ int git_repository_new(git_repository **out) GITERR_CHECK_ALLOC(repo); repo->is_bare = 1; + repo->is_worktree = 0; return 0; } @@ -220,9 +277,10 @@ static int load_config_data(git_repository *repo, const git_config *config) static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path) { - int error; + int error; git_config_entry *ce; - git_buf worktree = GIT_BUF_INIT; + git_buf worktree = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT; if (repo->is_bare) return 0; @@ -231,9 +289,26 @@ static int load_workdir(git_repository *repo, git_config *config, git_buf *paren &ce, config, "core.worktree", false)) < 0) return error; - if (ce && ce->value) { + if (repo->is_worktree) { + char *gitlink = git_worktree__read_link(repo->gitdir, GIT_GITDIR_FILE); + if (!gitlink) { + error = -1; + goto cleanup; + } + + git_buf_attach(&worktree, gitlink, 0); + + if ((git_path_dirname_r(&worktree, worktree.ptr)) < 0 || + git_path_to_dir(&worktree) < 0) { + error = -1; + goto cleanup; + } + + repo->workdir = git_buf_detach(&worktree); + } + else if (ce && ce->value) { if ((error = git_path_prettify_dir( - &worktree, ce->value, repo->path_repository)) < 0) + &worktree, ce->value, repo->gitdir)) < 0) goto cleanup; repo->workdir = git_buf_detach(&worktree); @@ -241,7 +316,7 @@ static int load_workdir(git_repository *repo, git_config *config, git_buf *paren else if (parent_path && git_path_isdir(parent_path->ptr)) repo->workdir = git_buf_detach(parent_path); else { - if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || + if (git_path_dirname_r(&worktree, repo->gitdir) < 0 || git_path_to_dir(&worktree) < 0) { error = -1; goto cleanup; @@ -252,6 +327,7 @@ static int load_workdir(git_repository *repo, git_config *config, git_buf *paren GITERR_CHECK_ALLOC(repo->workdir); cleanup: + git_buf_free(&path); git_config_entry_free(ce); return error; } @@ -332,7 +408,7 @@ static int read_gitfile(git_buf *path_out, const char *file_path) memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { giterr_set(GITERR_REPOSITORY, - "The `.git` file at '%s' is malformed", file_path); + "the `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { @@ -348,9 +424,10 @@ static int read_gitfile(git_buf *path_out, const char *file_path) } static int find_repo( - git_buf *repo_path, - git_buf *parent_path, - git_buf *link_path, + git_buf *gitdir_path, + git_buf *workdir_path, + git_buf *gitlink_path, + git_buf *commondir_path, const char *start_path, uint32_t flags, const char *ceiling_dirs) @@ -358,13 +435,14 @@ static int find_repo( int error; git_buf path = GIT_BUF_INIT; git_buf repo_link = GIT_BUF_INIT; + git_buf common_link = GIT_BUF_INIT; struct stat st; dev_t initial_device = 0; int min_iterations; bool in_dot_git; size_t ceiling_offset = 0; - git_buf_free(repo_path); + git_buf_clear(gitdir_path); error = git_path_prettify(&path, start_path, NULL); if (error < 0) @@ -404,9 +482,16 @@ static int find_repo( break; if (S_ISDIR(st.st_mode)) { - if (valid_repository_path(&path)) { + if (valid_repository_path(&path, &common_link)) { git_path_to_dir(&path); - git_buf_set(repo_path, path.ptr, path.size); + git_buf_set(gitdir_path, path.ptr, path.size); + + if (gitlink_path) + git_buf_attach(gitlink_path, + git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0); + if (commondir_path) + git_buf_swap(&common_link, commondir_path); + break; } } @@ -414,11 +499,13 @@ static int find_repo( error = read_gitfile(&repo_link, path.ptr); if (error < 0) break; - if (valid_repository_path(&repo_link)) { - git_buf_swap(repo_path, &repo_link); + if (valid_repository_path(&repo_link, &common_link)) { + git_buf_swap(gitdir_path, &repo_link); - if (link_path) - error = git_buf_put(link_path, path.ptr, path.size); + if (gitlink_path) + error = git_buf_put(gitlink_path, path.ptr, path.size); + if (commondir_path) + git_buf_swap(&common_link, commondir_path); } break; } @@ -444,27 +531,28 @@ static int find_repo( break; } - if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { - if (!git_buf_len(repo_path)) - git_buf_clear(parent_path); + if (!error && workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { + if (!git_buf_len(gitdir_path)) + git_buf_clear(workdir_path); else { - git_path_dirname_r(parent_path, path.ptr); - git_path_to_dir(parent_path); + git_path_dirname_r(workdir_path, path.ptr); + git_path_to_dir(workdir_path); } - if (git_buf_oom(parent_path)) + if (git_buf_oom(workdir_path)) return -1; } /* If we didn't find the repository, and we don't have any other error * to report, report that. */ - if (!git_buf_len(repo_path) && !error) { + if (!git_buf_len(gitdir_path) && !error) { giterr_set(GITERR_REPOSITORY, - "Could not find repository from '%s'", start_path); + "could not find repository from '%s'", start_path); error = GIT_ENOTFOUND; } git_buf_free(&path); git_buf_free(&repo_link); + git_buf_free(&common_link); return error; } @@ -473,26 +561,30 @@ int git_repository_open_bare( const char *bare_path) { int error; - git_buf path = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT, common_path = GIT_BUF_INIT; git_repository *repo = NULL; if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0) return error; - if (!valid_repository_path(&path)) { + if (!valid_repository_path(&path, &common_path)) { git_buf_free(&path); - giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path); + git_buf_free(&common_path); + giterr_set(GITERR_REPOSITORY, "path is not a repository: %s", bare_path); return GIT_ENOTFOUND; } repo = repository_alloc(); GITERR_CHECK_ALLOC(repo); - repo->path_repository = git_buf_detach(&path); - GITERR_CHECK_ALLOC(repo->path_repository); + repo->gitdir = git_buf_detach(&path); + GITERR_CHECK_ALLOC(repo->gitdir); + repo->commondir = git_buf_detach(&common_path); + GITERR_CHECK_ALLOC(repo->commondir); /* of course we're bare! */ repo->is_bare = 1; + repo->is_worktree = 0; repo->workdir = NULL; *repo_ptr = repo; @@ -668,6 +760,29 @@ success: return error; } +static int repo_is_worktree(unsigned *out, const git_repository *repo) +{ + git_buf gitdir_link = GIT_BUF_INIT; + int error; + + /* Worktrees cannot have the same commondir and gitdir */ + if (repo->commondir && repo->gitdir + && !strcmp(repo->commondir, repo->gitdir)) { + *out = 0; + return 0; + } + + if ((error = git_buf_joinpath(&gitdir_link, repo->gitdir, "gitdir")) < 0) + return -1; + + /* A 'gitdir' file inside a git directory is currently + * only used when the repository is a working tree. */ + *out = !!git_path_exists(gitdir_link.ptr); + + git_buf_free(&gitdir_link); + return error; +} + int git_repository_open_ext( git_repository **repo_ptr, const char *start_path, @@ -675,8 +790,9 @@ int git_repository_open_ext( const char *ceiling_dirs) { int error; - git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT, - link_path = GIT_BUF_INIT; + unsigned is_worktree; + git_buf gitdir = GIT_BUF_INIT, workdir = GIT_BUF_INIT, + gitlink = GIT_BUF_INIT, commondir = GIT_BUF_INIT; git_repository *repo; git_config *config = NULL; @@ -687,7 +803,7 @@ int git_repository_open_ext( *repo_ptr = NULL; error = find_repo( - &path, &parent, &link_path, start_path, flags, ceiling_dirs); + &gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs); if (error < 0 || !repo_ptr) return error; @@ -695,13 +811,21 @@ int git_repository_open_ext( repo = repository_alloc(); GITERR_CHECK_ALLOC(repo); - repo->path_repository = git_buf_detach(&path); - GITERR_CHECK_ALLOC(repo->path_repository); + repo->gitdir = git_buf_detach(&gitdir); + GITERR_CHECK_ALLOC(repo->gitdir); - if (link_path.size) { - repo->path_gitlink = git_buf_detach(&link_path); - GITERR_CHECK_ALLOC(repo->path_gitlink); + if (gitlink.size) { + repo->gitlink = git_buf_detach(&gitlink); + GITERR_CHECK_ALLOC(repo->gitlink); } + if (commondir.size) { + repo->commondir = git_buf_detach(&commondir); + GITERR_CHECK_ALLOC(repo->commondir); + } + + if ((error = repo_is_worktree(&is_worktree, repo)) < 0) + goto cleanup; + repo->is_worktree = is_worktree; /* * We'd like to have the config, but git doesn't particularly @@ -721,12 +845,13 @@ int git_repository_open_ext( if (config && ((error = load_config_data(repo, config)) < 0 || - (error = load_workdir(repo, config, &parent)) < 0)) + (error = load_workdir(repo, config, &workdir)) < 0)) goto cleanup; } cleanup: - git_buf_free(&parent); + git_buf_free(&gitdir); + git_buf_free(&workdir); git_config_free(config); if (error < 0) @@ -743,6 +868,36 @@ int git_repository_open(git_repository **repo_out, const char *path) repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL); } +int git_repository_open_from_worktree(git_repository **repo_out, git_worktree *wt) +{ + git_buf path = GIT_BUF_INIT; + git_repository *repo = NULL; + int len, err; + + assert(repo_out && wt); + + *repo_out = NULL; + len = strlen(wt->gitlink_path); + + if (len <= 4 || strcasecmp(wt->gitlink_path + len - 4, ".git")) { + err = -1; + goto out; + } + + if ((err = git_buf_set(&path, wt->gitlink_path, len - 4)) < 0) + goto out; + + if ((err = git_repository_open(&repo, path.ptr)) < 0) + goto out; + + *repo_out = repo; + +out: + git_buf_free(&path); + + return err; +} + int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb) { git_repository *repo; @@ -768,7 +923,7 @@ int git_repository_discover( git_buf_sanitize(out); - return find_repo(out, NULL, NULL, start_path, flags, ceiling_dirs); + return find_repo(out, NULL, NULL, NULL, start_path, flags, ceiling_dirs); } static int load_config( @@ -788,14 +943,10 @@ static int load_config( if ((error = git_config_new(&cfg)) < 0) return error; - error = git_buf_joinpath( - &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO); - if (error < 0) - goto on_error; + if ((error = git_repository_item_path(&config_path, repo, GIT_REPOSITORY_ITEM_CONFIG)) == 0) + error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0); - if ((error = git_config_add_file_ondisk( - cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0 && - error != GIT_ENOTFOUND) + if (error && error != GIT_ENOTFOUND) goto on_error; git_buf_free(&config_path); @@ -923,18 +1074,23 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo) git_buf odb_path = GIT_BUF_INIT; git_odb *odb; - if ((error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR)) < 0) + if ((error = git_repository_item_path(&odb_path, repo, + GIT_REPOSITORY_ITEM_OBJECTS)) < 0 || + (error = git_odb_new(&odb)) < 0) return error; - error = git_odb_open(&odb, odb_path.ptr); - if (!error) { - GIT_REFCOUNT_OWN(odb, repo); + GIT_REFCOUNT_OWN(odb, repo); - odb = git__compare_and_swap(&repo->_odb, NULL, odb); - if (odb != NULL) { - GIT_REFCOUNT_OWN(odb, NULL); - git_odb_free(odb); - } + if ((error = git_odb__set_caps(odb, GIT_ODB_CAP_FROM_OWNER)) < 0 || + (error = git_odb__add_default_backends(odb, odb_path.ptr, 0, 0)) < 0) { + git_odb_free(odb); + return error; + } + + odb = git__compare_and_swap(&repo->_odb, NULL, odb); + if (odb != NULL) { + GIT_REFCOUNT_OWN(odb, NULL); + git_odb_free(odb); } git_buf_free(&odb_path); @@ -1009,7 +1165,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo) git_buf index_path = GIT_BUF_INIT; git_index *index; - if ((error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE)) < 0) + if ((error = git_buf_joinpath(&index_path, repo->gitdir, GIT_INDEX_FILE)) < 0) return error; error = git_index_open(&index, index_path.ptr); @@ -1125,13 +1281,13 @@ bool git_repository__reserved_names( prefixcmp = (error || ignorecase) ? git__prefixcmp_icase : git__prefixcmp; - if (repo->path_gitlink && - reserved_names_add8dot3(repo, repo->path_gitlink) < 0) + if (repo->gitlink && + reserved_names_add8dot3(repo, repo->gitlink) < 0) goto on_error; - if (repo->path_repository && - prefixcmp(repo->path_repository, repo->workdir) == 0 && - reserved_names_add8dot3(repo, repo->path_repository) < 0) + if (repo->gitdir && + prefixcmp(repo->gitdir, repo->workdir) == 0 && + reserved_names_add8dot3(repo, repo->gitdir) < 0) goto on_error; } } @@ -1180,7 +1336,7 @@ static int check_repositoryformatversion(git_config *config) if (GIT_REPO_VERSION < version) { giterr_set(GITERR_REPOSITORY, - "Unsupported repository version %d. Only versions up to %d are supported.", + "unsupported repository version %d. Only versions up to %d are supported.", version, GIT_REPO_VERSION); return -1; } @@ -1188,7 +1344,7 @@ static int check_repositoryformatversion(git_config *config) return 0; } -static int repo_init_create_head(const char *git_dir, const char *ref_name) +int git_repository_create_head(const char *git_dir, const char *ref_name) { git_buf ref_path = GIT_BUF_INIT; git_filebuf ref = GIT_FILEBUF_INIT; @@ -1274,12 +1430,12 @@ static int create_empty_file(const char *path, mode_t mode) int fd; if ((fd = p_creat(path, mode)) < 0) { - giterr_set(GITERR_OS, "Error while creating '%s'", path); + giterr_set(GITERR_OS, "error while creating '%s'", path); return -1; } if (p_close(fd) < 0) { - giterr_set(GITERR_OS, "Error while closing '%s'", path); + giterr_set(GITERR_OS, "error while closing '%s'", path); return -1; } @@ -1508,7 +1664,7 @@ static int repo_write_template( if (error) giterr_set(GITERR_OS, - "Failed to initialize repository with template '%s'", file); + "failed to initialize repository with template '%s'", file); return error; } @@ -1539,7 +1695,7 @@ static int repo_write_gitlink( if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) { giterr_set(GITERR_REPOSITORY, - "Cannot overwrite gitlink file into path '%s'", in_dir); + "cannot overwrite gitlink file into path '%s'", in_dir); error = GIT_EEXISTS; goto cleanup; } @@ -1593,7 +1749,7 @@ static int repo_init_structure( if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) { if (git_win32__set_hidden(repo_dir, true) < 0) { giterr_set(GITERR_OS, - "Failed to mark Git repository folder as hidden"); + "failed to mark Git repository folder as hidden"); return -1; } } @@ -1747,7 +1903,7 @@ static int repo_init_directories( if (git_path_dirname_r(wd_path, repo_path->ptr) < 0) return -1; } else { - giterr_set(GITERR_REPOSITORY, "Cannot pick working directory" + giterr_set(GITERR_REPOSITORY, "cannot pick working directory" " for non-bare repository that isn't a '.git' directory"); return -1; } @@ -1851,7 +2007,8 @@ int git_repository_init_ext( git_repository_init_options *opts) { int error; - git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT; + git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT, + common_path = GIT_BUF_INIT; const char *wd; assert(out && given_repo && opts); @@ -1863,11 +2020,11 @@ int git_repository_init_ext( goto cleanup; wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path); - if (valid_repository_path(&repo_path)) { + if (valid_repository_path(&repo_path, &common_path)) { if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) { giterr_set(GITERR_REPOSITORY, - "Attempt to reinitialize '%s'", given_repo); + "attempt to reinitialize '%s'", given_repo); error = GIT_EEXISTS; goto cleanup; } @@ -1884,7 +2041,7 @@ int git_repository_init_ext( repo_path.ptr, wd, opts)) && !(error = repo_init_config( repo_path.ptr, wd, opts->flags, opts->mode))) - error = repo_init_create_head( + error = git_repository_create_head( repo_path.ptr, opts->initial_head); } if (error < 0) @@ -1896,6 +2053,7 @@ int git_repository_init_ext( error = repo_init_create_origin(*out, opts->origin_url); cleanup: + git_buf_free(&common_path); git_buf_free(&repo_path); git_buf_free(&wd_path); @@ -1925,6 +2083,29 @@ int git_repository_head_detached(git_repository *repo) return exists; } +static int get_worktree_file_path(git_buf *out, git_repository *repo, const char *worktree, const char *file) +{ + git_buf_clear(out); + return git_buf_printf(out, "%s/worktrees/%s/%s", repo->commondir, worktree, file); +} + +int git_repository_head_detached_for_worktree(git_repository *repo, const char *name) +{ + git_reference *ref = NULL; + int error; + + assert(repo && name); + + if ((error = git_repository_head_for_worktree(&ref, repo, name)) < 0) + goto out; + + error = (git_reference_type(ref) != GIT_REF_SYMBOLIC); +out: + git_reference_free(ref); + + return error; +} + int git_repository_head(git_reference **head_out, git_repository *repo) { git_reference *head; @@ -1944,6 +2125,71 @@ int git_repository_head(git_reference **head_out, git_repository *repo) return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error; } +int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name) +{ + git_buf path = GIT_BUF_INIT; + git_reference *head = NULL; + int error; + + assert(out && repo && name); + + *out = NULL; + + if ((error = get_worktree_file_path(&path, repo, name, GIT_HEAD_FILE)) < 0 || + (error = git_reference__read_head(&head, repo, path.ptr)) < 0) + goto out; + + if (git_reference_type(head) != GIT_REF_OID) { + git_reference *resolved; + + error = git_reference_lookup_resolved(&resolved, repo, git_reference_symbolic_target(head), -1); + git_reference_free(head); + head = resolved; + } + + *out = head; + +out: + if (error) + git_reference_free(head); + + git_buf_free(&path); + + return error; +} + +int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload) +{ + git_strarray worktrees = GIT_VECTOR_INIT; + git_buf path = GIT_BUF_INIT; + int error; + size_t i; + + /* Execute callback for HEAD of commondir */ + if ((error = git_buf_joinpath(&path, repo->commondir, GIT_HEAD_FILE)) < 0 || + (error = cb(repo, path.ptr, payload) != 0)) + goto out; + + if ((error = git_worktree_list(&worktrees, repo)) < 0) { + error = 0; + goto out; + } + + /* Execute callback for all worktree HEADs */ + for (i = 0; i < worktrees.count; i++) { + if (get_worktree_file_path(&path, repo, worktrees.strings[i], GIT_HEAD_FILE) < 0) + continue; + + if ((error = cb(repo, path.ptr, payload)) != 0) + goto out; + } + +out: + git_buf_free(&path); + git_strarray_free(&worktrees); + return error; +} + int git_repository_head_unborn(git_repository *repo) { git_reference *ref = NULL; @@ -2002,10 +2248,50 @@ int git_repository_is_empty(git_repository *repo) return is_empty; } +int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item) +{ + const char *parent; + + switch (items[item].parent) { + case GIT_REPOSITORY_ITEM_GITDIR: + parent = git_repository_path(repo); + break; + case GIT_REPOSITORY_ITEM_WORKDIR: + parent = git_repository_workdir(repo); + break; + case GIT_REPOSITORY_ITEM_COMMONDIR: + parent = git_repository_commondir(repo); + break; + default: + giterr_set(GITERR_INVALID, "invalid item directory"); + return -1; + } + + if (parent == NULL) { + giterr_set(GITERR_INVALID, "path cannot exist in repository"); + return GIT_ENOTFOUND; + } + + if (git_buf_sets(out, parent) < 0) + return -1; + + if (items[item].name) { + if (git_buf_joinpath(out, parent, items[item].name) < 0) + return -1; + } + + if (items[item].directory) { + if (git_path_to_dir(out) < 0) + return -1; + } + + return 0; +} + const char *git_repository_path(git_repository *repo) { assert(repo); - return repo->path_repository; + return repo->gitdir; } const char *git_repository_workdir(git_repository *repo) @@ -2018,6 +2304,12 @@ const char *git_repository_workdir(git_repository *repo) return repo->workdir; } +const char *git_repository_commondir(git_repository *repo) +{ + assert(repo); + return repo->commondir; +} + int git_repository_set_workdir( git_repository *repo, const char *workdir, int update_gitlink) { @@ -2068,6 +2360,12 @@ int git_repository_is_bare(git_repository *repo) return repo->is_bare; } +int git_repository_is_worktree(git_repository *repo) +{ + assert(repo); + return repo->is_worktree; +} + int git_repository_set_bare(git_repository *repo) { int error; @@ -2122,7 +2420,7 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head git_oid_fmt(orig_head_str, orig_head); - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 && + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_ORIG_HEAD_FILE)) == 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 && (error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_HEXSZ, orig_head_str)) == 0) error = git_filebuf_commit(&file); @@ -2143,13 +2441,13 @@ int git_repository_message(git_buf *out, git_repository *repo) git_buf_sanitize(out); - if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) + if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0) return -1; if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) { if (errno == ENOENT) error = GIT_ENOTFOUND; - giterr_set(GITERR_OS, "Could not access message file"); + giterr_set(GITERR_OS, "could not access message file"); } else { error = git_futils_readbuffer(out, git_buf_cstr(&path)); } @@ -2164,7 +2462,7 @@ int git_repository_message_remove(git_repository *repo) git_buf path = GIT_BUF_INIT; int error; - if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) + if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0) return -1; error = p_unlink(git_buf_cstr(&path)); @@ -2227,7 +2525,7 @@ int git_repository_hashfile( } if (!git__is_sizet(len)) { - giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); + giterr_set(GITERR_OS, "file size overflow for 32-bit systems"); error = -1; goto cleanup; } @@ -2254,7 +2552,9 @@ static int checkout_message(git_buf *out, git_reference *old, const char *new) git_buf_puts(out, " to "); - if (git_reference__is_branch(new)) + if (git_reference__is_branch(new) || + git_reference__is_tag(new) || + git_reference__is_remote(new)) git_buf_puts(out, git_reference__shorthand(new)); else git_buf_puts(out, new); @@ -2265,6 +2565,41 @@ static int checkout_message(git_buf *out, git_reference *old, const char *new) return 0; } +static int detach(git_repository *repo, const git_oid *id, const char *new) +{ + int error; + git_buf log_message = GIT_BUF_INIT; + git_object *object = NULL, *peeled = NULL; + git_reference *new_head = NULL, *current = NULL; + + assert(repo && id); + + if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0) + return error; + + if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0) + goto cleanup; + + if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0) + goto cleanup; + + if (new == NULL) + new = git_oid_tostr_s(git_object_id(peeled)); + + if ((error = checkout_message(&log_message, current, new)) < 0) + goto cleanup; + + error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message)); + +cleanup: + git_buf_free(&log_message); + git_object_free(object); + git_object_free(peeled); + git_reference_free(current); + git_reference_free(new_head); + return error; +} + int git_repository_set_head( git_repository* repo, const char* refname) @@ -2285,12 +2620,21 @@ int git_repository_set_head( if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; + if (ref && current->type == GIT_REF_SYMBOLIC && git__strcmp(current->target.symbolic, ref->name) && + git_reference_is_branch(ref) && git_branch_is_checked_out(ref)) { + giterr_set(GITERR_REPOSITORY, "cannot set HEAD to reference '%s' as it is the current HEAD " + "of a linked repository.", git_reference_name(ref)); + error = -1; + goto cleanup; + } + if (!error) { if (git_reference_is_branch(ref)) { error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), true, git_buf_cstr(&log_message)); } else { - error = git_repository_set_head_detached(repo, git_reference_target(ref)); + error = detach(repo, git_reference_target(ref), + git_reference_is_tag(ref) || git_reference_is_remote(ref) ? refname : NULL); } } else if (git_reference__is_branch(refname)) { error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, @@ -2305,41 +2649,6 @@ cleanup: return error; } -static int detach(git_repository *repo, const git_oid *id, const char *from) -{ - int error; - git_buf log_message = GIT_BUF_INIT; - git_object *object = NULL, *peeled = NULL; - git_reference *new_head = NULL, *current = NULL; - - assert(repo && id); - - if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0) - return error; - - if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0) - goto cleanup; - - if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0) - goto cleanup; - - if (from == NULL) - from = git_oid_tostr_s(git_object_id(peeled)); - - if ((error = checkout_message(&log_message, current, from)) < 0) - goto cleanup; - - error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message)); - -cleanup: - git_buf_free(&log_message); - git_object_free(object); - git_object_free(peeled); - git_reference_free(current); - git_reference_free(new_head); - return error; -} - int git_repository_set_head_detached( git_repository* repo, const git_oid* commitish) @@ -2400,7 +2709,7 @@ int git_repository_state(git_repository *repo) assert(repo); - if (git_buf_puts(&repo_path, repo->path_repository) < 0) + if (git_buf_puts(&repo_path, repo->gitdir) < 0) return -1; if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE)) @@ -2442,7 +2751,7 @@ int git_repository__cleanup_files( for (error = 0, i = 0; !error && i < files_len; ++i) { const char *path; - if (git_buf_joinpath(&buf, repo->path_repository, files[i]) < 0) + if (git_buf_joinpath(&buf, repo->gitdir, files[i]) < 0) return -1; path = git_buf_cstr(&buf); @@ -2486,7 +2795,7 @@ int git_repository_is_shallow(git_repository *repo) struct stat st; int error; - if ((error = git_buf_joinpath(&path, repo->path_repository, "shallow")) < 0) + if ((error = git_buf_joinpath(&path, repo->gitdir, "shallow")) < 0) return error; error = git_path_lstat(path.ptr, &st); @@ -2541,3 +2850,31 @@ int git_repository_set_ident(git_repository *repo, const char *name, const char return 0; } + +int git_repository_submodule_cache_all(git_repository *repo) +{ + int error; + + assert(repo); + + if ((error = git_strmap_alloc(&repo->submodule_cache))) + return error; + + error = git_submodule__map(repo, repo->submodule_cache); + return error; +} + +int git_repository_submodule_cache_clear(git_repository *repo) +{ + git_submodule *sm; + assert(repo); + if (repo->submodule_cache == NULL) { + return 0; + } + git_strmap_foreach_value(repo->submodule_cache, sm, { + git_submodule_free(sm); + }); + git_strmap_free(repo->submodule_cache); + repo->submodule_cache = 0; + return 0; +} diff --git a/src/repository.h b/src/repository.h index fd679b483..52f9ec260 100644 --- a/src/repository.h +++ b/src/repository.h @@ -31,6 +31,8 @@ /* Default DOS-compatible 8.3 "short name" for a git repository, "GIT~1" */ #define GIT_DIR_SHORTNAME "GIT~1" +extern bool git_repository__fsync_gitdir; + /** Cvar cache identifiers */ typedef enum { GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */ @@ -46,6 +48,7 @@ typedef enum { GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */ GIT_CVAR_PROTECTHFS, /* core.protectHFS */ GIT_CVAR_PROTECTNTFS, /* core.protectNTFS */ + GIT_CVAR_FSYNCOBJECTFILES, /* core.fsyncObjectFiles */ GIT_CVAR_CACHE_MAX } git_cvar_cached; @@ -106,6 +109,8 @@ typedef enum { GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE, /* core.protectNTFS */ GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE, + /* core.fsyncObjectFiles */ + GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CVAR_FALSE, } git_cvar_value; /* internal repository init flags */ @@ -126,8 +131,9 @@ struct git_repository { git_attr_cache *attrcache; git_diff_driver_registry *diff_drivers; - char *path_repository; - char *path_gitlink; + char *gitlink; + char *gitdir; + char *commondir; char *workdir; char *namespace; @@ -137,12 +143,14 @@ struct git_repository { git_array_t(git_buf) reserved_names; unsigned is_bare:1; + unsigned is_worktree:1; unsigned int lru_counter; git_atomic attr_session_key; git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX]; + git_strmap *submodule_cache; }; GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo) @@ -151,6 +159,27 @@ GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo) } int git_repository_head_tree(git_tree **tree, git_repository *repo); +int git_repository_create_head(const char *git_dir, const char *ref_name); + +/* + * Called for each HEAD. + * + * Can return either 0, causing the iteration over HEADs to + * continue, or a non-0 value causing the iteration to abort. The + * return value is passed back to the caller of + * `git_repository_foreach_head` + */ +typedef int (*git_repository_foreach_head_cb)(git_repository *repo, const char *path, void *payload); + +/* + * Iterate over repository and all worktree HEADs. + * + * This function will be called for the repository HEAD and for + * all HEADS of linked worktrees. For each HEAD, the callback is + * executed with the given payload. The return value equals the + * return value of the last executed callback function. + */ +int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload); /* * Weak pointers to repository internals. @@ -182,7 +211,7 @@ GIT_INLINE(int) git_repository__ensure_not_bare( giterr_set( GITERR_REPOSITORY, - "Cannot %s. This operation is not allowed against bare repositories.", + "cannot %s. This operation is not allowed against bare repositories.", operation_name); return GIT_EBAREREPO; diff --git a/src/reset.c b/src/reset.c index db0bfb373..066b5dbda 100644 --- a/src/reset.c +++ b/src/reset.c @@ -137,7 +137,7 @@ static int reset( (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE || git_index_has_conflicts(index))) { - giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge.", ERROR_MSG); + giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge", ERROR_MSG); error = GIT_EUNMERGED; goto cleanup; } diff --git a/src/revert.c b/src/revert.c index c481e7dea..747938fb3 100644 --- a/src/revert.c +++ b/src/revert.c @@ -27,7 +27,7 @@ static int write_revert_head( git_buf file_path = GIT_BUF_INIT; int error = 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_REVERT_HEAD_FILE)) >= 0 && + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_REVERT_HEAD_FILE)) >= 0 && (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) >= 0 && (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) error = git_filebuf_commit(&file); @@ -49,7 +49,7 @@ static int write_merge_msg( git_buf file_path = GIT_BUF_INIT; int error = 0; - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) < 0 || (error = git_filebuf_printf(&file, "Revert \"%s\"\n\nThis reverts commit %s.\n", commit_msgline, commit_oidstr)) < 0) @@ -133,13 +133,13 @@ int git_revert_commit( if (git_commit_parentcount(revert_commit) > 1) { if (!mainline) return revert_seterr(revert_commit, - "Mainline branch is not specified but %s is a merge commit"); + "mainline branch is not specified but %s is a merge commit"); parent = mainline; } else { if (mainline) return revert_seterr(revert_commit, - "Mainline branch specified but %s is not a merge commit"); + "mainline branch specified but %s is not a merge commit"); parent = git_commit_parentcount(revert_commit); } diff --git a/src/revparse.c b/src/revparse.c index aa7e0bd98..fd6bd1ea6 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -46,7 +46,7 @@ static int build_regex(regex_t *regex, const char *pattern) int error; if (*pattern == '\0') { - giterr_set(GITERR_REGEX, "Empty pattern"); + giterr_set(GITERR_REGEX, "empty pattern"); return GIT_EINVALIDSPEC; } @@ -118,7 +118,7 @@ static int revparse_lookup_object( if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND) return error; - giterr_set(GITERR_REFERENCE, "Revspec '%s' not found.", spec); + giterr_set(GITERR_REFERENCE, "revspec '%s' not found", spec); return GIT_ENOTFOUND; } @@ -245,7 +245,7 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide notfound: giterr_set( GITERR_REFERENCE, - "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, + "reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, git_reference_name(ref), numentries, identifier); git_reflog_free(reflog); @@ -757,7 +757,7 @@ int revparse__ext( * TODO: support merge-stage path lookup (":2:Makefile") * and plain index blob lookup (:i-am/a/blob) */ - giterr_set(GITERR_INVALID, "Unimplemented"); + giterr_set(GITERR_INVALID, "unimplemented"); error = GIT_ERROR; goto cleanup; } @@ -816,7 +816,7 @@ cleanup: if (error) { if (error == GIT_EINVALIDSPEC) giterr_set(GITERR_INVALID, - "Failed to parse revision specifier - Invalid pattern '%s'", spec); + "failed to parse revision specifier - Invalid pattern '%s'", spec); git_object_free(base_rev); git_reference_free(reference); @@ -892,6 +892,17 @@ int git_revparse( const char *rstr; revspec->flags = GIT_REVPARSE_RANGE; + /* + * Following git.git, don't allow '..' because it makes command line + * arguments which can be either paths or revisions ambiguous when the + * path is almost certainly intended. The empty range '...' is still + * allowed. + */ + if (!git__strcmp(spec, "..")) { + giterr_set(GITERR_INVALID, "Invalid pattern '..'"); + return GIT_EINVALIDSPEC; + } + lstr = git__substrdup(spec, dotdot - spec); rstr = dotdot + 2; if (dotdot[2] == '.') { @@ -899,9 +910,17 @@ int git_revparse( rstr++; } - error = git_revparse_single(&revspec->from, repo, lstr); - if (!error) - error = git_revparse_single(&revspec->to, repo, rstr); + error = git_revparse_single( + &revspec->from, + repo, + *lstr == '\0' ? "HEAD" : lstr); + + if (!error) { + error = git_revparse_single( + &revspec->to, + repo, + *rstr == '\0' ? "HEAD" : rstr); + } git__free((void*)lstr); } else { diff --git a/src/revwalk.c b/src/revwalk.c index f5502a72e..77fa9fd0c 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -15,8 +15,6 @@ #include "merge.h" #include "vector.h" -GIT__USE_OIDMAP - git_commit_list_node *git_revwalk__commit_lookup( git_revwalk *walk, const git_oid *oid) { @@ -25,9 +23,9 @@ git_commit_list_node *git_revwalk__commit_lookup( int ret; /* lookup and reserve space if not already present */ - pos = kh_get(oid, walk->commits, oid); - if (pos != kh_end(walk->commits)) - return kh_value(walk->commits, pos); + pos = git_oidmap_lookup_index(walk->commits, oid); + if (git_oidmap_valid_index(walk->commits, pos)) + return git_oidmap_value_at(walk->commits, pos); commit = git_commit_list_alloc_node(walk); if (commit == NULL) @@ -35,9 +33,9 @@ git_commit_list_node *git_revwalk__commit_lookup( git_oid_cpy(&commit->oid, oid); - pos = kh_put(oid, walk->commits, &commit->oid, &ret); + pos = git_oidmap_put(walk->commits, &commit->oid, &ret); assert(ret != 0); - kh_value(walk->commits, pos) = commit; + git_oidmap_set_value_at(walk->commits, pos, commit); return commit; } @@ -61,7 +59,7 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting, if (from_glob) return 0; - giterr_set(GITERR_INVALID, "Object is not a committish"); + giterr_set(GITERR_INVALID, "object is not a committish"); return -1; } if (error < 0) @@ -198,7 +196,7 @@ int git_revwalk_push_range(git_revwalk *walk, const char *range) if (revspec.flags & GIT_REVPARSE_MERGE_BASE) { /* TODO: support "..." */ - giterr_set(GITERR_INVALID, "Symmetric differences not implemented in revwalk"); + giterr_set(GITERR_INVALID, "symmetric differences not implemented in revwalk"); return GIT_EINVALIDSPEC; } @@ -233,9 +231,12 @@ static int revwalk_next_timesort(git_commit_list_node **object_out, git_revwalk { git_commit_list_node *next; - if ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) { - *object_out = next; - return 0; + while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) { + /* Some commits might become uninteresting after being added to the list */ + if (!next->uninteresting) { + *object_out = next; + return 0; + } } giterr_clear(); @@ -702,7 +703,7 @@ void git_revwalk_reset(git_revwalk *walk) assert(walk); - kh_foreach_value(walk->commits, commit, { + git_oidmap_foreach_value(walk->commits, commit, { commit->seen = 0; commit->in_degree = 0; commit->topo_delay = 0; @@ -733,7 +734,7 @@ int git_revwalk_add_hide_cb( if (walk->hide_cb) { /* There is already a callback added */ - giterr_set(GITERR_INVALID, "There is already a callback added to hide commits in revision walker."); + giterr_set(GITERR_INVALID, "there is already a callback added to hide commits in revwalk"); return -1; } diff --git a/src/settings.c b/src/settings.c index 4a6e0f353..52b861ba0 100644 --- a/src/settings.c +++ b/src/settings.c @@ -15,6 +15,9 @@ #include "cache.h" #include "global.h" #include "object.h" +#include "odb.h" +#include "refs.h" +#include "transports/smart.h" void git_libgit2_version(int *major, int *minor, int *rev) { @@ -29,7 +32,7 @@ int git_libgit2_features(void) #ifdef GIT_THREADS | GIT_FEATURE_THREADS #endif -#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) +#ifdef GIT_HTTPS | GIT_FEATURE_HTTPS #endif #if defined(GIT_SSH) @@ -64,7 +67,7 @@ static int config_level_to_sysdir(int config_level) break; default: giterr_set( - GITERR_INVALID, "Invalid config path selector %d", config_level); + GITERR_INVALID, "invalid config path selector %d", config_level); } return val; @@ -193,6 +196,10 @@ int git_libgit2_opts(int key, ...) git_object__strict_input_validation = (va_arg(ap, int) != 0); break; + case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION: + git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0); + break; + case GIT_OPT_SET_SSL_CIPHERS: #ifdef GIT_OPENSSL { @@ -217,6 +224,30 @@ int git_libgit2_opts(int key, ...) } break; + case GIT_OPT_ENABLE_OFS_DELTA: + git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0); + break; + + case GIT_OPT_ENABLE_FSYNC_GITDIR: + git_repository__fsync_gitdir = (va_arg(ap, int) != 0); + break; + + case GIT_OPT_GET_WINDOWS_SHAREMODE: +#ifdef GIT_WIN32 + *(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode; +#endif + break; + + case GIT_OPT_SET_WINDOWS_SHAREMODE: +#ifdef GIT_WIN32 + git_win32__createfile_sharemode = va_arg(ap, unsigned long); +#endif + break; + + case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION: + git_odb__strict_hash_verification = (va_arg(ap, int) != 0); + break; + default: giterr_set(GITERR_INVALID, "invalid option key"); error = -1; diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c index c6b561340..ead26de06 100644 --- a/src/sha1_lookup.c +++ b/src/sha1_lookup.c @@ -206,7 +206,7 @@ int sha1_entry_pos(const void *table, #endif if (!(lo <= mi && mi < hi)) { - giterr_set(GITERR_INVALID, "Assertion failure. Binary search invariant is false"); + giterr_set(GITERR_INVALID, "assertion failure: binary search invariant is false"); return -1; } diff --git a/src/signature.c b/src/signature.c index 22cba7ef3..a56b8a299 100644 --- a/src/signature.c +++ b/src/signature.c @@ -25,7 +25,7 @@ void git_signature_free(git_signature *sig) static int signature_error(const char *msg) { - giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg); + giterr_set(GITERR_INVALID, "failed to parse signature - %s", msg); return -1; } @@ -228,8 +228,11 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, const char *time_start = email_end + 2; const char *time_end; - if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) + if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) { + git__free(sig->name); + git__free(sig->email); return signature_error("invalid Unix timestamp"); + } /* do we have a timezone? */ if (time_end + 1 < buffer_end) { diff --git a/src/socket_stream.c b/src/socket_stream.c index 71f49118e..c0a168448 100644 --- a/src/socket_stream.c +++ b/src/socket_stream.c @@ -57,7 +57,7 @@ static int close_socket(GIT_SOCKET s) return -1; if (0 != WSACleanup()) { - giterr_set(GITERR_OS, "Winsock cleanup failed"); + giterr_set(GITERR_OS, "winsock cleanup failed"); return -1; } @@ -82,13 +82,13 @@ int socket_connect(git_stream *stream) WSADATA wsd; if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { - giterr_set(GITERR_OS, "Winsock init failed"); + giterr_set(GITERR_OS, "winsock init failed"); return -1; } if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) { WSACleanup(); - giterr_set(GITERR_OS, "Winsock init failed"); + giterr_set(GITERR_OS, "winsock init failed"); return -1; } #endif @@ -99,17 +99,15 @@ int socket_connect(git_stream *stream) if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) { giterr_set(GITERR_NET, - "Failed to resolve address for %s: %s", st->host, p_gai_strerror(ret)); + "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret)); return -1; } for (p = info; p != NULL; p = p->ai_next) { s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if (s == INVALID_SOCKET) { - net_set_error("error creating socket"); - break; - } + if (s == INVALID_SOCKET) + continue; if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) break; @@ -121,7 +119,7 @@ int socket_connect(git_stream *stream) /* Oops, we couldn't connect to any address */ if (s == INVALID_SOCKET && p == NULL) { - giterr_set(GITERR_OS, "Failed to connect to %s", st->host); + giterr_set(GITERR_OS, "failed to connect to %s", st->host); p_freeaddrinfo(info); return -1; } diff --git a/src/sortedcache.c b/src/sortedcache.c index 5bd989a9f..cc322d478 100644 --- a/src/sortedcache.c +++ b/src/sortedcache.c @@ -1,7 +1,5 @@ #include "sortedcache.h" -GIT__USE_STRMAP - int git_sortedcache_new( git_sortedcache **out, size_t item_path_offset, @@ -27,7 +25,7 @@ int git_sortedcache_new( goto fail; if (git_rwlock_init(&sc->lock)) { - giterr_set(GITERR_OS, "Failed to initialize lock"); + giterr_set(GITERR_OS, "failed to initialize lock"); goto fail; } @@ -162,7 +160,7 @@ int git_sortedcache_wlock(git_sortedcache *sc) GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */ if (git_rwlock_wrlock(&sc->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire write lock on cache"); + giterr_set(GITERR_OS, "unable to acquire write lock on cache"); return -1; } return 0; @@ -181,7 +179,7 @@ int git_sortedcache_rlock(git_sortedcache *sc) GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */ if (git_rwlock_rdlock(&sc->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire read lock on cache"); + giterr_set(GITERR_OS, "unable to acquire read lock on cache"); return -1; } return 0; @@ -221,7 +219,7 @@ int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf) } if (!git__is_sizet(st.st_size)) { - giterr_set(GITERR_INVALID, "Unable to load file larger than size_t"); + giterr_set(GITERR_INVALID, "unable to load file larger than size_t"); error = -1; (void)p_close(fd); goto unlock; @@ -294,13 +292,13 @@ int git_sortedcache_upsert(void **out, git_sortedcache *sc, const char *key) item_key = ((char *)item) + sc->item_path_offset; memcpy(item_key, key, keylen); - pos = kh_put(str, sc->map, item_key, &error); + pos = git_strmap_put(sc->map, item_key, &error); if (error < 0) goto done; if (!error) - kh_key(sc->map, pos) = item_key; - kh_val(sc->map, pos) = item; + git_strmap_set_key_at(sc->map, pos, item_key); + git_strmap_set_value_at(sc->map, pos, item); error = git_vector_insert(&sc->items, item); if (error < 0) @@ -373,7 +371,7 @@ int git_sortedcache_remove(git_sortedcache *sc, size_t pos) */ if ((item = git_vector_get(&sc->items, pos)) == NULL) { - giterr_set(GITERR_INVALID, "Removing item out of range"); + giterr_set(GITERR_INVALID, "removing item out of range"); return GIT_ENOTFOUND; } diff --git a/src/stash.c b/src/stash.c index f5f4f36bf..d13220cdd 100644 --- a/src/stash.c +++ b/src/stash.c @@ -27,7 +27,7 @@ static int create_error(int error, const char *msg) { - giterr_set(GITERR_STASH, "Cannot stash changes - %s", msg); + giterr_set(GITERR_STASH, "cannot stash changes - %s", msg); return error; } @@ -36,7 +36,7 @@ static int retrieve_head(git_reference **out, git_repository *repo) int error = git_repository_head(out, repo); if (error == GIT_EUNBORNBRANCH) - return create_error(error, "You do not have the initial commit yet."); + return create_error(error, "you do not have the initial commit yet."); return error; } @@ -198,7 +198,7 @@ static int stash_update_index_from_diff( /* Unimplemented */ giterr_set( GITERR_INVALID, - "Cannot update index. Unimplemented status (%d)", + "cannot update index. Unimplemented status (%d)", delta->status); return -1; } @@ -479,7 +479,7 @@ static int ensure_there_are_changes_to_stash( return 0; if (!error) - return create_error(GIT_ENOTFOUND, "There is nothing to stash."); + return create_error(GIT_ENOTFOUND, "there is nothing to stash."); return error; } @@ -593,7 +593,7 @@ static int retrieve_stash_commit( max = git_reflog_entrycount(reflog); if (!max || index > max - 1) { error = GIT_ENOTFOUND; - giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index); + giterr_set(GITERR_STASH, "no stashed state at position %" PRIuZ, index); goto cleanup; } @@ -1036,7 +1036,7 @@ int git_stash_drop( if (!max || index > max - 1) { error = GIT_ENOTFOUND; - giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index); + giterr_set(GITERR_STASH, "no stashed state at position %" PRIuZ, index); goto cleanup; } diff --git a/src/status.c b/src/status.c index e610f5fe1..6752b5625 100644 --- a/src/status.c +++ b/src/status.c @@ -243,13 +243,13 @@ static int status_validate_options(const git_status_options *opts) GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) { - giterr_set(GITERR_INVALID, "Unknown status 'show' option"); + giterr_set(GITERR_INVALID, "unknown status 'show' option"); return -1; } if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 && (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) { - giterr_set(GITERR_INVALID, "Updating index from status " + giterr_set(GITERR_INVALID, "updating index from status " "is not allowed when index refresh is disabled"); return -1; } @@ -510,13 +510,13 @@ int git_status_file( if (error < 0 && sfi.ambiguous) { giterr_set(GITERR_INVALID, - "Ambiguous path '%s' given to git_status_file", sfi.expected); + "ambiguous path '%s' given to git_status_file", sfi.expected); error = GIT_EAMBIGUOUS; } if (!error && !sfi.count) { giterr_set(GITERR_INVALID, - "Attempt to get status of nonexistent file '%s'", path); + "attempt to get status of nonexistent file '%s'", path); error = GIT_ENOTFOUND; } diff --git a/src/strmap.c b/src/strmap.c index b26a13d1f..de6826d03 100644 --- a/src/strmap.c +++ b/src/strmap.c @@ -7,6 +7,101 @@ #include "strmap.h" +__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) + +int git_strmap_alloc(git_strmap **map) +{ + if ((*map = kh_init(str)) == NULL) { + giterr_set_oom(); + return -1; + } + + return 0; +} + +void git_strmap_free(git_strmap *map) +{ + kh_destroy(str, map); +} + +void git_strmap_clear(git_strmap *map) +{ + kh_clear(str, map); +} + +size_t git_strmap_num_entries(git_strmap *map) +{ + return kh_size(map); +} + +size_t git_strmap_lookup_index(git_strmap *map, const char *key) +{ + return kh_get(str, map, key); +} + +int git_strmap_valid_index(git_strmap *map, size_t idx) +{ + return idx != kh_end(map); +} + +int git_strmap_exists(git_strmap *map, const char *key) +{ + return kh_get(str, map, key) != kh_end(map); +} + +int git_strmap_has_data(git_strmap *map, size_t idx) +{ + return kh_exist(map, idx); +} + +const char *git_strmap_key(git_strmap *map, size_t idx) +{ + return kh_key(map, idx); +} + +void git_strmap_set_key_at(git_strmap *map, size_t idx, char *key) +{ + kh_val(map, idx) = key; +} + +void *git_strmap_value_at(git_strmap *map, size_t idx) +{ + return kh_val(map, idx); +} + +void git_strmap_set_value_at(git_strmap *map, size_t idx, void *value) +{ + kh_val(map, idx) = value; +} + +void git_strmap_delete_at(git_strmap *map, size_t idx) +{ + kh_del(str, map, idx); +} + +int git_strmap_put(git_strmap *map, const char *key, int *err) +{ + return kh_put(str, map, key, err); +} + +void git_strmap_insert(git_strmap *map, const char *key, void *value, int *rval) +{ + khiter_t idx = kh_put(str, map, key, rval); + + if ((*rval) >= 0) { + if ((*rval) == 0) + kh_key(map, idx) = key; + kh_val(map, idx) = value; + } +} + +void git_strmap_delete(git_strmap *map, const char *key) +{ + khiter_t idx = git_strmap_lookup_index(map, key); + if (git_strmap_valid_index(map, idx)) + git_strmap_delete_at(map, idx); +} + int git_strmap_next( void **data, git_strmap_iter* iter, diff --git a/src/strmap.h b/src/strmap.h index 520984744..802b92494 100644 --- a/src/strmap.h +++ b/src/strmap.h @@ -20,49 +20,27 @@ __KHASH_TYPE(str, const char *, void *) typedef khash_t(str) git_strmap; typedef khiter_t git_strmap_iter; -#define GIT__USE_STRMAP \ - __KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) +int git_strmap_alloc(git_strmap **map); +void git_strmap_free(git_strmap *map); +void git_strmap_clear(git_strmap *map); -#define git_strmap_alloc(hp) \ - ((*(hp) = kh_init(str)) == NULL) ? giterr_set_oom(), -1 : 0 +size_t git_strmap_num_entries(git_strmap *map); -#define git_strmap_free(h) kh_destroy(str, h), h = NULL -#define git_strmap_clear(h) kh_clear(str, h) +size_t git_strmap_lookup_index(git_strmap *map, const char *key); +int git_strmap_valid_index(git_strmap *map, size_t idx); -#define git_strmap_num_entries(h) kh_size(h) +int git_strmap_exists(git_strmap *map, const char *key); +int git_strmap_has_data(git_strmap *map, size_t idx); -#define git_strmap_lookup_index(h, k) kh_get(str, h, k) -#define git_strmap_valid_index(h, idx) (idx != kh_end(h)) +const char *git_strmap_key(git_strmap *map, size_t idx); +void git_strmap_set_key_at(git_strmap *map, size_t idx, char *key); +void *git_strmap_value_at(git_strmap *map, size_t idx); +void git_strmap_set_value_at(git_strmap *map, size_t idx, void *value); +void git_strmap_delete_at(git_strmap *map, size_t idx); -#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h)) -#define git_strmap_has_data(h, idx) kh_exist(h, idx) - -#define git_strmap_key(h, idx) kh_key(h, idx) -#define git_strmap_value_at(h, idx) kh_val(h, idx) -#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v -#define git_strmap_delete_at(h, idx) kh_del(str, h, idx) - -#define git_strmap_insert(h, key, val, rval) do { \ - khiter_t __pos = kh_put(str, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) kh_key(h, __pos) = key; \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_strmap_insert2(h, key, val, oldv, rval) do { \ - khiter_t __pos = kh_put(str, h, key, &rval); \ - if (rval >= 0) { \ - if (rval == 0) { \ - oldv = kh_val(h, __pos); \ - kh_key(h, __pos) = key; \ - } else { oldv = NULL; } \ - kh_val(h, __pos) = val; \ - } } while (0) - -#define git_strmap_delete(h, key) do { \ - khiter_t __pos = git_strmap_lookup_index(h, key); \ - if (git_strmap_valid_index(h, __pos)) \ - git_strmap_delete_at(h, __pos); } while (0) +int git_strmap_put(git_strmap *map, const char *key, int *err); +void git_strmap_insert(git_strmap *map, const char *key, void *value, int *rval); +void git_strmap_delete(git_strmap *map, const char *key); #define git_strmap_foreach kh_foreach #define git_strmap_foreach_value kh_foreach_value diff --git a/src/submodule.c b/src/submodule.c index 86ad53be0..ddd4b0663 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -22,6 +22,7 @@ #include "iterator.h" #include "path.h" #include "index.h" +#include "worktree.h" #define GIT_MODULES_FILE ".gitmodules" @@ -124,8 +125,8 @@ static void submodule_set_lookup_error(int error, const char *name) return; giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? - "No submodule named '%s'" : - "Submodule '%s' has not been added yet", name); + "no submodule named '%s'" : + "submodule '%s' has not been added yet", name); } typedef struct { @@ -149,40 +150,52 @@ static int find_by_path(const git_config_entry *entry, void *payload) } /** - * Find out the name of a submodule from its path + * Release the name map returned by 'load_submodule_names'. */ -static int name_from_path(git_buf *out, git_config *cfg, const char *path) +static void free_submodule_names(git_strmap *names) +{ + git_buf *name = 0; + if (names == NULL) + return; + git_strmap_foreach_value(names, name, { + git__free(name); + }); + git_strmap_free(names); + return; +} + +/** + * Map submodule paths to names. + * TODO: for some use-cases, this might need case-folding on a + * case-insensitive filesystem + */ +static int load_submodule_names(git_strmap *out, git_config *cfg) { const char *key = "submodule\\..*\\.path"; git_config_iterator *iter; git_config_entry *entry; - int error; + git_buf buf = GIT_BUF_INIT; + int rval; + int error = 0; if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0) return error; - while ((error = git_config_next(&entry, iter)) == 0) { + while (git_config_next(&entry, iter) == 0) { const char *fdot, *ldot; - /* TODO: this should maybe be strcasecmp on a case-insensitive fs */ - if (strcmp(path, entry->value) != 0) - continue; - fdot = strchr(entry->name, '.'); ldot = strrchr(entry->name, '.'); - git_buf_clear(out); - git_buf_put(out, fdot + 1, ldot - fdot - 1); - goto cleanup; + git_buf_put(&buf, fdot + 1, ldot - fdot - 1); + git_strmap_insert(out, entry->value, git_buf_detach(&buf), &rval); + if (rval < 0) { + giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table"); + return -1; + } } - if (error == GIT_ITEROVER) { - giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path); - error = GIT_ENOTFOUND; - } - -cleanup: git_config_iterator_free(iter); - return error; + return 0; } int git_submodule_lookup( @@ -196,6 +209,17 @@ int git_submodule_lookup( assert(repo && name); + if (repo->submodule_cache != NULL) { + khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name); + if (git_strmap_valid_index(repo->submodule_cache, pos)) { + if (out) { + *out = git_strmap_value_at(repo->submodule_cache, pos); + GIT_REFCOUNT_INC(*out); + } + return 0; + } + } + if ((error = submodule_alloc(&sm, repo, name)) < 0) return error; @@ -306,7 +330,7 @@ static int submodule_get_or_create(git_submodule **out, git_repository *repo, gi if ((error = submodule_alloc(&sm, repo, name)) < 0) return error; - pos = kh_put(str, map, sm->name, &error); + pos = git_strmap_put(map, sm->name, &error); /* nobody can beat us to adding it */ assert(error != 0); if (error < 0) { @@ -324,89 +348,108 @@ done: static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg) { - int error; - git_iterator *i; - const git_index_entry *entry; - git_buf name = GIT_BUF_INIT; + int error; + git_iterator *i = NULL; + const git_index_entry *entry; + git_strmap *names = 0; - if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0) - return error; + git_strmap_alloc(&names); + if ((error = load_submodule_names(names, cfg))) + goto done; - while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(map, entry->path); - git_submodule *sm; + if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0) + goto done; - git_buf_clear(&name); - if (!name_from_path(&name, cfg, entry->path)) { - git_strmap_lookup_index(map, name.ptr); - } + while (!(error = git_iterator_advance(&entry, i))) { + khiter_t pos = git_strmap_lookup_index(map, entry->path); + git_submodule *sm; - if (git_strmap_valid_index(map, pos)) { - sm = git_strmap_value_at(map, pos); + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); - if (S_ISGITLINK(entry->mode)) - submodule_update_from_index_entry(sm, entry); - else - sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; - } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name.ptr ? name.ptr : entry->path)) { - submodule_update_from_index_entry(sm, entry); - git_submodule_free(sm); - } - } - } + if (S_ISGITLINK(entry->mode)) + submodule_update_from_index_entry(sm, entry); + else + sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; + } else if (S_ISGITLINK(entry->mode)) { + khiter_t name_pos; + const char *name; - if (error == GIT_ITEROVER) - error = 0; + name_pos = git_strmap_lookup_index(names, entry->path); + if (git_strmap_valid_index(names, name_pos)) { + name = git_strmap_value_at(names, name_pos); + } else { + name = entry->path; + } - git_buf_free(&name); - git_iterator_free(i); + if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) { + submodule_update_from_index_entry(sm, entry); + git_submodule_free(sm); + } + } + } - return error; + if (error == GIT_ITEROVER) + error = 0; + +done: + git_iterator_free(i); + free_submodule_names(names); + + return error; } static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg) { - int error; - git_iterator *i; - const git_index_entry *entry; - git_buf name = GIT_BUF_INIT; + int error; + git_iterator *i = NULL; + const git_index_entry *entry; + git_strmap *names = 0; + git_strmap_alloc(&names); + if ((error = load_submodule_names(names, cfg))) + goto done; - if ((error = git_iterator_for_tree(&i, head, NULL)) < 0) - return error; + if ((error = git_iterator_for_tree(&i, head, NULL)) < 0) + goto done; - while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(map, entry->path); - git_submodule *sm; + while (!(error = git_iterator_advance(&entry, i))) { + khiter_t pos = git_strmap_lookup_index(map, entry->path); + git_submodule *sm; - git_buf_clear(&name); - if (!name_from_path(&name, cfg, entry->path)) { - git_strmap_lookup_index(map, name.ptr); - } + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); - if (git_strmap_valid_index(map, pos)) { - sm = git_strmap_value_at(map, pos); + if (S_ISGITLINK(entry->mode)) + submodule_update_from_head_data(sm, entry->mode, &entry->id); + else + sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; + } else if (S_ISGITLINK(entry->mode)) { + khiter_t name_pos; + const char *name; - if (S_ISGITLINK(entry->mode)) - submodule_update_from_head_data(sm, entry->mode, &entry->id); - else - sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; - } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name.ptr ? name.ptr : entry->path)) { - submodule_update_from_head_data( - sm, entry->mode, &entry->id); - git_submodule_free(sm); - } - } - } + name_pos = git_strmap_lookup_index(names, entry->path); + if (git_strmap_valid_index(names, name_pos)) { + name = git_strmap_value_at(names, name_pos); + } else { + name = entry->path; + } - if (error == GIT_ITEROVER) - error = 0; + if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) { + submodule_update_from_head_data( + sm, entry->mode, &entry->id); + git_submodule_free(sm); + } + } + } - git_buf_free(&name); - git_iterator_free(i); + if (error == GIT_ITEROVER) + error = 0; - return error; +done: + git_iterator_free(i); + free_submodule_names(names); + + return error; } /* If have_sm is true, sm is populated, otherwise map an repo are. */ @@ -416,7 +459,7 @@ typedef struct { git_repository *repo; } lfc_data; -static int all_submodules(git_repository *repo, git_strmap *map) +int git_submodule__map(git_repository *repo, git_strmap *map) { int error = 0; git_index *idx = NULL; @@ -470,12 +513,12 @@ static int all_submodules(git_repository *repo, git_strmap *map) goto cleanup; } /* add back submodule information from index */ - if (idx) { + if (mods && idx) { if ((error = submodules_from_index(map, idx, mods)) < 0) goto cleanup; } /* add submodule information from HEAD */ - if (head) { + if (mods && head) { if ((error = submodules_from_head(map, head, mods)) < 0) goto cleanup; } @@ -509,11 +552,11 @@ int git_submodule_foreach( if ((error = git_strmap_alloc(&submodules)) < 0) return error; - if ((error = all_submodules(repo, submodules)) < 0) + if ((error = git_submodule__map(repo, submodules)) < 0) goto done; if (!(error = git_vector_init( - &snapshot, kh_size(submodules), submodule_cmp))) { + &snapshot, git_strmap_num_entries(submodules), submodule_cmp))) { git_strmap_foreach_value(submodules, sm, { if ((error = git_vector_insert(&snapshot, sm)) < 0) @@ -574,8 +617,10 @@ static int submodule_repo_init( * Old style: sub-repo goes directly into repo//.git/ */ if (use_gitlink) { - error = git_buf_join3( - &repodir, '/', git_repository_path(parent_repo), "modules", path); + error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES); + if (error < 0) + goto cleanup; + error = git_buf_joinpath(&repodir, repodir.ptr, path); if (error < 0) goto cleanup; @@ -618,7 +663,7 @@ int git_submodule_add_setup( giterr_clear(); else { giterr_set(GITERR_SUBMODULE, - "Attempt to add submodule '%s' that already exists", path); + "attempt to add submodule '%s' that already exists", path); return GIT_EEXISTS; } @@ -628,7 +673,7 @@ int git_submodule_add_setup( path += strlen(git_repository_workdir(repo)); if (git_path_root(path) >= 0) { - giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path"); + giterr_set(GITERR_SUBMODULE, "submodule path must be a relative path"); error = -1; goto cleanup; } @@ -637,7 +682,7 @@ int git_submodule_add_setup( if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) { giterr_set(GITERR_SUBMODULE, - "Adding submodules to a bare repository is not supported"); + "adding submodules to a bare repository is not supported"); return -1; } @@ -758,7 +803,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index) /* read stat information for submodule working directory */ if (p_stat(path.ptr, &st) < 0) { giterr_set(GITERR_SUBMODULE, - "Cannot add submodule without working directory"); + "cannot add submodule without working directory"); error = -1; goto cleanup; } @@ -771,7 +816,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index) /* calling git_submodule_open will have set sm->wd_oid if possible */ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) { giterr_set(GITERR_SUBMODULE, - "Cannot add submodule without HEAD to index"); + "cannot add submodule without HEAD to index"); error = -1; goto cleanup; } @@ -861,7 +906,7 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur } else if (strchr(url, ':') != NULL || url[0] == '/') { error = git_buf_sets(out, url); } else { - giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL"); + giterr_set(GITERR_SUBMODULE, "invalid format for submodule URL"); error = -1; } @@ -1042,8 +1087,10 @@ static int submodule_repo_create( * /modules// with a gitlink in the * sub-repo workdir directory to that repository. */ - error = git_buf_join3( - &repodir, '/', git_repository_path(parent_repo), "modules", path); + error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES); + if (error < 0) + goto cleanup; + error = git_buf_joinpath(&repodir, repodir.ptr, path); if (error < 0) goto cleanup; @@ -1133,7 +1180,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio goto done; if (!init) { - giterr_set(GITERR_SUBMODULE, "Submodule is not initialized."); + giterr_set(GITERR_SUBMODULE, "submodule is not initialized"); error = GIT_ERROR; goto done; } @@ -1160,13 +1207,14 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio * will checkout the specific commit manually. */ clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE; - update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy; if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 || (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 || (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0) goto done; } else { + const git_oid *oid; + /** * Work dir is initialized - look up the commit in the parent repository's index, * update the workdir contents of the subrepository, and set the subrepository's @@ -1175,8 +1223,14 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio if ((error = git_submodule_open(&sub_repo, sm)) < 0) goto done; + if ((oid = git_submodule_index_id(sm)) == NULL) { + giterr_set(GITERR_SUBMODULE, "could not get ID of submodule in index"); + error = -1; + goto done; + } + /* Look up the target commit in the submodule. */ - if ((error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0) { + if ((error = git_object_lookup(&target_commit, sub_repo, oid, GIT_OBJ_COMMIT)) < 0) { /* If it isn't found then fetch and try again. */ if (error != GIT_ENOTFOUND || !update_options.allow_fetch || (error = lookup_default_remote(&remote, sub_repo)) < 0 || @@ -1215,7 +1269,7 @@ int git_submodule_init(git_submodule *sm, int overwrite) if (!sm->url) { giterr_set(GITERR_SUBMODULE, - "No URL configured for submodule '%s'", sm->name); + "no URL configured for submodule '%s'", sm->name); return -1; } @@ -1259,7 +1313,7 @@ int git_submodule_sync(git_submodule *sm) if (!sm->url) { giterr_set(GITERR_SUBMODULE, - "No URL configured for submodule '%s'", sm->name); + "no URL configured for submodule '%s'", sm->name); return -1; } @@ -1502,13 +1556,22 @@ int git_submodule__status( return 0; } - /* refresh the index OID */ - if (submodule_update_index(sm) < 0) - return -1; + /* If the user has requested caching submodule state, performing these + * expensive operations (especially `submodule_update_head`, which is + * bottlenecked on `git_repository_head_tree`) eliminates much of the + * advantage. We will, therefore, interpret the request for caching to + * apply here to and skip them. + */ - /* refresh the HEAD OID */ - if (submodule_update_head(sm) < 0) - return -1; + if (sm->repo->submodule_cache == NULL) { + /* refresh the index OID */ + if (submodule_update_index(sm) < 0) + return -1; + + /* refresh the HEAD OID */ + if (submodule_update_head(sm) < 0) + return -1; + } /* for ignore == dirty, don't scan the working directory */ if (ign == GIT_SUBMODULE_IGNORE_DIRTY) { @@ -1566,7 +1629,6 @@ int git_submodule_location(unsigned int *location, git_submodule *sm) location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL); } - /* * INTERNAL FUNCTIONS */ @@ -1578,7 +1640,7 @@ static int submodule_alloc( git_submodule *sm; if (!name || !(namelen = strlen(name))) { - giterr_set(GITERR_SUBMODULE, "Invalid submodule name"); + giterr_set(GITERR_SUBMODULE, "invalid submodule name"); return -1; } @@ -1630,7 +1692,7 @@ void git_submodule_free(git_submodule *sm) static int submodule_config_error(const char *property, const char *value) { giterr_set(GITERR_INVALID, - "Invalid value for submodule '%s' property: '%s'", property, value); + "invalid value for submodule '%s' property: '%s'", property, value); return -1; } @@ -1813,7 +1875,7 @@ static int submodule_load_each(const git_config_entry *entry, void *payload) goto done; } - git_strmap_insert(map, sm->name, sm, error); + git_strmap_insert(map, sm->name, sm, &error); assert(error != 0); if (error < 0) goto done; @@ -1968,7 +2030,7 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo) if (error == GIT_ENOTFOUND) giterr_set( GITERR_SUBMODULE, - "Cannot get default remote for submodule - no local tracking " + "cannot get default remote for submodule - no local tracking " "branch for HEAD and origin does not exist"); return error; @@ -1977,17 +2039,28 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo) static int get_url_base(git_buf *url, git_repository *repo) { int error; + git_worktree *wt = NULL; git_remote *remote = NULL; - if (!(error = lookup_default_remote(&remote, repo))) { + if ((error = lookup_default_remote(&remote, repo)) == 0) { error = git_buf_sets(url, git_remote_url(remote)); - git_remote_free(remote); - } - else if (error == GIT_ENOTFOUND) { - /* if repository does not have a default remote, use workdir instead */ + goto out; + } else if (error != GIT_ENOTFOUND) + goto out; + else giterr_clear(); + + /* if repository does not have a default remote, use workdir instead */ + if (git_repository_is_worktree(repo)) { + if ((error = git_worktree_open_from_repository(&wt, repo)) < 0) + goto out; + error = git_buf_sets(url, wt->parent_path); + } else error = git_buf_sets(url, git_repository_workdir(repo)); - } + +out: + git_remote_free(remote); + git_worktree_free(wt); return error; } diff --git a/src/submodule.h b/src/submodule.h index 2ef2031b3..456a93979 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -143,4 +143,7 @@ extern int git_submodule_parse_ignore( extern int git_submodule_parse_update( git_submodule_update_t *out, const char *value); +extern int git_submodule__map( + git_repository *repo, + git_strmap *map); #endif diff --git a/src/sysdir.c b/src/sysdir.c index e89db7697..9312a7edb 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -150,7 +150,7 @@ int git_sysdir_get_str( GITERR_CHECK_ERROR(git_sysdir_get(&path, which)); if (!out || path->size >= outlen) { - giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path"); + giterr_set(GITERR_NOMEMORY, "buffer is too short for the path"); return GIT_EBUFS; } @@ -241,7 +241,7 @@ static int git_sysdir_find_in_dirlist( done: git_buf_free(path); - giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name); + giterr_set(GITERR_OS, "the %s file '%s' doesn't exist", label, name); return GIT_ENOTFOUND; } @@ -275,3 +275,14 @@ int git_sysdir_find_template_dir(git_buf *path) path, NULL, GIT_SYSDIR_TEMPLATE, "template"); } +int git_sysdir_expand_global_file(git_buf *path, const char *filename) +{ + int error; + + if ((error = git_sysdir_find_global_file(path, NULL)) == 0) { + if (filename) + error = git_buf_joinpath(path, path->ptr, filename); + } + + return error; +} diff --git a/src/sysdir.h b/src/sysdir.h index 11878981c..79f23818a 100644 --- a/src/sysdir.h +++ b/src/sysdir.h @@ -55,6 +55,18 @@ extern int git_sysdir_find_programdata_file(git_buf *path, const char *filename) */ extern int git_sysdir_find_template_dir(git_buf *path); +/** + * Expand the name of a "global" file (i.e. one in a user's home + * directory). Unlike `find_global_file` (above), this makes no + * attempt to check for the existence of the file, and is useful if + * you want the full path regardless of existence. + * + * @param path buffer to write the full path into + * @param filename name of file in the home directory + * @return 0 on success or -1 on error + */ +extern int git_sysdir_expand_global_file(git_buf *path, const char *filename); + typedef enum { GIT_SYSDIR_SYSTEM = 0, GIT_SYSDIR_GLOBAL = 1, diff --git a/src/tag.c b/src/tag.c index fe840fe82..2bf23fc3c 100644 --- a/src/tag.c +++ b/src/tag.c @@ -61,7 +61,7 @@ const char *git_tag_message(const git_tag *t) static int tag_error(const char *str) { - giterr_set(GITERR_TAG, "Failed to parse tag. %s", str); + giterr_set(GITERR_TAG, "failed to parse tag: %s", str); return -1; } @@ -76,13 +76,13 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) char *search; if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0) - return tag_error("Object field invalid"); + return tag_error("object field invalid"); if (buffer + 5 >= buffer_end) - return tag_error("Object too short"); + return tag_error("object too short"); if (memcmp(buffer, "type ", 5) != 0) - return tag_error("Type field not found"); + return tag_error("type field not found"); buffer += 5; tag->type = GIT_OBJ_BAD; @@ -91,7 +91,7 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) size_t type_length = strlen(tag_types[i]); if (buffer + type_length >= buffer_end) - return tag_error("Object too short"); + return tag_error("object too short"); if (memcmp(buffer, tag_types[i], type_length) == 0) { tag->type = i; @@ -101,19 +101,19 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) } if (tag->type == GIT_OBJ_BAD) - return tag_error("Invalid object type"); + return tag_error("invalid object type"); if (buffer + 4 >= buffer_end) - return tag_error("Object too short"); + return tag_error("object too short"); if (memcmp(buffer, "tag ", 4) != 0) - return tag_error("Tag field not found"); + return tag_error("tag field not found"); buffer += 4; search = memchr(buffer, '\n', buffer_end - buffer); if (search == NULL) - return tag_error("Object too short"); + return tag_error("object too short"); text_len = search - buffer; @@ -234,7 +234,7 @@ static int write_tag_annotation( on_error: git_buf_free(&tag); - giterr_set(GITERR_OBJECT, "Failed to create tag annotation."); + giterr_set(GITERR_OBJECT, "failed to create tag annotation"); return -1; } @@ -257,7 +257,7 @@ static int git_tag_create__internal( assert(!create_tag_annotation || (tagger && message)); if (git_object_owner(target) != repo) { - giterr_set(GITERR_INVALID, "The given target does not belong to this repository"); + giterr_set(GITERR_INVALID, "the given target does not belong to this repository"); return -1; } @@ -269,7 +269,7 @@ static int git_tag_create__internal( * reference unless overwriting has explicitly been requested **/ if (error == 0 && !allow_ref_overwrite) { git_buf_free(&ref_name); - giterr_set(GITERR_TAG, "Tag already exists"); + giterr_set(GITERR_TAG, "tag already exists"); return GIT_EEXISTS; } @@ -349,7 +349,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu goto on_error; if (tag.type != target_obj->cached.type) { - giterr_set(GITERR_TAG, "The type for the given target is invalid"); + giterr_set(GITERR_TAG, "the type for the given target is invalid"); goto on_error; } @@ -366,7 +366,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu /** Ensure the tag name doesn't conflict with an already existing * reference unless overwriting has explicitly been requested **/ if (error == 0 && !allow_ref_overwrite) { - giterr_set(GITERR_TAG, "Tag already exists"); + giterr_set(GITERR_TAG, "tag already exists"); return GIT_EEXISTS; } diff --git a/src/thread-utils.h b/src/thread-utils.h index f0161989f..2df2aeb99 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -7,6 +7,12 @@ #ifndef INCLUDE_thread_utils_h__ #define INCLUDE_thread_utils_h__ +#if defined(__GNUC__) && defined(GIT_THREADS) +# if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)) +# error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DTHREADSAFE=OFF +# endif +#endif + /* Common operations even if threading has been disabled */ typedef struct { #if defined(GIT_WIN32) diff --git a/src/trace.c b/src/trace.c index ee5039f56..0f2142861 100644 --- a/src/trace.c +++ b/src/trace.c @@ -32,7 +32,7 @@ int git_trace_set(git_trace_level_t level, git_trace_callback callback) GIT_UNUSED(callback); giterr_set(GITERR_INVALID, - "This version of libgit2 was not built with tracing."); + "this version of libgit2 was not built with tracing."); return -1; #endif } diff --git a/src/transaction.c b/src/transaction.c index 2c8a1e8bd..3d3f35a03 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -19,8 +19,6 @@ #include "git2/sys/refs.h" #include "git2/sys/refdb_backend.h" -GIT__USE_STRMAP - typedef enum { TRANSACTION_NONE, TRANSACTION_REFS, @@ -120,7 +118,7 @@ int git_transaction_lock_ref(git_transaction *tx, const char *refname) if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0) return error; - git_strmap_insert(tx->locks, node->name, node, error); + git_strmap_insert(tx->locks, node->name, node, &error); if (error < 0) goto cleanup; @@ -323,7 +321,6 @@ static int update_target(git_refdb *db, transaction_node *node) int git_transaction_commit(git_transaction *tx) { transaction_node *node; - git_strmap_iter pos; int error = 0; assert(tx); @@ -335,11 +332,7 @@ int git_transaction_commit(git_transaction *tx) return error; } - for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { - if (!git_strmap_has_data(tx->locks, pos)) - continue; - - node = git_strmap_value_at(tx->locks, pos); + git_strmap_foreach_value(tx->locks, node, { if (node->reflog) { if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0) return error; @@ -349,7 +342,7 @@ int git_transaction_commit(git_transaction *tx) if ((error = update_target(tx->db, node)) < 0) return error; } - } + }); return 0; } @@ -358,7 +351,6 @@ void git_transaction_free(git_transaction *tx) { transaction_node *node; git_pool pool; - git_strmap_iter pos; assert(tx); @@ -373,16 +365,12 @@ void git_transaction_free(git_transaction *tx) } /* start by unlocking the ones we've left hanging, if any */ - for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { - if (!git_strmap_has_data(tx->locks, pos)) - continue; - - node = git_strmap_value_at(tx->locks, pos); + git_strmap_foreach_value(tx->locks, node, { if (node->committed) continue; git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL); - } + }); git_refdb_free(tx->db); git_strmap_free(tx->locks); diff --git a/src/transport.c b/src/transport.c index f08d2dc68..b66165332 100644 --- a/src/transport.c +++ b/src/transport.c @@ -121,7 +121,7 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url) int error; if ((error = transport_find_fn(&fn, url, ¶m)) == GIT_ENOTFOUND) { - giterr_set(GITERR_NET, "Unsupported URL protocol"); + giterr_set(GITERR_NET, "unsupported URL protocol"); return -1; } else if (error < 0) return error; diff --git a/src/transports/auth_negotiate.c b/src/transports/auth_negotiate.c index 8b99fc735..7c868c9fd 100644 --- a/src/transports/auth_negotiate.c +++ b/src/transports/auth_negotiate.c @@ -107,13 +107,13 @@ static int negotiate_next_token( challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0; if (challenge_len < 9) { - giterr_set(GITERR_NET, "No negotiate challenge sent from server"); + giterr_set(GITERR_NET, "no negotiate challenge sent from server"); error = -1; goto done; } else if (challenge_len > 9) { if (git_buf_decode_base64(&input_buf, ctx->challenge + 10, challenge_len - 10) < 0) { - giterr_set(GITERR_NET, "Invalid negotiate challenge from server"); + giterr_set(GITERR_NET, "invalid negotiate challenge from server"); error = -1; goto done; } @@ -122,7 +122,7 @@ static int negotiate_next_token( input_token.length = input_buf.size; input_token_ptr = &input_token; } else if (ctx->gss_context != GSS_C_NO_CONTEXT) { - giterr_set(GITERR_NET, "Could not restart authentication"); + giterr_set(GITERR_NET, "could not restart authentication"); error = -1; goto done; } @@ -228,7 +228,7 @@ static int negotiate_init_context( gss_release_oid_set(&status_minor, &mechanism_list); if (!ctx->oid) { - giterr_set(GITERR_NET, "Negotiate authentication is not supported"); + giterr_set(GITERR_NET, "negotiate authentication is not supported"); return -1; } diff --git a/src/transports/cred.c b/src/transports/cred.c index 49ede48bf..8e3f64435 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -216,7 +216,7 @@ int git_cred_ssh_key_memory_new( GIT_UNUSED(passphrase); giterr_set(GITERR_INVALID, - "This version of libgit2 was not built with ssh memory credentials."); + "this version of libgit2 was not built with ssh memory credentials."); return -1; #endif } diff --git a/src/transports/git.c b/src/transports/git.c index 6c6acf9c5..01edfdc49 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -45,7 +45,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) delim = strchr(url, '/'); if (delim == NULL) { - giterr_set(GITERR_NET, "Malformed URL"); + giterr_set(GITERR_NET, "malformed URL"); return -1; } @@ -240,7 +240,7 @@ static int _git_uploadpack( return 0; } - giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK"); + giterr_set(GITERR_NET, "must call UPLOADPACK_LS before UPLOADPACK"); return -1; } @@ -296,7 +296,7 @@ static int _git_receivepack( return 0; } - giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK"); + giterr_set(GITERR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK"); return -1; } diff --git a/src/transports/http.c b/src/transports/http.c index 155fd7b30..cb4a6d0d5 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -403,7 +403,7 @@ static int on_headers_complete(http_parser *parser) t->location) { if (s->redirect_count >= 7) { - giterr_set(GITERR_NET, "Too many redirects"); + giterr_set(GITERR_NET, "too many redirects"); return t->parse_error = PARSE_ERROR_GENERIC; } @@ -428,14 +428,14 @@ static int on_headers_complete(http_parser *parser) /* Check for a 200 HTTP status code. */ if (parser->status_code != 200) { giterr_set(GITERR_NET, - "Unexpected HTTP status code: %d", + "unexpected HTTP status code: %d", parser->status_code); return t->parse_error = PARSE_ERROR_GENERIC; } /* The response must contain a Content-Type header. */ if (!t->content_type) { - giterr_set(GITERR_NET, "No Content-Type header in response"); + giterr_set(GITERR_NET, "no Content-Type header in response"); return t->parse_error = PARSE_ERROR_GENERIC; } @@ -455,7 +455,7 @@ static int on_headers_complete(http_parser *parser) if (strcmp(t->content_type, git_buf_cstr(&buf))) { git_buf_free(&buf); giterr_set(GITERR_NET, - "Invalid Content-Type: %s", + "invalid Content-Type: %s", t->content_type); return t->parse_error = PARSE_ERROR_GENERIC; } @@ -488,7 +488,7 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len) return 0; if (ctx->buf_size < len) { - giterr_set(GITERR_NET, "Can't fit data in the buffer"); + giterr_set(GITERR_NET, "can't fit data in the buffer"); return t->parse_error = PARSE_ERROR_GENERIC; } @@ -575,6 +575,9 @@ static int apply_proxy_config(http_subtransport *t) if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0) return error; + opts.credentials = t->owner->proxy.credentials; + opts.certificate_check = t->owner->proxy.certificate_check; + opts.payload = t->owner->proxy.payload; opts.type = GIT_PROXY_SPECIFIED; opts.url = url; error = git_stream_set_proxy(t->io, &opts); @@ -856,7 +859,7 @@ static int http_stream_write_single( assert(t->connected); if (s->sent_request) { - giterr_set(GITERR_NET, "Subtransport configured for only one write"); + giterr_set(GITERR_NET, "subtransport configured for only one write"); return -1; } diff --git a/src/transports/local.c b/src/transports/local.c index 4eae9dead..e24e99860 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -252,7 +252,7 @@ static int local_ls(const git_remote_head ***out, size_t *size, git_transport *t transport_local *t = (transport_local *)transport; if (!t->have_refs) { - giterr_set(GITERR_NET, "The transport has not yet loaded the refs"); + giterr_set(GITERR_NET, "the transport has not yet loaded the refs"); return -1; } @@ -371,11 +371,12 @@ static int local_push( but we forbid all pushes just in case */ if (!remote_repo->is_bare) { error = GIT_EBAREREPO; - giterr_set(GITERR_INVALID, "Local push doesn't (yet) support pushing to non-bare repos."); + giterr_set(GITERR_INVALID, "local push doesn't (yet) support pushing to non-bare repos."); goto on_error; } - if ((error = git_buf_joinpath(&odb_path, git_repository_path(remote_repo), "objects/pack")) < 0) + if ((error = git_repository_item_path(&odb_path, remote_repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0 + || (error = git_buf_joinpath(&odb_path, odb_path.ptr, "pack")) < 0) goto on_error; error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs); diff --git a/src/transports/smart.c b/src/transports/smart.c index 7a35c39d8..a96fdf6fb 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -231,7 +231,7 @@ static int git_smart__connect( else if (GIT_DIRECTION_PUSH == t->direction) service = GIT_SERVICE_RECEIVEPACK_LS; else { - giterr_set(GITERR_NET, "Invalid direction"); + giterr_set(GITERR_NET, "invalid direction"); return -1; } @@ -252,7 +252,7 @@ static int git_smart__connect( pkt = (git_pkt *)git_vector_get(&t->refs, 0); if (!pkt || GIT_PKT_COMMENT != pkt->type) { - giterr_set(GITERR_NET, "Invalid response"); + giterr_set(GITERR_NET, "invalid response"); return -1; } else { /* Remove the comment pkt from the list */ @@ -299,7 +299,7 @@ static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transpo transport_smart *t = (transport_smart *)transport; if (!t->have_refs) { - giterr_set(GITERR_NET, "The transport has not yet loaded the refs"); + giterr_set(GITERR_NET, "the transport has not yet loaded the refs"); return -1; } @@ -319,7 +319,7 @@ int git_smart__negotiation_step(git_transport *transport, void *data, size_t len return -1; if (GIT_DIRECTION_FETCH != t->direction) { - giterr_set(GITERR_NET, "This operation is only valid for fetch"); + giterr_set(GITERR_NET, "this operation is only valid for fetch"); return -1; } @@ -348,7 +348,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream return -1; if (GIT_DIRECTION_PUSH != t->direction) { - giterr_set(GITERR_NET, "This operation is only valid for push"); + giterr_set(GITERR_NET, "this operation is only valid for push"); return -1; } @@ -472,6 +472,12 @@ int git_transport_smart_credentials(git_cred **out, git_transport *transport, co return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload); } +int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport) +{ + transport_smart *t = (transport_smart *) transport; + return git_proxy_options_dup(out, &t->proxy); +} + int git_transport_smart(git_transport **out, git_remote *owner, void *param) { transport_smart *t; diff --git a/src/transports/smart.h b/src/transports/smart.h index 0a0c3fc1b..b47001fe0 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -26,6 +26,8 @@ #define GIT_CAP_THIN_PACK "thin-pack" #define GIT_CAP_SYMREF "symref" +extern bool git_smart__ofs_delta_enabled; + enum git_pkt_type { GIT_PKT_CMD, GIT_PKT_FLUSH, diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index e05196cd8..a661dfe13 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -226,7 +226,7 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len) /* Check for a bit of consistency */ if (line[GIT_OID_HEXSZ] != ' ') { - giterr_set(GITERR_NET, "Error parsing pkt-line"); + giterr_set(GITERR_NET, "error parsing pkt-line"); error = -1; goto error_out; } @@ -270,7 +270,7 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len) line += 3; /* skip "ok " */ if (!(ptr = strchr(line, '\n'))) { - giterr_set(GITERR_NET, "Invalid packet line"); + giterr_set(GITERR_NET, "invalid packet line"); git__free(pkt); return -1; } @@ -327,7 +327,7 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) return 0; out_err: - giterr_set(GITERR_NET, "Invalid packet line"); + giterr_set(GITERR_NET, "invalid packet line"); git__free(pkt->ref); git__free(pkt); return -1; @@ -543,7 +543,7 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca if (len > 0xffff) { giterr_set(GITERR_NET, - "Tried to produce packet with invalid length %" PRIuZ, len); + "tried to produce packet with invalid length %" PRIuZ, len); return -1; } diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index db6a8b9c8..8146fa163 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -19,6 +19,8 @@ /* The minimal interval between progress updates (in seconds). */ #define MIN_PROGRESS_UPDATE_INTERVAL 0.5 +bool git_smart__ofs_delta_enabled = true; + int git_smart__store_refs(transport_smart *t, int flushes) { gitno_buffer *buf = &t->buffer; @@ -60,7 +62,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) gitno_consume(buf, line_end); if (pkt->type == GIT_PKT_ERR) { - giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error); + giterr_set(GITERR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error); git__free(pkt); return -1; } @@ -138,7 +140,7 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec if (*ptr == ' ') ptr++; - if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { + if (git_smart__ofs_delta_enabled && !git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { caps->common = caps->ofs_delta = 1; ptr += strlen(GIT_CAP_OFS_DELTA); continue; @@ -323,7 +325,8 @@ static int wait_while_ack(gitno_buffer *buf) if (pkt->type == GIT_PKT_ACK && (pkt->status != GIT_ACK_CONTINUE && - pkt->status != GIT_ACK_COMMON)) { + pkt->status != GIT_ACK_COMMON && + pkt->status != GIT_ACK_READY)) { git__free(pkt); return 0; } diff --git a/src/transports/ssh.c b/src/transports/ssh.c index cfd573665..4c55e3f2a 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -9,6 +9,7 @@ #include #endif +#include "global.h" #include "git2.h" #include "buffer.h" #include "netops.h" @@ -83,7 +84,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) done: if (!repo) { - giterr_set(GITERR_NET, "Malformed git protocol URL"); + giterr_set(GITERR_NET, "malformed git protocol URL"); return -1; } @@ -274,7 +275,7 @@ static int git_ssh_extract_url_parts( } if (colon == NULL || (colon < start)) { - giterr_set(GITERR_NET, "Malformed URL"); + giterr_set(GITERR_NET, "malformed URL"); return -1; } @@ -445,7 +446,7 @@ static int request_creds(git_cred **out, ssh_subtransport *t, const char *user, else if (error < 0) return error; else if (!cred) { - giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials"); + giterr_set(GITERR_SSH, "callback failed to initialize SSH credentials"); return -1; } } @@ -478,7 +479,7 @@ static int _git_ssh_session_create( s = libssh2_session_init(); if (!s) { - giterr_set(GITERR_NET, "Failed to initialize SSH session"); + giterr_set(GITERR_NET, "failed to initialize SSH session"); return -1; } @@ -487,7 +488,7 @@ static int _git_ssh_session_create( } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); if (rc != LIBSSH2_ERROR_NONE) { - ssh_error(s, "Failed to start SSH session"); + ssh_error(s, "failed to start SSH session"); libssh2_session_free(s); return -1; } @@ -685,7 +686,7 @@ static int ssh_uploadpack( return 0; } - giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK"); + giterr_set(GITERR_NET, "must call UPLOADPACK_LS before UPLOADPACK"); return -1; } @@ -712,7 +713,7 @@ static int ssh_receivepack( return 0; } - giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK"); + giterr_set(GITERR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK"); return -1; } @@ -844,7 +845,7 @@ int git_smart_subtransport_ssh( assert(out); *out = NULL; - giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support"); + giterr_set(GITERR_INVALID, "cannot create SSH transport. Library was built without SSH support"); return -1; #endif } @@ -888,16 +889,27 @@ int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *p assert(out); *out = NULL; - giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support"); + giterr_set(GITERR_INVALID, "cannot create SSH transport. Library was built without SSH support"); return -1; #endif } +#ifdef GIT_SSH +static void shutdown_ssh(void) +{ + libssh2_exit(); +} +#endif + int git_transport_ssh_global_init(void) { #ifdef GIT_SSH + if (libssh2_init(0) < 0) { + giterr_set(GITERR_SSH, "unable to initialize libssh2"); + return -1; + } - libssh2_init(0); + git__on_shutdown(shutdown_ssh); return 0; #else diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 78e42cf3b..fb504c912 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -68,7 +68,9 @@ static const IID IID_IInternetSecurityManager_mingw = typedef enum { GIT_WINHTTP_AUTH_BASIC = 1, - GIT_WINHTTP_AUTH_NEGOTIATE = 2, + GIT_WINHTTP_AUTH_NTLM = 2, + GIT_WINHTTP_AUTH_NEGOTIATE = 4, + GIT_WINHTTP_AUTH_DIGEST = 8, } winhttp_authmechanism_t; typedef struct { @@ -95,79 +97,71 @@ typedef struct { git_cred *cred; git_cred *url_cred; git_cred *proxy_cred; - int auth_mechanism; + int auth_mechanisms; HINTERNET session; HINTERNET connection; } winhttp_subtransport; -static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred) +static int _apply_userpass_credential(HINTERNET request, DWORD target, DWORD scheme, git_cred *cred) { git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; wchar_t *user, *pass; - int error; + int user_len = 0, pass_len = 0, error = 0; - if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0) - return error; + if ((error = user_len = git__utf8_to_16_alloc(&user, c->username)) < 0) + goto done; - if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0) - return error; + if ((error = pass_len = git__utf8_to_16_alloc(&pass, c->password)) < 0) + goto done; - if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC, - user, pass, NULL)) { - giterr_set(GITERR_OS, "failed to set proxy auth"); + if (!WinHttpSetCredentials(request, target, scheme, user, pass, NULL)) { + giterr_set(GITERR_OS, "failed to set credentials"); error = -1; } +done: + if (user_len > 0) + git__memzero(user, user_len * sizeof(wchar_t)); + + if (pass_len > 0) + git__memzero(pass, pass_len * sizeof(wchar_t)); + git__free(user); git__free(pass); return error; } -static int apply_basic_credential(HINTERNET request, git_cred *cred) +static int apply_userpass_credential_proxy(HINTERNET request, git_cred *cred, int mechanisms) { - git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; - git_buf buf = GIT_BUF_INIT, raw = GIT_BUF_INIT; - wchar_t *wide = NULL; - int error = -1, wide_len; - - git_buf_printf(&raw, "%s:%s", c->username, c->password); - - if (git_buf_oom(&raw) || - git_buf_puts(&buf, "Authorization: Basic ") < 0 || - git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0) - goto on_error; - - if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) { - giterr_set(GITERR_OS, "Failed to convert string to wide form"); - goto on_error; + if (GIT_WINHTTP_AUTH_DIGEST & mechanisms) { + return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY, + WINHTTP_AUTH_SCHEME_DIGEST, cred); } - if (!WinHttpAddRequestHeaders(request, wide, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); - goto on_error; - } - - error = 0; - -on_error: - /* We were dealing with plaintext passwords, so clean up after ourselves a bit. */ - if (wide) - memset(wide, 0x0, wide_len * sizeof(wchar_t)); - - if (buf.size) - memset(buf.ptr, 0x0, buf.size); - - if (raw.size) - memset(raw.ptr, 0x0, raw.size); - - git__free(wide); - git_buf_free(&buf); - git_buf_free(&raw); - return error; + return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY, + WINHTTP_AUTH_SCHEME_BASIC, cred); } -static int apply_default_credentials(HINTERNET request) +static int apply_userpass_credential(HINTERNET request, int mechanisms, git_cred *cred) +{ + DWORD native_scheme; + + if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) || + (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE)) { + native_scheme = WINHTTP_AUTH_SCHEME_NTLM; + } else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) { + native_scheme = WINHTTP_AUTH_SCHEME_BASIC; + } else { + giterr_set(GITERR_NET, "invalid authentication scheme"); + return -1; + } + + return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_SERVER, + native_scheme, cred); +} + +static int apply_default_credentials(HINTERNET request, int mechanisms) { /* Either the caller explicitly requested that default credentials be passed, * or our fallback credential callback was invoked and checked that the target @@ -177,6 +171,12 @@ static int apply_default_credentials(HINTERNET request) * to Internet Explorer security zones, but in fact does not. */ DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW; + if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) == 0 && + (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) == 0) { + giterr_set(GITERR_NET, "invalid authentication scheme"); + return -1; + } + if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD))) return -1; @@ -202,7 +202,7 @@ static int fallback_cred_acquire_cb( /* Convert URL to wide characters */ if (git__utf8_to_16_alloc(&wide_url, url) < 0) { - giterr_set(GITERR_OS, "Failed to convert string to wide form"); + giterr_set(GITERR_OS, "failed to convert string to wide form"); return -1; } @@ -248,8 +248,12 @@ static int certificate_check(winhttp_stream *s, int valid) git_cert_x509 cert; /* If there is no override, we should fail if WinHTTP doesn't think it's fine */ - if (t->owner->certificate_check_cb == NULL && !valid) + if (t->owner->certificate_check_cb == NULL && !valid) { + if (!giterr_last()) + giterr_set(GITERR_NET, "unknown certificate check failure"); + return GIT_ECERTIFICATE; + } if (t->owner->certificate_check_cb == NULL || !t->connection_data.use_ssl) return 0; @@ -351,7 +355,7 @@ static int winhttp_stream_connect(winhttp_stream *s) /* Convert URL to wide characters */ if (git__utf8_to_16_alloc(&s->request_uri, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert string to wide form"); + giterr_set(GITERR_OS, "failed to convert string to wide form"); goto on_error; } @@ -366,12 +370,12 @@ static int winhttp_stream_connect(winhttp_stream *s) t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0); if (!s->request) { - giterr_set(GITERR_OS, "Failed to open request"); + giterr_set(GITERR_OS, "failed to open request"); goto on_error; } if (!WinHttpSetTimeouts(s->request, default_timeout, default_connect_timeout, default_timeout, default_timeout)) { - giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP"); + giterr_set(GITERR_OS, "failed to set timeouts for WinHTTP"); goto on_error; } @@ -425,7 +429,6 @@ static int winhttp_stream_connect(winhttp_stream *s) git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port); if (git_buf_oom(&processed_url)) { - giterr_set_oom(); error = -1; goto on_error; } @@ -444,7 +447,7 @@ static int winhttp_stream_connect(winhttp_stream *s) WINHTTP_OPTION_PROXY, &proxy_info, sizeof(WINHTTP_PROXY_INFO))) { - giterr_set(GITERR_OS, "Failed to set proxy"); + giterr_set(GITERR_OS, "failed to set proxy"); git__free(proxy_wide); goto on_error; } @@ -453,7 +456,7 @@ static int winhttp_stream_connect(winhttp_stream *s) if (t->proxy_cred) { if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) { - if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0) + if ((error = apply_userpass_credential_proxy(s->request, t->proxy_cred, t->auth_mechanisms)) < 0) goto on_error; } } @@ -467,7 +470,7 @@ static int winhttp_stream_connect(winhttp_stream *s) WINHTTP_OPTION_DISABLE_FEATURE, &disable_redirects, sizeof(disable_redirects))) { - giterr_set(GITERR_OS, "Failed to disable redirects"); + giterr_set(GITERR_OS, "failed to disable redirects"); goto on_error; } @@ -481,7 +484,7 @@ static int winhttp_stream_connect(winhttp_stream *s) /* Send Pragma: no-cache header */ if (!WinHttpAddRequestHeaders(s->request, pragma_nocache, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); goto on_error; } @@ -494,13 +497,13 @@ static int winhttp_stream_connect(winhttp_stream *s) goto on_error; if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert content-type to wide characters"); + giterr_set(GITERR_OS, "failed to convert content-type to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); goto on_error; } @@ -511,13 +514,13 @@ static int winhttp_stream_connect(winhttp_stream *s) goto on_error; if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert accept header to wide characters"); + giterr_set(GITERR_OS, "failed to convert accept header to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); goto on_error; } } @@ -527,13 +530,13 @@ static int winhttp_stream_connect(winhttp_stream *s) git_buf_clear(&buf); git_buf_puts(&buf, t->owner->custom_headers.strings[i]); if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert custom header to wide characters"); + giterr_set(GITERR_OS, "failed to convert custom header to wide characters"); goto on_error; } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); goto on_error; } } @@ -550,13 +553,11 @@ static int winhttp_stream_connect(winhttp_stream *s) /* If we have a credential on the subtransport, apply it to the request */ if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT && - t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC && - apply_basic_credential(s->request, t->cred) < 0) + apply_userpass_credential(s->request, t->auth_mechanisms, t->cred) < 0) goto on_error; else if (t->cred && t->cred->credtype == GIT_CREDTYPE_DEFAULT && - t->auth_mechanism == GIT_WINHTTP_AUTH_NEGOTIATE && - apply_default_credentials(s->request) < 0) + apply_default_credentials(s->request, t->auth_mechanisms) < 0) goto on_error; /* If no other credentials have been applied and the URL has username and @@ -565,7 +566,7 @@ static int winhttp_stream_connect(winhttp_stream *s) if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0) goto on_error; - if (apply_basic_credential(s->request, t->url_cred) < 0) + if (apply_userpass_credential(s->request, GIT_WINHTTP_AUTH_BASIC, t->url_cred) < 0) goto on_error; } @@ -585,30 +586,40 @@ on_error: static int parse_unauthorized_response( HINTERNET request, int *allowed_types, - int *auth_mechanism) + int *allowed_mechanisms) { DWORD supported, first, target; *allowed_types = 0; - *auth_mechanism = 0; + *allowed_mechanisms = 0; - /* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes(). - * We can assume this was already done, since we know we are unauthorized. + /* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes(). + * We can assume this was already done, since we know we are unauthorized. */ if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) { - giterr_set(GITERR_OS, "Failed to parse supported auth schemes"); + giterr_set(GITERR_OS, "failed to parse supported auth schemes"); return -1; } + if (WINHTTP_AUTH_SCHEME_NTLM & supported) { + *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; + *allowed_types |= GIT_CREDTYPE_DEFAULT; + *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE; + } + + if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) { + *allowed_types |= GIT_CREDTYPE_DEFAULT; + *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE; + } + if (WINHTTP_AUTH_SCHEME_BASIC & supported) { *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; - *auth_mechanism = GIT_WINHTTP_AUTH_BASIC; + *allowed_mechanisms |= GIT_WINHTTP_AUTH_BASIC; } - if ((WINHTTP_AUTH_SCHEME_NTLM & supported) || - (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported)) { - *allowed_types |= GIT_CREDTYPE_DEFAULT; - *auth_mechanism = GIT_WINHTTP_AUTH_NEGOTIATE; + if (WINHTTP_AUTH_SCHEME_DIGEST & supported) { + *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; + *allowed_mechanisms |= GIT_WINHTTP_AUTH_DIGEST; } return 0; @@ -629,7 +640,7 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) git_buf_cstr(&buf), (DWORD)git_buf_len(&buf), &bytes_written)) { git_buf_free(&buf); - giterr_set(GITERR_OS, "Failed to write chunk header"); + giterr_set(GITERR_OS, "failed to write chunk header"); return -1; } @@ -639,7 +650,7 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) if (!WinHttpWriteData(request, buffer, (DWORD)len, &bytes_written)) { - giterr_set(GITERR_OS, "Failed to write chunk"); + giterr_set(GITERR_OS, "failed to write chunk"); return -1; } @@ -647,7 +658,7 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) if (!WinHttpWriteData(request, "\r\n", 2, &bytes_written)) { - giterr_set(GITERR_OS, "Failed to write chunk footer"); + giterr_set(GITERR_OS, "failed to write chunk footer"); return -1; } @@ -660,7 +671,7 @@ static int winhttp_close_connection(winhttp_subtransport *t) if (t->connection) { if (!WinHttpCloseHandle(t->connection)) { - giterr_set(GITERR_OS, "Unable to close connection"); + giterr_set(GITERR_OS, "unable to close connection"); ret = -1; } @@ -669,7 +680,7 @@ static int winhttp_close_connection(winhttp_subtransport *t) if (t->session) { if (!WinHttpCloseHandle(t->session)) { - giterr_set(GITERR_OS, "Unable to close session"); + giterr_set(GITERR_OS, "unable to close session"); ret = -1; } @@ -694,6 +705,38 @@ static int user_agent(git_buf *ua) return git_buf_putc(ua, ')'); } +static void CALLBACK winhttp_status( + HINTERNET connection, + DWORD_PTR ctx, + DWORD code, + LPVOID info, + DWORD info_len) +{ + DWORD status; + + if (code != WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) + return; + + status = *((DWORD *)info); + + if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)) + giterr_set(GITERR_NET, "SSL certificate issued for different common name"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)) + giterr_set(GITERR_NET, "SSL certificate has expired"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)) + giterr_set(GITERR_NET, "SSL certificate signed by unknown CA"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)) + giterr_set(GITERR_NET, "SSL certificate is invalid"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)) + giterr_set(GITERR_NET, "certificate revocation check failed"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)) + giterr_set(GITERR_NET, "SSL certificate was revoked"); + else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)) + giterr_set(GITERR_NET, "security libraries could not be loaded"); + else + giterr_set(GITERR_NET, "unknown security error %d", status); +} + static int winhttp_connect( winhttp_subtransport *t) { @@ -714,7 +757,7 @@ static int winhttp_connect( /* Prepare host */ if (git__utf8_to_16_alloc(&wide_host, t->connection_data.host) < 0) { - giterr_set(GITERR_OS, "Unable to convert host to wide characters"); + giterr_set(GITERR_OS, "unable to convert host to wide characters"); return -1; } @@ -724,7 +767,7 @@ static int winhttp_connect( } if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) { - giterr_set(GITERR_OS, "Unable to convert host to wide characters"); + giterr_set(GITERR_OS, "unable to convert host to wide characters"); git__free(wide_host); git_buf_free(&ua); return -1; @@ -741,16 +784,16 @@ static int winhttp_connect( 0); if (!t->session) { - giterr_set(GITERR_OS, "Failed to init WinHTTP"); + giterr_set(GITERR_OS, "failed to init WinHTTP"); goto on_error; } if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) { - giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP"); + giterr_set(GITERR_OS, "failed to set timeouts for WinHTTP"); goto on_error; } - + /* Establish connection */ t->connection = WinHttpConnect( t->session, @@ -759,7 +802,12 @@ static int winhttp_connect( 0); if (!t->connection) { - giterr_set(GITERR_OS, "Failed to connect to host"); + giterr_set(GITERR_OS, "failed to connect to host"); + goto on_error; + } + + if (WinHttpSetStatusCallback(t->connection, winhttp_status, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE, 0) == WINHTTP_INVALID_STATUS_CALLBACK) { + giterr_set(GITERR_OS, "failed to set status callback"); goto on_error; } @@ -801,16 +849,15 @@ static int send_request(winhttp_stream *s, size_t len, int ignore_length) int request_failed = 0, cert_valid = 1, error = 0; DWORD ignore_flags; - if ((error = do_send_request(s, len, ignore_length)) < 0) - request_failed = 1; - - if (request_failed) { + giterr_clear(); + if ((error = do_send_request(s, len, ignore_length)) < 0) { if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) { giterr_set(GITERR_OS, "failed to send request"); return -1; - } else { - cert_valid = 0; } + + request_failed = 1; + cert_valid = 0; } giterr_clear(); @@ -826,14 +873,14 @@ static int send_request(winhttp_stream *s, size_t len, int ignore_length) return 0; ignore_flags = no_check_cert_flags; - + if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) { giterr_set(GITERR_OS, "failed to set security options"); return -1; } if ((error = do_send_request(s, len, ignore_length)) < 0) - giterr_set(GITERR_OS, "failed to send request"); + giterr_set(GITERR_OS, "failed to send request with unchecked certificate"); return error; } @@ -853,7 +900,7 @@ static int winhttp_stream_read( replay: /* Enforce a reasonable cap on the number of replays */ if (++replay_count >= 7) { - giterr_set(GITERR_NET, "Too many redirects or authentication replays"); + giterr_set(GITERR_NET, "too many redirects or authentication replays"); return -1; } @@ -888,7 +935,7 @@ replay: if (!WinHttpWriteData(s->request, "0\r\n\r\n", 5, &bytes_written)) { - giterr_set(GITERR_OS, "Failed to write final chunk"); + giterr_set(GITERR_OS, "failed to write final chunk"); return -1; } } @@ -899,7 +946,7 @@ replay: if (INVALID_SET_FILE_POINTER == SetFilePointer(s->post_body, 0, 0, FILE_BEGIN) && NO_ERROR != GetLastError()) { - giterr_set(GITERR_OS, "Failed to reset file pointer"); + giterr_set(GITERR_OS, "failed to reset file pointer"); return -1; } @@ -913,14 +960,14 @@ replay: &bytes_read, NULL) || !bytes_read) { git__free(buffer); - giterr_set(GITERR_OS, "Failed to read from temp file"); + giterr_set(GITERR_OS, "failed to read from temp file"); return -1; } if (!WinHttpWriteData(s->request, buffer, bytes_read, &bytes_written)) { git__free(buffer); - giterr_set(GITERR_OS, "Failed to write data"); + giterr_set(GITERR_OS, "failed to write data"); return -1; } @@ -936,7 +983,7 @@ replay: } if (!WinHttpReceiveResponse(s->request, 0)) { - giterr_set(GITERR_OS, "Failed to receive response"); + giterr_set(GITERR_OS, "failed to receive response"); return -1; } @@ -948,7 +995,7 @@ replay: WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_length, WINHTTP_NO_HEADER_INDEX)) { - giterr_set(GITERR_OS, "Failed to retrieve status code"); + giterr_set(GITERR_OS, "failed to retrieve status code"); return -1; } @@ -978,7 +1025,7 @@ replay: &location_length, WINHTTP_NO_HEADER_INDEX) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - giterr_set(GITERR_OS, "Failed to read Location header"); + giterr_set(GITERR_OS, "failed to read Location header"); return -1; } @@ -991,14 +1038,14 @@ replay: location, &location_length, WINHTTP_NO_HEADER_INDEX)) { - giterr_set(GITERR_OS, "Failed to read Location header"); + giterr_set(GITERR_OS, "failed to read Location header"); git__free(location); return -1; } /* Convert the Location header to UTF-8 */ if (git__utf16_to_8_alloc(&location8, location) < 0) { - giterr_set(GITERR_OS, "Failed to convert Location header to UTF-8"); + giterr_set(GITERR_OS, "failed to convert Location header to UTF-8"); git__free(location); return -1; } @@ -1029,13 +1076,13 @@ replay: if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) { int allowed_types; - if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0) + if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanisms) < 0) return -1; /* TODO: extract the username from the url, no payload? */ if (t->owner->proxy.credentials) { int cred_error = 1; - cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, NULL); + cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, t->owner->proxy.payload); if (cred_error < 0) return cred_error; @@ -1049,7 +1096,7 @@ replay: if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) { int allowed_types; - if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0) + if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanisms) < 0) return -1; if (allowed_types) { @@ -1090,7 +1137,7 @@ replay: } if (HTTP_STATUS_OK != status_code) { - giterr_set(GITERR_NET, "Request failed with status code: %d", status_code); + giterr_set(GITERR_NET, "request failed with status code: %d", status_code); return -1; } @@ -1101,7 +1148,7 @@ replay: p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service); if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) { - giterr_set(GITERR_OS, "Failed to convert expected content-type to wide characters"); + giterr_set(GITERR_OS, "failed to convert expected content-type to wide characters"); return -1; } @@ -1112,12 +1159,12 @@ replay: WINHTTP_HEADER_NAME_BY_INDEX, &content_type, &content_type_length, WINHTTP_NO_HEADER_INDEX)) { - giterr_set(GITERR_OS, "Failed to retrieve response content-type"); + giterr_set(GITERR_OS, "failed to retrieve response content-type"); return -1; } if (wcscmp(expected_content_type, content_type)) { - giterr_set(GITERR_NET, "Received unexpected content-type"); + giterr_set(GITERR_NET, "received unexpected content-type"); return -1; } @@ -1129,7 +1176,7 @@ replay: (DWORD)buf_size, &dw_bytes_read)) { - giterr_set(GITERR_OS, "Failed to read data"); + giterr_set(GITERR_OS, "failed to read data"); return -1; } @@ -1152,7 +1199,7 @@ static int winhttp_stream_write_single( /* This implementation of write permits only a single call. */ if (s->sent_request) { - giterr_set(GITERR_NET, "Subtransport configured for only one write"); + giterr_set(GITERR_NET, "subtransport configured for only one write"); return -1; } @@ -1165,7 +1212,7 @@ static int winhttp_stream_write_single( (LPCVOID)buffer, (DWORD)len, &bytes_written)) { - giterr_set(GITERR_OS, "Failed to write data"); + giterr_set(GITERR_OS, "failed to write data"); return -1; } @@ -1183,12 +1230,12 @@ static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch) if (RPC_S_OK != status && RPC_S_UUID_LOCAL_ONLY != status && RPC_S_UUID_NO_ADDRESS != status) { - giterr_set(GITERR_NET, "Unable to generate name for temp file"); + giterr_set(GITERR_NET, "unable to generate name for temp file"); return -1; } if (buffer_len_cch < UUID_LENGTH_CCH + 1) { - giterr_set(GITERR_NET, "Buffer too small for name of temp file"); + giterr_set(GITERR_NET, "buffer too small for name of temp file"); return -1; } @@ -1203,7 +1250,7 @@ static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch) uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]); if (result < UUID_LENGTH_CCH) { - giterr_set(GITERR_OS, "Unable to generate name for temp file"); + giterr_set(GITERR_OS, "unable to generate name for temp file"); return -1; } @@ -1215,7 +1262,7 @@ static int get_temp_file(LPWSTR buffer, DWORD buffer_len_cch) size_t len; if (!GetTempPathW(buffer_len_cch, buffer)) { - giterr_set(GITERR_OS, "Failed to get temp path"); + giterr_set(GITERR_OS, "failed to get temp path"); return -1; } @@ -1258,13 +1305,13 @@ static int winhttp_stream_write_buffered( if (INVALID_HANDLE_VALUE == s->post_body) { s->post_body = NULL; - giterr_set(GITERR_OS, "Failed to create temporary file"); + giterr_set(GITERR_OS, "failed to create temporary file"); return -1; } } if (!WriteFile(s->post_body, buffer, (DWORD)len, &bytes_written, NULL)) { - giterr_set(GITERR_OS, "Failed to write to temporary file"); + giterr_set(GITERR_OS, "failed to write to temporary file"); return -1; } @@ -1291,7 +1338,7 @@ static int winhttp_stream_write_chunked( if (!WinHttpAddRequestHeaders(s->request, transfer_encoding, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); + giterr_set(GITERR_OS, "failed to add a header to the request"); return -1; } diff --git a/src/tree-cache.c b/src/tree-cache.c index b37be0f0d..548054136 100644 --- a/src/tree-cache.c +++ b/src/tree-cache.c @@ -137,7 +137,7 @@ static int read_tree_internal(git_tree_cache **out, return 0; corrupted: - giterr_set(GITERR_INDEX, "Corrupted TREE extension in index"); + giterr_set(GITERR_INDEX, "corrupted TREE extension in index"); return -1; } @@ -149,7 +149,7 @@ int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer return -1; if (buffer < buffer_end) { - giterr_set(GITERR_INDEX, "Corrupted TREE extension in index (unexpected trailing data)"); + giterr_set(GITERR_INDEX, "corrupted TREE extension in index (unexpected trailing data)"); return -1; } diff --git a/src/tree.c b/src/tree.c index 9655ad739..6b1d1b238 100644 --- a/src/tree.c +++ b/src/tree.c @@ -20,8 +20,6 @@ #define TREE_ENTRY_CHECK_NAMELEN(n) \ if (n > UINT16_MAX) { giterr_set(GITERR_INVALID, "tree entry path too long"); } -GIT__USE_STRMAP - static bool valid_filemode(const int filemode) { return (filemode == GIT_FILEMODE_TREE @@ -505,7 +503,7 @@ static int append_entry( entry->attr = (uint16_t)filemode; - git_strmap_insert(bld->map, entry->filename, entry, error); + git_strmap_insert(bld->map, entry->filename, entry, &error); if (error < 0) { git_tree_entry_free(entry); giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename); @@ -520,7 +518,8 @@ static int write_tree( git_repository *repo, git_index *index, const char *dirname, - size_t start) + size_t start, + git_buf *shared_buf) { git_treebuilder *bld = NULL; size_t i, entries = git_index_entrycount(index); @@ -573,7 +572,7 @@ static int write_tree( GITERR_CHECK_ALLOC(subdir); /* Write out the subtree */ - written = write_tree(&sub_oid, repo, index, subdir, i); + written = write_tree(&sub_oid, repo, index, subdir, i, shared_buf); if (written < 0) { git__free(subdir); goto on_error; @@ -605,7 +604,7 @@ static int write_tree( } } - if (git_treebuilder_write(oid, bld) < 0) + if (git_treebuilder_write_with_buffer(oid, bld, shared_buf) < 0) goto on_error; git_treebuilder_free(bld); @@ -621,13 +620,14 @@ int git_tree__write_index( { int ret; git_tree *tree; + git_buf shared_buf = GIT_BUF_INIT; bool old_ignore_case = false; assert(oid && index && repo); if (git_index_has_conflicts(index)) { giterr_set(GITERR_INDEX, - "Cannot create a tree from a not fully merged index."); + "cannot create a tree from a not fully merged index."); return GIT_EUNMERGED; } @@ -646,7 +646,8 @@ int git_tree__write_index( git_index__set_ignore_case(index, false); } - ret = write_tree(oid, repo, index, "", 0); + ret = write_tree(oid, repo, index, "", 0, &shared_buf); + git_buf_free(&shared_buf); if (old_ignore_case) git_index__set_ignore_case(index, true); @@ -751,7 +752,7 @@ int git_treebuilder_insert( entry = alloc_entry(filename, strlen(filename), id); GITERR_CHECK_ALLOC(entry); - git_strmap_insert(bld->map, entry->filename, entry, error); + git_strmap_insert(bld->map, entry->filename, entry, &error); if (error < 0) { git_tree_entry_free(entry); @@ -801,47 +802,61 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename) } int git_treebuilder_write(git_oid *oid, git_treebuilder *bld) +{ + int error; + git_buf buffer = GIT_BUF_INIT; + + error = git_treebuilder_write_with_buffer(oid, bld, &buffer); + + git_buf_free(&buffer); + return error; +} + +int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *tree) { int error = 0; size_t i, entrycount; - git_buf tree = GIT_BUF_INIT; git_odb *odb; git_tree_entry *entry; - git_vector entries; + git_vector entries = GIT_VECTOR_INIT; assert(bld); + assert(tree); + + git_buf_clear(tree); entrycount = git_strmap_num_entries(bld->map); - if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0) - return -1; + if ((error = git_vector_init(&entries, entrycount, entry_sort_cmp)) < 0) + goto out; + + if (tree->asize == 0 && + (error = git_buf_grow(tree, entrycount * 72)) < 0) + goto out; git_strmap_foreach_value(bld->map, entry, { - if (git_vector_insert(&entries, entry) < 0) - return -1; + if ((error = git_vector_insert(&entries, entry)) < 0) + goto out; }); git_vector_sort(&entries); - /* Grow the buffer beforehand to an estimated size */ - error = git_buf_grow(&tree, entrycount * 72); - for (i = 0; i < entries.length && !error; ++i) { - git_tree_entry *entry = git_vector_get(&entries, i); + entry = git_vector_get(&entries, i); - git_buf_printf(&tree, "%o ", entry->attr); - git_buf_put(&tree, entry->filename, entry->filename_len + 1); - git_buf_put(&tree, (char *)entry->oid->id, GIT_OID_RAWSZ); + git_buf_printf(tree, "%o ", entry->attr); + git_buf_put(tree, entry->filename, entry->filename_len + 1); + git_buf_put(tree, (char *)entry->oid->id, GIT_OID_RAWSZ); - if (git_buf_oom(&tree)) + if (git_buf_oom(tree)) { error = -1; + goto out; + } } + if ((error = git_repository_odb__weakptr(&odb, bld->repo)) == 0) + error = git_odb_write(oid, odb, tree->ptr, tree->size, GIT_OBJ_TREE); - if (!error && - !(error = git_repository_odb__weakptr(&odb, bld->repo))) - error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE); - - git_buf_free(&tree); +out: git_vector_free(&entries); return error; @@ -909,7 +924,7 @@ int git_tree_entry_bypath( filename_len = subpath_len(path); if (filename_len == 0) { - giterr_set(GITERR_TREE, "Invalid tree path given"); + giterr_set(GITERR_TREE, "invalid tree path given"); return GIT_ENOTFOUND; } @@ -1027,7 +1042,7 @@ int git_tree_walk( git_buf root_path = GIT_BUF_INIT; if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) { - giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk"); + giterr_set(GITERR_INVALID, "invalid walking mode for tree walk"); return -1; } @@ -1237,7 +1252,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli const git_tree_entry *e = git_treebuilder_get(last->bld, basename); if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) { git__free(basename); - giterr_set(GITERR_TREE, "Cannot replace '%s' with '%s' at '%s'", + giterr_set(GITERR_TREE, "cannot replace '%s' with '%s' at '%s'", git_object_type2string(git_tree_entry_type(e)), git_object_type2string(git_object__type_from_filemode(update->filemode)), update->path); @@ -1257,7 +1272,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli break; } default: - giterr_set(GITERR_TREE, "unkown action for update"); + giterr_set(GITERR_TREE, "unknown action for update"); error = -1; goto cleanup; } diff --git a/src/unix/map.c b/src/unix/map.c index c55ad1aa7..9d9b1fe38 100644 --- a/src/unix/map.c +++ b/src/unix/map.c @@ -52,7 +52,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs out->data = mmap(NULL, len, mprot, mflag, fd, offset); if (!out->data || out->data == MAP_FAILED) { - giterr_set(GITERR_OS, "Failed to mmap. Could not write data"); + giterr_set(GITERR_OS, "failed to mmap. Could not write data"); return -1; } diff --git a/src/unix/posix.h b/src/unix/posix.h index ad13291e8..52985fd8a 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -40,9 +40,14 @@ typedef int GIT_SOCKET; #define p_link(o,n) link(o, n) #define p_unlink(p) unlink(p) #define p_mkdir(p,m) mkdir(p, m) -#define p_fsync(fd) fsync(fd) extern char *p_realpath(const char *, char *); +GIT_INLINE(int) p_fsync(int fd) +{ + p_fsync__cnt++; + return fsync(fd); +} + #define p_recv(s,b,l,f) recv(s,b,l,f) #define p_send(s,b,l,f) send(s,b,l,f) #define p_inet_pton(a, b, c) inet_pton(a, b, c) @@ -50,7 +55,7 @@ extern char *p_realpath(const char *, char *); #define p_strcasecmp(s1, s2) strcasecmp(s1, s2) #define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c) #define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a) -#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__) +#define p_snprintf(b, c, ...) snprintf(b, c, __VA_ARGS__) #define p_mkstemp(p) mkstemp(p) #define p_chdir(p) chdir(p) #define p_chmod(p,m) chmod(p, m) diff --git a/src/util.c b/src/util.c index 76ca711b2..a44f4c9ac 100644 --- a/src/util.c +++ b/src/util.c @@ -136,7 +136,7 @@ int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const cha Return: if (ndig == 0) { - giterr_set(GITERR_INVALID, "Failed to convert string to long. Not a number"); + giterr_set(GITERR_INVALID, "failed to convert string to long: not a number"); return -1; } @@ -144,7 +144,7 @@ Return: *endptr = p; if (ovfl) { - giterr_set(GITERR_INVALID, "Failed to convert string to long. Overflow error"); + giterr_set(GITERR_INVALID, "failed to convert string to long: overflow error"); return -1; } @@ -169,7 +169,7 @@ int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const cha tmp_int = tmp_long & 0xFFFFFFFF; if (tmp_int != tmp_long) { - giterr_set(GITERR_INVALID, "Failed to convert. '%s' is too large", nptr); + giterr_set(GITERR_INVALID, "failed to convert: '%s' is too large", nptr); return -1; } diff --git a/src/varint.c b/src/varint.c index 2f868607c..beac8c709 100644 --- a/src/varint.c +++ b/src/varint.c @@ -36,7 +36,7 @@ int git_encode_varint(unsigned char *buf, size_t bufsize, uintmax_t value) while (value >>= 7) varint[--pos] = 128 | (--value & 127); if (buf) { - if (bufsize < pos) + if (bufsize < (sizeof(varint) - pos)) return -1; memcpy(buf, varint + pos, sizeof(varint) - pos); } diff --git a/src/vector.c b/src/vector.c index baec8036f..620a1f56c 100644 --- a/src/vector.c +++ b/src/vector.c @@ -406,6 +406,9 @@ void git_vector_reverse(git_vector *v) { size_t a, b; + if (v->length == 0) + return; + a = 0; b = v->length - 1; diff --git a/src/win32/dir.c b/src/win32/dir.c index c15757085..8a724a4e9 100644 --- a/src/win32/dir.c +++ b/src/win32/dir.c @@ -28,7 +28,7 @@ git__DIR *git__opendir(const char *dir) new->h = FindFirstFileW(filter_w, &new->f); if (new->h == INVALID_HANDLE_VALUE) { - giterr_set(GITERR_OS, "Could not open directory '%s'", dir); + giterr_set(GITERR_OS, "could not open directory '%s'", dir); git__free(new); return NULL; } @@ -53,7 +53,7 @@ int git__readdir_ext( else if (!FindNextFileW(d->h, &d->f)) { if (GetLastError() == ERROR_NO_MORE_FILES) return 0; - giterr_set(GITERR_OS, "Could not read from directory '%s'", d->dir); + giterr_set(GITERR_OS, "could not read from directory '%s'", d->dir); return -1; } @@ -98,7 +98,7 @@ void git__rewinddir(git__DIR *d) d->h = FindFirstFileW(filter_w, &d->f); if (d->h == INVALID_HANDLE_VALUE) - giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir); + giterr_set(GITERR_OS, "could not open directory '%s'", d->dir); else d->first = 1; } diff --git a/src/win32/findfile.c b/src/win32/findfile.c index 58c22279e..1c768f7f4 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -38,7 +38,7 @@ static int win32_path_to_8(git_buf *dest, const wchar_t *src) git_win32_utf8_path utf8_path; if (git_win32_path_to_utf8(utf8_path, src) < 0) { - giterr_set(GITERR_OS, "Unable to convert path to UTF-8"); + giterr_set(GITERR_OS, "unable to convert path to UTF-8"); return -1; } diff --git a/src/win32/map.c b/src/win32/map.c index 03a3646a6..5fcc1085b 100644 --- a/src/win32/map.c +++ b/src/win32/map.c @@ -67,7 +67,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs if (fh == INVALID_HANDLE_VALUE) { errno = EBADF; - giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value"); + giterr_set(GITERR_OS, "failed to mmap. Invalid handle value"); return -1; } @@ -86,13 +86,13 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs if (page_offset != 0) { /* offset must be multiple of the allocation granularity */ errno = EINVAL; - giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of allocation granularity"); + giterr_set(GITERR_OS, "failed to mmap. Offset must be multiple of allocation granularity"); return -1; } out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL); if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) { - giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value"); + giterr_set(GITERR_OS, "failed to mmap. Invalid handle value"); out->fmh = NULL; return -1; } @@ -103,7 +103,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs off_hi = (DWORD)(page_start >> 32); out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len); if (!out->data) { - giterr_set(GITERR_OS, "Failed to mmap. No data written"); + giterr_set(GITERR_OS, "failed to mmap. No data written"); CloseHandle(out->fmh); out->fmh = NULL; return -1; @@ -121,7 +121,7 @@ int p_munmap(git_map *map) if (map->data) { if (!UnmapViewOfFile(map->data)) { - giterr_set(GITERR_OS, "Failed to munmap. Could not unmap view of file"); + giterr_set(GITERR_OS, "failed to munmap. Could not unmap view of file"); error = -1; } map->data = NULL; @@ -129,7 +129,7 @@ int p_munmap(git_map *map) if (map->fmh) { if (!CloseHandle(map->fmh)) { - giterr_set(GITERR_OS, "Failed to munmap. Could not close handle"); + giterr_set(GITERR_OS, "failed to munmap. Could not close handle"); error = -1; } map->fmh = NULL; diff --git a/src/win32/posix.h b/src/win32/posix.h index 73705fb2b..64769ecd3 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -14,6 +14,9 @@ #include "utf-conv.h" #include "dir.h" +extern unsigned long git_win32__createfile_sharemode; +extern int git_win32__retries; + typedef SOCKET GIT_SOCKET; #define p_lseek(f,n,w) _lseeki64(f, n, w) diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index fea634b00..e4fe4142c 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -26,15 +26,6 @@ #define IO_REPARSE_TAG_SYMLINK (0xA000000CL) #endif -/* Options which we always provide to _wopen. - * - * _O_BINARY - Raw access; no translation of CR or LF characters - * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes. - * The Windows default is 'not inheritable', but the CRT's default (following - * POSIX convention) is 'inheritable'. We have no desire for our handles to be - * inheritable on Windows, so specify the flag to get default behavior back. */ -#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT) - /* Allowable mode bits on Win32. Using mode bits that are not supported on * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it * so we simply remove them. @@ -44,6 +35,164 @@ /* GetFinalPathNameByHandleW signature */ typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD); +unsigned long git_win32__createfile_sharemode = + FILE_SHARE_READ | FILE_SHARE_WRITE; +int git_win32__retries = 10; + +GIT_INLINE(void) set_errno(void) +{ + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_NO_MORE_FILES: + case ERROR_BAD_NETPATH: + case ERROR_BAD_NET_NAME: + case ERROR_BAD_PATHNAME: + case ERROR_FILENAME_EXCED_RANGE: + errno = ENOENT; + break; + case ERROR_BAD_ENVIRONMENT: + errno = E2BIG; + break; + case ERROR_BAD_FORMAT: + case ERROR_INVALID_STARTING_CODESEG: + case ERROR_INVALID_STACKSEG: + case ERROR_INVALID_MODULETYPE: + case ERROR_INVALID_EXE_SIGNATURE: + case ERROR_EXE_MARKED_INVALID: + case ERROR_BAD_EXE_FORMAT: + case ERROR_ITERATED_DATA_EXCEEDS_64k: + case ERROR_INVALID_MINALLOCSIZE: + case ERROR_DYNLINK_FROM_INVALID_RING: + case ERROR_IOPL_NOT_ENABLED: + case ERROR_INVALID_SEGDPL: + case ERROR_AUTODATASEG_EXCEEDS_64k: + case ERROR_RING2SEG_MUST_BE_MOVABLE: + case ERROR_RELOC_CHAIN_XEEDS_SEGLIM: + case ERROR_INFLOOP_IN_RELOC_CHAIN: + errno = ENOEXEC; + break; + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_TARGET_HANDLE: + case ERROR_DIRECT_ACCESS_HANDLE: + errno = EBADF; + break; + case ERROR_WAIT_NO_CHILDREN: + case ERROR_CHILD_NOT_COMPLETE: + errno = ECHILD; + break; + case ERROR_NO_PROC_SLOTS: + case ERROR_MAX_THRDS_REACHED: + case ERROR_NESTING_NOT_ALLOWED: + errno = EAGAIN; + break; + case ERROR_ARENA_TRASHED: + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_INVALID_BLOCK: + case ERROR_NOT_ENOUGH_QUOTA: + errno = ENOMEM; + break; + case ERROR_ACCESS_DENIED: + case ERROR_CURRENT_DIRECTORY: + case ERROR_WRITE_PROTECT: + case ERROR_BAD_UNIT: + case ERROR_NOT_READY: + case ERROR_BAD_COMMAND: + case ERROR_CRC: + case ERROR_BAD_LENGTH: + case ERROR_SEEK: + case ERROR_NOT_DOS_DISK: + case ERROR_SECTOR_NOT_FOUND: + case ERROR_OUT_OF_PAPER: + case ERROR_WRITE_FAULT: + case ERROR_READ_FAULT: + case ERROR_GEN_FAILURE: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + case ERROR_WRONG_DISK: + case ERROR_SHARING_BUFFER_EXCEEDED: + case ERROR_NETWORK_ACCESS_DENIED: + case ERROR_CANNOT_MAKE: + case ERROR_FAIL_I24: + case ERROR_DRIVE_LOCKED: + case ERROR_SEEK_ON_DEVICE: + case ERROR_NOT_LOCKED: + case ERROR_LOCK_FAILED: + errno = EACCES; + break; + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + errno = EEXIST; + break; + case ERROR_NOT_SAME_DEVICE: + errno = EXDEV; + break; + case ERROR_INVALID_FUNCTION: + case ERROR_INVALID_ACCESS: + case ERROR_INVALID_DATA: + case ERROR_INVALID_PARAMETER: + case ERROR_NEGATIVE_SEEK: + errno = EINVAL; + break; + case ERROR_TOO_MANY_OPEN_FILES: + errno = EMFILE; + break; + case ERROR_DISK_FULL: + errno = ENOSPC; + break; + case ERROR_BROKEN_PIPE: + errno = EPIPE; + break; + case ERROR_DIR_NOT_EMPTY: + errno = ENOTEMPTY; + break; + default: + errno = EINVAL; + } +} + +GIT_INLINE(bool) last_error_retryable(void) +{ + int os_error = GetLastError(); + + return (os_error == ERROR_SHARING_VIOLATION || + os_error == ERROR_ACCESS_DENIED); +} + +#define do_with_retries(fn, remediation) \ + do { \ + int __tries, __ret; \ + for (__tries = 0; __tries < git_win32__retries; __tries++) { \ + if (__tries && (__ret = (remediation)) != 0) \ + return __ret; \ + if ((__ret = (fn)) != GIT_RETRY) \ + return __ret; \ + Sleep(5); \ + } \ + return -1; \ + } while (0) \ + +static int ensure_writable(wchar_t *path) +{ + DWORD attrs; + + if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES) + goto on_error; + + if ((attrs & FILE_ATTRIBUTE_READONLY) == 0) + return 0; + + if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY))) + goto on_error; + + return 0; + +on_error: + set_errno(); + return -1; +} + /** * Truncate or extend file. * @@ -89,30 +238,34 @@ int p_link(const char *old, const char *new) return -1; } +GIT_INLINE(int) unlink_once(const wchar_t *path) +{ + if (DeleteFileW(path)) + return 0; + + if (last_error_retryable()) + return GIT_RETRY; + + set_errno(); + return -1; +} + int p_unlink(const char *path) { - git_win32_path buf; - int error; + git_win32_path wpath; - if (git_win32_path_from_utf8(buf, path) < 0) + if (git_win32_path_from_utf8(wpath, path) < 0) return -1; - error = _wunlink(buf); - - /* If the file could not be deleted because it was - * read-only, clear the bit and try again */ - if (error == -1 && errno == EACCES) { - _wchmod(buf, 0666); - error = _wunlink(buf); - } - - return error; + do_with_retries(unlink_once(wpath), ensure_writable(wpath)); } int p_fsync(int fd) { HANDLE fh = (HANDLE)_get_osfhandle(fd); + p_fsync__cnt++; + if (fh == INVALID_HANDLE_VALUE) { errno = EBADF; return -1; @@ -210,44 +363,6 @@ int p_lstat_posixly(const char *filename, struct stat *buf) return do_lstat(filename, buf, true); } -int p_utimes(const char *filename, const struct p_timeval times[2]) -{ - int fd, error; - - if ((fd = p_open(filename, O_RDWR)) < 0) - return fd; - - error = p_futimes(fd, times); - - close(fd); - return error; -} - -int p_futimes(int fd, const struct p_timeval times[2]) -{ - HANDLE handle; - FILETIME atime = {0}, mtime = {0}; - - if (times == NULL) { - SYSTEMTIME st; - - GetSystemTime(&st); - SystemTimeToFileTime(&st, &atime); - SystemTimeToFileTime(&st, &mtime); - } else { - git_win32__timeval_to_filetime(&atime, times[0]); - git_win32__timeval_to_filetime(&mtime, times[1]); - } - - if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE) - return -1; - - if (SetFileTime(handle, NULL, &atime, &mtime) == 0) - return -1; - - return 0; -} - int p_readlink(const char *path, char *buf, size_t bufsiz) { git_win32_path path_w, target_w; @@ -280,12 +395,91 @@ int p_symlink(const char *old, const char *new) return git_futils_fake_symlink(old, new); } +struct open_opts { + DWORD access; + DWORD sharing; + SECURITY_ATTRIBUTES security; + DWORD creation_disposition; + DWORD attributes; + int osf_flags; +}; + +GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode) +{ + memset(opts, 0, sizeof(struct open_opts)); + + switch (flags & (O_WRONLY | O_RDWR)) { + case O_WRONLY: + opts->access = GENERIC_WRITE; + break; + case O_RDWR: + opts->access = GENERIC_READ | GENERIC_WRITE; + break; + default: + opts->access = GENERIC_READ; + break; + } + + opts->sharing = (DWORD)git_win32__createfile_sharemode; + + switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) { + case O_CREAT | O_EXCL: + case O_CREAT | O_TRUNC | O_EXCL: + opts->creation_disposition = CREATE_NEW; + break; + case O_CREAT | O_TRUNC: + opts->creation_disposition = CREATE_ALWAYS; + break; + case O_TRUNC: + opts->creation_disposition = TRUNCATE_EXISTING; + break; + case O_CREAT: + opts->creation_disposition = OPEN_ALWAYS; + break; + default: + opts->creation_disposition = OPEN_EXISTING; + break; + } + + opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ? + FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL; + opts->osf_flags = flags & (O_RDONLY | O_APPEND); + + opts->security.nLength = sizeof(SECURITY_ATTRIBUTES); + opts->security.lpSecurityDescriptor = NULL; + opts->security.bInheritHandle = 0; +} + +GIT_INLINE(int) open_once( + const wchar_t *path, + struct open_opts *opts) +{ + int fd; + + HANDLE handle = CreateFileW(path, opts->access, opts->sharing, + &opts->security, opts->creation_disposition, opts->attributes, 0); + + if (handle == INVALID_HANDLE_VALUE) { + if (last_error_retryable()) + return GIT_RETRY; + + set_errno(); + return -1; + } + + if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0) + CloseHandle(handle); + + return fd; +} + int p_open(const char *path, int flags, ...) { - git_win32_path buf; + git_win32_path wpath; mode_t mode = 0; + struct open_opts opts = {0}; - if (git_win32_path_from_utf8(buf, path) < 0) + if (git_win32_path_from_utf8(wpath, path) < 0) return -1; if (flags & O_CREAT) { @@ -296,19 +490,83 @@ int p_open(const char *path, int flags, ...) va_end(arg_list); } - return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode & WIN32_MODE_MASK); + open_opts_from_posix(&opts, flags, mode); + + do_with_retries( + open_once(wpath, &opts), + 0); } int p_creat(const char *path, mode_t mode) { - git_win32_path buf; + return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode); +} - if (git_win32_path_from_utf8(buf, path) < 0) +int p_utimes(const char *path, const struct p_timeval times[2]) +{ + git_win32_path wpath; + int fd, error; + DWORD attrs_orig, attrs_new = 0; + struct open_opts opts = { 0 }; + + if (git_win32_path_from_utf8(wpath, path) < 0) return -1; - return _wopen(buf, - _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS, - mode & WIN32_MODE_MASK); + attrs_orig = GetFileAttributesW(wpath); + + if (attrs_orig & FILE_ATTRIBUTE_READONLY) { + attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY; + + if (!SetFileAttributesW(wpath, attrs_new)) { + giterr_set(GITERR_OS, "failed to set attributes"); + return -1; + } + } + + open_opts_from_posix(&opts, O_RDWR, 0); + + if ((fd = open_once(wpath, &opts)) < 0) { + error = -1; + goto done; + } + + error = p_futimes(fd, times); + close(fd); + +done: + if (attrs_orig != attrs_new) { + DWORD os_error = GetLastError(); + SetFileAttributesW(wpath, attrs_orig); + SetLastError(os_error); + } + + return error; +} + +int p_futimes(int fd, const struct p_timeval times[2]) +{ + HANDLE handle; + FILETIME atime = { 0 }, mtime = { 0 }; + + if (times == NULL) { + SYSTEMTIME st; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &atime); + SystemTimeToFileTime(&st, &mtime); + } + else { + git_win32__timeval_to_filetime(&atime, times[0]); + git_win32__timeval_to_filetime(&mtime, times[1]); + } + + if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE) + return -1; + + if (SetFileTime(handle, NULL, &atime, &mtime) == 0) + return -1; + + return 0; } int p_getcwd(char *buffer_out, size_t size) @@ -581,62 +839,27 @@ int p_access(const char* path, mode_t mode) return _waccess(buf, mode & WIN32_MODE_MASK); } -static int ensure_writable(wchar_t *fpath) +GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to) { - DWORD attrs; - - attrs = GetFileAttributesW(fpath); - if (attrs == INVALID_FILE_ATTRIBUTES) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) - return 0; - - giterr_set(GITERR_OS, "failed to get attributes"); - return -1; - } - - if (!(attrs & FILE_ATTRIBUTE_READONLY)) + if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) return 0; - attrs &= ~FILE_ATTRIBUTE_READONLY; - if (!SetFileAttributesW(fpath, attrs)) { - giterr_set(GITERR_OS, "failed to set attributes"); - return -1; - } + if (last_error_retryable()) + return GIT_RETRY; - return 0; + set_errno(); + return -1; } int p_rename(const char *from, const char *to) { - git_win32_path wfrom; - git_win32_path wto; - int rename_tries; - int rename_succeeded; - int error; + git_win32_path wfrom, wto; if (git_win32_path_from_utf8(wfrom, from) < 0 || git_win32_path_from_utf8(wto, to) < 0) return -1; - /* wait up to 50ms if file is locked by another thread or process */ - rename_tries = 0; - rename_succeeded = 0; - while (rename_tries < 10) { - if (ensure_writable(wto) == 0 && - MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) { - rename_succeeded = 1; - break; - } - - error = GetLastError(); - if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) { - Sleep(5); - rename_tries++; - } else - break; - } - - return rename_succeeded ? 0 : -1; + do_with_retries(rename_once(wfrom, wto), ensure_writable(wto)); } int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags) diff --git a/src/win32/w32_crtdbg_stacktrace.c b/src/win32/w32_crtdbg_stacktrace.c index a778f4164..2dbdaf45b 100644 --- a/src/win32/w32_crtdbg_stacktrace.c +++ b/src/win32/w32_crtdbg_stacktrace.c @@ -253,11 +253,11 @@ int git_win32__crtdbg_stacktrace__dump( bool b_quiet = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET); if (b_leaks_since_mark && b_leaks_total) { - giterr_set(GITERR_INVALID, "Cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL."); + giterr_set(GITERR_INVALID, "cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL."); return GIT_ERROR; } if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) { - giterr_set(GITERR_INVALID, "Nothing to do."); + giterr_set(GITERR_INVALID, "nothing to do."); return GIT_ERROR; } diff --git a/src/win32/w32_util.c b/src/win32/w32_util.c index 60311bb50..b7b1ffa10 100644 --- a/src/win32/w32_util.c +++ b/src/win32/w32_util.c @@ -68,7 +68,7 @@ int git_win32__set_hidden(const char *path, bool hidden) newattrs = attrs & ~FILE_ATTRIBUTE_HIDDEN; if (attrs != newattrs && !SetFileAttributesW(buf, newattrs)) { - giterr_set(GITERR_OS, "Failed to %s hidden bit for '%s'", + giterr_set(GITERR_OS, "failed to %s hidden bit for '%s'", hidden ? "set" : "unset", path); return -1; } diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index 784a7a04c..77973b502 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -174,7 +174,7 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat( /* st_size gets the UTF-8 length of the target name, in bytes, * not counting the NULL terminator */ if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) { - giterr_set(GITERR_OS, "Could not convert reparse point name for '%ls'", path); + giterr_set(GITERR_OS, "could not convert reparse point name for '%ls'", path); return -1; } } diff --git a/src/worktree.c b/src/worktree.c new file mode 100644 index 000000000..ede155b69 --- /dev/null +++ b/src/worktree.c @@ -0,0 +1,558 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "common.h" + +#include "git2/branch.h" +#include "git2/commit.h" +#include "git2/worktree.h" + +#include "repository.h" +#include "worktree.h" + +static bool is_worktree_dir(const char *dir) +{ + git_buf buf = GIT_BUF_INIT; + int error; + + if (git_buf_sets(&buf, dir) < 0) + return -1; + + error = git_path_contains_file(&buf, "commondir") + && git_path_contains_file(&buf, "gitdir") + && git_path_contains_file(&buf, "HEAD"); + + git_buf_free(&buf); + return error; +} + +int git_worktree_list(git_strarray *wts, git_repository *repo) +{ + git_vector worktrees = GIT_VECTOR_INIT; + git_buf path = GIT_BUF_INIT; + char *worktree; + unsigned i, len; + int error; + + assert(wts && repo); + + wts->count = 0; + wts->strings = NULL; + + if ((error = git_buf_printf(&path, "%s/worktrees/", repo->commondir)) < 0) + goto exit; + if (!git_path_exists(path.ptr) || git_path_is_empty_dir(path.ptr)) + goto exit; + if ((error = git_path_dirload(&worktrees, path.ptr, path.size, 0x0)) < 0) + goto exit; + + len = path.size; + + git_vector_foreach(&worktrees, i, worktree) { + git_buf_truncate(&path, len); + git_buf_puts(&path, worktree); + + if (!is_worktree_dir(path.ptr)) { + git_vector_remove(&worktrees, i); + git__free(worktree); + } + } + + wts->strings = (char **)git_vector_detach(&wts->count, NULL, &worktrees); + +exit: + git_buf_free(&path); + + return error; +} + +char *git_worktree__read_link(const char *base, const char *file) +{ + git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT; + + assert(base && file); + + if (git_buf_joinpath(&path, base, file) < 0) + goto err; + if (git_futils_readbuffer(&buf, path.ptr) < 0) + goto err; + git_buf_free(&path); + + git_buf_rtrim(&buf); + + if (!git_path_is_relative(buf.ptr)) + return git_buf_detach(&buf); + + if (git_buf_sets(&path, base) < 0) + goto err; + if (git_path_apply_relative(&path, buf.ptr) < 0) + goto err; + git_buf_free(&buf); + + return git_buf_detach(&path); + +err: + git_buf_free(&buf); + git_buf_free(&path); + + return NULL; +} + +static int write_wtfile(const char *base, const char *file, const git_buf *buf) +{ + git_buf path = GIT_BUF_INIT; + int err; + + assert(base && file && buf); + + if ((err = git_buf_joinpath(&path, base, file)) < 0) + goto out; + + if ((err = git_futils_writebuffer(buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0) + goto out; + +out: + git_buf_free(&path); + + return err; +} + +static int open_worktree_dir(git_worktree **out, const char *parent, const char *dir, const char *name) +{ + git_buf gitdir = GIT_BUF_INIT; + git_worktree *wt = NULL; + int error = 0; + + if (!is_worktree_dir(dir)) { + error = -1; + goto out; + } + + if ((wt = git__calloc(1, sizeof(struct git_repository))) == NULL) { + error = -1; + goto out; + } + + if ((wt->name = git__strdup(name)) == NULL + || (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL + || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL + || (wt->parent_path = git__strdup(parent)) == NULL) { + error = -1; + goto out; + } + + if ((error = git_path_prettify_dir(&gitdir, dir, NULL)) < 0) + goto out; + wt->gitdir_path = git_buf_detach(&gitdir); + + wt->locked = !!git_worktree_is_locked(NULL, wt); + + *out = wt; + +out: + if (error) + git_worktree_free(wt); + git_buf_free(&gitdir); + + return error; +} + +int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name) +{ + git_buf path = GIT_BUF_INIT; + git_worktree *wt = NULL; + int error; + + assert(repo && name); + + *out = NULL; + + if ((error = git_buf_printf(&path, "%s/worktrees/%s", repo->commondir, name)) < 0) + goto out; + + if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0) + goto out; + +out: + git_buf_free(&path); + + if (error) + git_worktree_free(wt); + + return error; +} + +int git_worktree_open_from_repository(git_worktree **out, git_repository *repo) +{ + git_buf parent = GIT_BUF_INIT; + const char *gitdir, *commondir; + char *name = NULL; + int error = 0; + + if (!git_repository_is_worktree(repo)) { + giterr_set(GITERR_WORKTREE, "cannot open worktree of a non-worktree repo"); + error = -1; + goto out; + } + + gitdir = git_repository_path(repo); + commondir = git_repository_commondir(repo); + + if ((error = git_path_prettify_dir(&parent, "..", commondir)) < 0) + goto out; + + /* The name is defined by the last component in '.git/worktree/%s' */ + name = git_path_basename(gitdir); + + if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0) + goto out; + +out: + git__free(name); + git_buf_free(&parent); + + return error; +} + +void git_worktree_free(git_worktree *wt) +{ + if (!wt) + return; + + git__free(wt->commondir_path); + git__free(wt->gitlink_path); + git__free(wt->gitdir_path); + git__free(wt->parent_path); + git__free(wt->name); + git__free(wt); +} + +int git_worktree_validate(const git_worktree *wt) +{ + git_buf buf = GIT_BUF_INIT; + int err = 0; + + assert(wt); + + git_buf_puts(&buf, wt->gitdir_path); + if (!is_worktree_dir(buf.ptr)) { + giterr_set(GITERR_WORKTREE, + "Worktree gitdir ('%s') is not valid", + wt->gitlink_path); + err = -1; + goto out; + } + + if (!git_path_exists(wt->parent_path)) { + giterr_set(GITERR_WORKTREE, + "Worktree parent directory ('%s') does not exist ", + wt->parent_path); + err = -2; + goto out; + } + + if (!git_path_exists(wt->commondir_path)) { + giterr_set(GITERR_WORKTREE, + "Worktree common directory ('%s') does not exist ", + wt->commondir_path); + err = -3; + goto out; + } + +out: + git_buf_free(&buf); + + return err; +} + +int git_worktree_add_init_options(git_worktree_add_options *opts, + unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, + git_worktree_add_options, GIT_WORKTREE_ADD_OPTIONS_INIT); + return 0; +} + +int git_worktree_add(git_worktree **out, git_repository *repo, + const char *name, const char *worktree, + const git_worktree_add_options *opts) +{ + git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT, buf = GIT_BUF_INIT; + git_reference *ref = NULL, *head = NULL; + git_commit *commit = NULL; + git_repository *wt = NULL; + git_checkout_options coopts = GIT_CHECKOUT_OPTIONS_INIT; + git_worktree_add_options wtopts = GIT_WORKTREE_ADD_OPTIONS_INIT; + int err; + + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_ADD_OPTIONS_VERSION, "git_worktree_add_options"); + + if (opts) + memcpy(&wtopts, opts, sizeof(wtopts)); + + assert(out && repo && name && worktree); + + *out = NULL; + + /* Create gitdir directory ".git/worktrees/" */ + if ((err = git_buf_joinpath(&gitdir, repo->commondir, "worktrees")) < 0) + goto out; + if (!git_path_exists(gitdir.ptr)) + if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) + goto out; + if ((err = git_buf_joinpath(&gitdir, gitdir.ptr, name)) < 0) + goto out; + if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) + goto out; + if ((err = git_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0) + goto out; + + /* Create worktree work dir */ + if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0) + goto out; + if ((err = git_path_prettify_dir(&wddir, worktree, NULL)) < 0) + goto out; + + if (wtopts.lock) { + int fd; + + if ((err = git_buf_joinpath(&buf, gitdir.ptr, "locked")) < 0) + goto out; + + if ((fd = p_creat(buf.ptr, 0644)) < 0) { + err = fd; + goto out; + } + + p_close(fd); + git_buf_clear(&buf); + } + + /* Create worktree .git file */ + if ((err = git_buf_printf(&buf, "gitdir: %s\n", gitdir.ptr)) < 0) + goto out; + if ((err = write_wtfile(wddir.ptr, ".git", &buf)) < 0) + goto out; + + /* Create gitdir files */ + if ((err = git_path_prettify_dir(&buf, repo->commondir, NULL) < 0) + || (err = git_buf_putc(&buf, '\n')) < 0 + || (err = write_wtfile(gitdir.ptr, "commondir", &buf)) < 0) + goto out; + if ((err = git_buf_joinpath(&buf, wddir.ptr, ".git")) < 0 + || (err = git_buf_putc(&buf, '\n')) < 0 + || (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0) + goto out; + + /* Create new branch */ + if ((err = git_repository_head(&head, repo)) < 0) + goto out; + if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0) + goto out; + if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0) + goto out; + + /* Set worktree's HEAD */ + if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0) + goto out; + if ((err = git_repository_open(&wt, wddir.ptr)) < 0) + goto out; + + /* Checkout worktree's HEAD */ + coopts.checkout_strategy = GIT_CHECKOUT_FORCE; + if ((err = git_checkout_head(wt, &coopts)) < 0) + goto out; + + /* Load result */ + if ((err = git_worktree_lookup(out, repo, name)) < 0) + goto out; + +out: + git_buf_free(&gitdir); + git_buf_free(&wddir); + git_buf_free(&buf); + git_reference_free(ref); + git_reference_free(head); + git_commit_free(commit); + git_repository_free(wt); + + return err; +} + +int git_worktree_lock(git_worktree *wt, char *creason) +{ + git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + int err; + + assert(wt); + + if ((err = git_worktree_is_locked(NULL, wt)) < 0) + goto out; + + if ((err = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) + goto out; + + if (creason) + git_buf_attach_notowned(&buf, creason, strlen(creason)); + + if ((err = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0) + goto out; + + wt->locked = 1; + +out: + git_buf_free(&path); + + return err; +} + +int git_worktree_unlock(git_worktree *wt) +{ + git_buf path = GIT_BUF_INIT; + + assert(wt); + + if (!git_worktree_is_locked(NULL, wt)) + return 0; + + if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0) + return -1; + + if (p_unlink(path.ptr) != 0) { + git_buf_free(&path); + return -1; + } + + wt->locked = 0; + + git_buf_free(&path); + + return 0; +} + +int git_worktree_is_locked(git_buf *reason, const git_worktree *wt) +{ + git_buf path = GIT_BUF_INIT; + int ret; + + assert(wt); + + if (reason) + git_buf_clear(reason); + + if ((ret = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) + goto out; + if ((ret = git_path_exists(path.ptr)) && reason) + git_futils_readbuffer(reason, path.ptr); + +out: + git_buf_free(&path); + + return ret; +} + +int git_worktree_prune_init_options( + git_worktree_prune_options *opts, + unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, + git_worktree_prune_options, GIT_WORKTREE_PRUNE_OPTIONS_INIT); + return 0; +} + +int git_worktree_is_prunable(git_worktree *wt, + git_worktree_prune_options *opts) +{ + git_buf reason = GIT_BUF_INIT; + git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, + "git_worktree_prune_options"); + + if (opts) + memcpy(&popts, opts, sizeof(popts)); + + if ((popts.flags & GIT_WORKTREE_PRUNE_LOCKED) == 0 && + git_worktree_is_locked(&reason, wt)) + { + if (!reason.size) + git_buf_attach_notowned(&reason, "no reason given", 15); + giterr_set(GITERR_WORKTREE, "Not pruning locked working tree: '%s'", reason.ptr); + git_buf_free(&reason); + + return 0; + } + + if ((popts.flags & GIT_WORKTREE_PRUNE_VALID) == 0 && + git_worktree_validate(wt) == 0) + { + giterr_set(GITERR_WORKTREE, "Not pruning valid working tree"); + return 0; + } + + return 1; +} + +int git_worktree_prune(git_worktree *wt, + git_worktree_prune_options *opts) +{ + git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + git_buf path = GIT_BUF_INIT; + char *wtpath; + int err; + + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, + "git_worktree_prune_options"); + + if (opts) + memcpy(&popts, opts, sizeof(popts)); + + if (!git_worktree_is_prunable(wt, &popts)) { + err = -1; + goto out; + } + + /* Delete gitdir in parent repository */ + if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name)) < 0) + goto out; + if (!git_path_exists(path.ptr)) + { + giterr_set(GITERR_WORKTREE, "Worktree gitdir '%s' does not exist", path.ptr); + err = -1; + goto out; + } + if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0) + goto out; + + /* Skip deletion of the actual working tree if it does + * not exist or deletion was not requested */ + if ((popts.flags & GIT_WORKTREE_PRUNE_WORKING_TREE) == 0 || + !git_path_exists(wt->gitlink_path)) + { + goto out; + } + + if ((wtpath = git_path_dirname(wt->gitlink_path)) == NULL) + goto out; + git_buf_attach(&path, wtpath, 0); + if (!git_path_exists(path.ptr)) + { + giterr_set(GITERR_WORKTREE, "Working tree '%s' does not exist", path.ptr); + err = -1; + goto out; + } + if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0) + goto out; + +out: + git_buf_free(&path); + + return err; +} diff --git a/src/worktree.h b/src/worktree.h new file mode 100644 index 000000000..57c2e65f0 --- /dev/null +++ b/src/worktree.h @@ -0,0 +1,35 @@ +/* + * 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_worktree_h__ +#define INCLUDE_worktree_h__ + +#include "git2/common.h" +#include "git2/worktree.h" + +struct git_worktree { + /* Name of the working tree. This is the name of the + * containing directory in the `$PARENT/.git/worktrees/` + * directory. */ + char *name; + + /* Path to the .git file in the working tree's repository */ + char *gitlink_path; + /* Path to the .git directory inside the parent's + * worktrees directory */ + char *gitdir_path; + /* Path to the common directory contained in the parent + * repository */ + char *commondir_path; + /* Path to the parent's working directory */ + char *parent_path; + + int locked:1; +}; + +char *git_worktree__read_link(const char *base, const char *file); + +#endif diff --git a/src/zstream.c b/src/zstream.c index d949aa81a..141b49b27 100644 --- a/src/zstream.c +++ b/src/zstream.c @@ -23,7 +23,7 @@ static int zstream_seterr(git_zstream *zs) else if (zs->z.msg) giterr_set_str(GITERR_ZLIB, zs->z.msg); else - giterr_set(GITERR_ZLIB, "Unknown compression error"); + giterr_set(GITERR_ZLIB, "unknown compression error"); return -1; } diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c index f1fe1c71f..a089ee408 100644 --- a/tests/attr/ignore.c +++ b/tests/attr/ignore.c @@ -21,8 +21,8 @@ static void assert_is_ignored_( { int is_ignored = 0; - cl_git_pass_( - git_ignore_path_is_ignored(&is_ignored, g_repo, filepath), file, line); + cl_git_expect( + git_ignore_path_is_ignored(&is_ignored, g_repo, filepath), 0, file, line); clar__assert_equal( file, line, "expected != is_ignored", 1, "%d", @@ -291,3 +291,15 @@ void test_attr_ignore__symlink_to_outside(void) assert_is_ignored(true, "symlink"); assert_is_ignored(true, "lala/../symlink"); } + +void test_attr_ignore__test(void) +{ + cl_git_rewritefile("attr/.gitignore", + "/*/\n" + "!/src\n"); + assert_is_ignored(false, "src/foo.c"); + assert_is_ignored(false, "src/foo/foo.c"); + assert_is_ignored(false, "README.md"); + assert_is_ignored(true, "dist/foo.o"); + assert_is_ignored(true, "bin/foo"); +} diff --git a/tests/checkout/head.c b/tests/checkout/head.c index 07cc1d209..ded86df33 100644 --- a/tests/checkout/head.c +++ b/tests/checkout/head.c @@ -38,7 +38,7 @@ void test_checkout_head__with_index_only_tree(void) cl_git_pass(git_repository_index(&index, g_repo)); p_mkdir("testrepo/newdir", 0777); - cl_git_mkfile("testrepo/newdir/newfile.txt", "new file\n"); + cl_git_mkfile("testrepo/newdir/newfile.txt", "new file\n"); cl_git_pass(git_index_add_bypath(index, "newdir/newfile.txt")); cl_git_pass(git_index_write(index)); @@ -60,3 +60,79 @@ void test_checkout_head__with_index_only_tree(void) git_index_free(index); } + +void test_checkout_head__do_not_remove_untracked_file(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_index *index; + + cl_git_pass(p_mkdir("testrepo/tracked", 0755)); + cl_git_mkfile("testrepo/tracked/tracked", "tracked\n"); + cl_git_mkfile("testrepo/tracked/untracked", "untracked\n"); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_add_bypath(index, "tracked/tracked")); + cl_git_pass(git_index_write(index)); + + git_index_free(index); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + cl_git_pass(git_checkout_head(g_repo, &opts)); + + cl_assert(!git_path_isfile("testrepo/tracked/tracked")); + cl_assert(git_path_isfile("testrepo/tracked/untracked")); +} + +void test_checkout_head__do_not_remove_untracked_file_in_subdir(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_index *index; + + cl_git_pass(p_mkdir("testrepo/tracked", 0755)); + cl_git_pass(p_mkdir("testrepo/tracked/subdir", 0755)); + cl_git_mkfile("testrepo/tracked/tracked", "tracked\n"); + cl_git_mkfile("testrepo/tracked/subdir/tracked", "tracked\n"); + cl_git_mkfile("testrepo/tracked/subdir/untracked", "untracked\n"); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_add_bypath(index, "tracked/tracked")); + cl_git_pass(git_index_add_bypath(index, "tracked/subdir/tracked")); + cl_git_pass(git_index_write(index)); + + git_index_free(index); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + cl_git_pass(git_checkout_head(g_repo, &opts)); + + cl_assert(!git_path_isfile("testrepo/tracked/tracked")); + cl_assert(!git_path_isfile("testrepo/tracked/subdir/tracked")); + cl_assert(git_path_isfile("testrepo/tracked/subdir/untracked")); +} + +void test_checkout_head__do_remove_tracked_subdir(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_index *index; + + cl_git_pass(p_mkdir("testrepo/subdir", 0755)); + cl_git_pass(p_mkdir("testrepo/subdir/tracked", 0755)); + cl_git_mkfile("testrepo/subdir/tracked-file", "tracked\n"); + cl_git_mkfile("testrepo/subdir/untracked-file", "untracked\n"); + cl_git_mkfile("testrepo/subdir/tracked/tracked1", "tracked\n"); + cl_git_mkfile("testrepo/subdir/tracked/tracked2", "tracked\n"); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_add_bypath(index, "subdir/tracked-file")); + cl_git_pass(git_index_add_bypath(index, "subdir/tracked/tracked1")); + cl_git_pass(git_index_add_bypath(index, "subdir/tracked/tracked2")); + cl_git_pass(git_index_write(index)); + + git_index_free(index); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + cl_git_pass(git_checkout_head(g_repo, &opts)); + + cl_assert(!git_path_isdir("testrepo/subdir/tracked")); + cl_assert(!git_path_isfile("testrepo/subdir/tracked-file")); + cl_assert(git_path_isfile("testrepo/subdir/untracked-file")); +} diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 4a0314a9e..b3b860c63 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -422,6 +422,41 @@ void test_checkout_tree__can_checkout_with_pattern(void) cl_assert(git_path_exists("testrepo/new.txt")); } +void test_checkout_tree__pathlist_checkout_ignores_non_matches(void) +{ + char *entries[] = { "branch_file.txt", "link_to_new.txt" }; + + /* reset to beginning of history (i.e. just a README file) */ + + g_opts.checkout_strategy = + GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED; + + cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master")); + + cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master")); + + cl_assert(git_path_exists("testrepo/README")); + cl_assert(git_path_exists("testrepo/branch_file.txt")); + cl_assert(git_path_exists("testrepo/link_to_new.txt")); + cl_assert(git_path_exists("testrepo/new.txt")); + + git_object_free(g_object); + cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479")); + + g_opts.checkout_strategy = + GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH; + g_opts.paths.strings = entries; + g_opts.paths.count = 2; + + cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); + + cl_assert(git_path_exists("testrepo/README")); + cl_assert(!git_path_exists("testrepo/branch_file.txt")); + cl_assert(!git_path_exists("testrepo/link_to_new.txt")); + cl_assert(git_path_exists("testrepo/new.txt")); +} + void test_checkout_tree__can_disable_pattern_match(void) { char *entries[] = { "b*.txt" }; diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index 314d3441e..bd10c009d 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -4,12 +4,20 @@ #include "git2/sys/repository.h" void cl_git_report_failure( - int error, const char *file, int line, const char *fncall) + int error, int expected, const char *file, int line, const char *fncall) { char msg[4096]; const git_error *last = giterr_last(); - p_snprintf(msg, 4096, "error %d - %s", - error, last ? last->message : ""); + + if (expected) + p_snprintf(msg, 4096, "error %d (expected %d) - %s", + error, expected, last ? last->message : ""); + else if (error || last) + p_snprintf(msg, 4096, "error %d - %s", + error, last ? last->message : ""); + else + p_snprintf(msg, 4096, "no error, expected non-zero return"); + clar__assert(0, file, line, fncall, msg, 1); } diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h index fc08bbf1f..c72d37db3 100644 --- a/tests/clar_libgit2.h +++ b/tests/clar_libgit2.h @@ -12,13 +12,15 @@ * * Use this wrapper around all `git_` library calls that return error codes! */ -#define cl_git_pass(expr) cl_git_pass_((expr), __FILE__, __LINE__) +#define cl_git_pass(expr) cl_git_expect((expr), 0, __FILE__, __LINE__) -#define cl_git_pass_(expr, file, line) do { \ +#define cl_git_fail_with(error, expr) cl_git_expect((expr), error, __FILE__, __LINE__) + +#define cl_git_expect(expr, expected, file, line) do { \ int _lg2_error; \ giterr_clear(); \ - if ((_lg2_error = (expr)) != 0) \ - cl_git_report_failure(_lg2_error, file, line, "Function call failed: " #expr); \ + if ((_lg2_error = (expr)) != expected) \ + cl_git_report_failure(_lg2_error, expected, file, line, "Function call failed: " #expr); \ } while (0) /** @@ -26,9 +28,11 @@ * just for consistency. Use with `git_` library * calls that are supposed to fail! */ -#define cl_git_fail(expr) cl_must_fail(expr) - -#define cl_git_fail_with(expr, error) cl_assert_equal_i(error,expr) +#define cl_git_fail(expr) do { \ + giterr_clear(); \ + if ((expr) == 0) \ + cl_git_report_failure(0, 0, __FILE__, __LINE__, "Function call succeeded: " #expr); \ + } while (0) /** * Like cl_git_pass, only for Win32 error code conventions @@ -37,7 +41,7 @@ int _win32_res; \ if ((_win32_res = (expr)) == 0) { \ giterr_set(GITERR_OS, "Returned: %d, system error code: %d", _win32_res, GetLastError()); \ - cl_git_report_failure(_win32_res, __FILE__, __LINE__, "System call failed: " #expr); \ + cl_git_report_failure(_win32_res, 0, __FILE__, __LINE__, "System call failed: " #expr); \ } \ } while(0) @@ -86,7 +90,7 @@ GIT_INLINE(void) cl_git_thread_check(void *data) clar__assert(0, threaderr->file, threaderr->line, threaderr->expr, threaderr->error_msg, 1); } -void cl_git_report_failure(int, const char *, int, const char *); +void cl_git_report_failure(int, int, const char *, int, const char *); #define cl_assert_at_line(expr,file,line) \ clar__assert((expr) != 0, file, line, "Expression is not true: " #expr, NULL, 1) diff --git a/tests/clone/local.c b/tests/clone/local.c index 91a0a1c2a..7f54d05de 100644 --- a/tests/clone/local.c +++ b/tests/clone/local.c @@ -16,6 +16,7 @@ static int file_url(git_buf *buf, const char *host, const char *path) return git_buf_printf(buf, "file://%s/%s", host, path); } +#ifdef GIT_WIN32 static int git_style_unc_path(git_buf *buf, const char *host, const char *path) { git_buf_clear(buf); @@ -49,6 +50,7 @@ static int unc_path(git_buf *buf, const char *host, const char *path) return 0; } +#endif void test_clone_local__should_clone_local(void) { diff --git a/tests/config/include.c b/tests/config/include.c index 882b89b16..0a07c9b85 100644 --- a/tests/config/include.c +++ b/tests/config/include.c @@ -108,6 +108,26 @@ void test_config_include__missing(void) git_config_free(cfg); } +void test_config_include__missing_homedir(void) +{ + git_config *cfg; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config"))); + cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz"); + + giterr_clear(); + cl_git_pass(git_config_open_ondisk(&cfg, "including")); + cl_assert(giterr_last() == NULL); + cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar")); + cl_assert_equal_s("baz", git_buf_cstr(&buf)); + + git_buf_free(&buf); + git_config_free(cfg); + + cl_sandbox_set_search_path_defaults(); +} + #define replicate10(s) s s s s s s s s s s void test_config_include__depth2(void) { diff --git a/tests/config/readonly.c b/tests/config/readonly.c new file mode 100644 index 000000000..f45abdd29 --- /dev/null +++ b/tests/config/readonly.c @@ -0,0 +1,64 @@ +#include "clar_libgit2.h" +#include "config_file.h" +#include "config.h" + +static git_config *cfg; + +void test_config_readonly__initialize(void) +{ + cl_git_pass(git_config_new(&cfg)); +} + +void test_config_readonly__cleanup(void) +{ + git_config_free(cfg); + cfg = NULL; +} + +void test_config_readonly__writing_to_readonly_fails(void) +{ + git_config_backend *backend; + + cl_git_pass(git_config_file__ondisk(&backend, "global")); + backend->readonly = 1; + cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0)); + + cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz")); + cl_assert(!git_path_exists("global")); +} + +void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void) +{ + git_config_backend *backend; + + cl_git_pass(git_config_file__ondisk(&backend, "global")); + backend->readonly = 1; + cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0)); + + cl_git_pass(git_config_file__ondisk(&backend, "local")); + cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0)); + + cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz")); + + cl_assert(git_path_exists("local")); + cl_assert(!git_path_exists("global")); + cl_git_pass(p_unlink("local")); +} + +void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void) +{ + git_config_backend *backend; + + cl_git_pass(git_config_file__ondisk(&backend, "local")); + backend->readonly = 1; + cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0)); + + cl_git_pass(git_config_file__ondisk(&backend, "global")); + cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0)); + + cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz")); + + cl_assert(!git_path_exists("local")); + cl_assert(git_path_exists("global")); + cl_git_pass(p_unlink("global")); +} diff --git a/tests/core/encoding.c b/tests/core/encoding.c index 7d91720f4..a677afe2e 100644 --- a/tests/core/encoding.c +++ b/tests/core/encoding.c @@ -29,6 +29,9 @@ void test_core_encoding__encode(void) cl_assert(git_encode_varint(buf, 100, 65) == 1); cl_assert(buf[0] == 'A'); + cl_assert(git_encode_varint(buf, 1, 1) == 1); + cl_assert(!memcmp(buf, "\x01", 1)); + cl_assert(git_encode_varint(buf, 100, 267869656) == 4); cl_assert(!memcmp(buf, "\xfe\xdc\xbaX", 4)); diff --git a/tests/core/features.c b/tests/core/features.c index cf5e19063..7b28cc0cb 100644 --- a/tests/core/features.c +++ b/tests/core/features.c @@ -17,7 +17,9 @@ void test_core_features__0(void) cl_assert((caps & GIT_FEATURE_THREADS) == 0); #endif +#ifdef GIT_HTTPS cl_assert((caps & GIT_FEATURE_HTTPS) != 0); +#endif #if defined(GIT_SSH) cl_assert((caps & GIT_FEATURE_SSH) != 0); diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c index 04a380b20..ef7ac6bd9 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -187,6 +187,35 @@ void test_core_filebuf__symlink_follow(void) cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES)); } +void test_core_filebuf__symlink_follow_absolute_paths(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf source = GIT_BUF_INIT, target = GIT_BUF_INIT; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(git_buf_joinpath(&source, clar_sandbox_path(), "linkdir/link")); + cl_git_pass(git_buf_joinpath(&target, clar_sandbox_path(), "linkdir/target")); + cl_git_pass(p_mkdir("linkdir", 0777)); + cl_git_pass(p_symlink(target.ptr, source.ptr)); + + cl_git_pass(git_filebuf_open(&file, source.ptr, 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); + git_buf_free(&source); + git_buf_free(&target); + + cl_git_pass(git_futils_rmdir_r("linkdir", NULL, GIT_RMDIR_REMOVE_FILES)); +} + void test_core_filebuf__symlink_depth(void) { git_filebuf file = GIT_FILEBUF_INIT; diff --git a/tests/core/oidmap.c b/tests/core/oidmap.c index 556a6ca4a..617da5483 100644 --- a/tests/core/oidmap.c +++ b/tests/core/oidmap.c @@ -1,8 +1,6 @@ #include "clar_libgit2.h" #include "oidmap.h" -GIT__USE_OIDMAP - typedef struct { git_oid oid; size_t extra; @@ -33,23 +31,23 @@ void test_core_oidmap__basic(void) khiter_t pos; int ret; - pos = kh_get(oid, map, &items[i].oid); - cl_assert(pos == kh_end(map)); + pos = git_oidmap_lookup_index(map, &items[i].oid); + cl_assert(!git_oidmap_valid_index(map, pos)); - pos = kh_put(oid, map, &items[i].oid, &ret); + pos = git_oidmap_put(map, &items[i].oid, &ret); cl_assert(ret != 0); - kh_val(map, pos) = &items[i]; + git_oidmap_set_value_at(map, pos, &items[i]); } for (i = 0; i < NITEMS; ++i) { khiter_t pos; - pos = kh_get(oid, map, &items[i].oid); - cl_assert(pos != kh_end(map)); + pos = git_oidmap_lookup_index(map, &items[i].oid); + cl_assert(git_oidmap_valid_index(map, pos)); - cl_assert_equal_p(kh_val(map, pos), &items[i]); + cl_assert_equal_p(git_oidmap_value_at(map, pos), &items[i]); } git_oidmap_free(map); @@ -87,23 +85,23 @@ void test_core_oidmap__hash_collision(void) khiter_t pos; int ret; - pos = kh_get(oid, map, &items[i].oid); - cl_assert(pos == kh_end(map)); + pos = git_oidmap_lookup_index(map, &items[i].oid); + cl_assert(!git_oidmap_valid_index(map, pos)); - pos = kh_put(oid, map, &items[i].oid, &ret); + pos = git_oidmap_put(map, &items[i].oid, &ret); cl_assert(ret != 0); - kh_val(map, pos) = &items[i]; + git_oidmap_set_value_at(map, pos, &items[i]); } for (i = 0; i < NITEMS; ++i) { khiter_t pos; - pos = kh_get(oid, map, &items[i].oid); - cl_assert(pos != kh_end(map)); + pos = git_oidmap_lookup_index(map, &items[i].oid); + cl_assert(git_oidmap_valid_index(map, pos)); - cl_assert_equal_p(kh_val(map, pos), &items[i]); + cl_assert_equal_p(git_oidmap_value_at(map, pos), &items[i]); } git_oidmap_free(map); diff --git a/tests/core/path.c b/tests/core/path.c index 71c6eda58..fefe2aeac 100644 --- a/tests/core/path.c +++ b/tests/core/path.c @@ -89,8 +89,12 @@ void test_core_path__00_dirname(void) check_dirname(REP16("/abc"), REP15("/abc")); #ifdef GIT_WIN32 + check_dirname("C:/", "C:/"); + check_dirname("C:", "C:/"); check_dirname("C:/path/", "C:/"); check_dirname("C:/path", "C:/"); + check_dirname("//computername/", "//computername/"); + check_dirname("//computername", "//computername/"); check_dirname("//computername/path/", "//computername/"); check_dirname("//computername/path", "//computername/"); check_dirname("//computername/sub/path/", "//computername/sub"); diff --git a/tests/core/posix.c b/tests/core/posix.c index 26ae36049..172462073 100644 --- a/tests/core/posix.c +++ b/tests/core/posix.c @@ -94,10 +94,7 @@ void test_core_posix__inet_pton(void) cl_assert(p_inet_pton(AF_INET, "10.foo.bar.1", &addr) == 0); /* Test unsupported address families */ - cl_git_fail(p_inet_pton(12, "52.472", NULL)); /* AF_DECnet */ - cl_assert_equal_i(EAFNOSUPPORT, errno); - - cl_git_fail(p_inet_pton(5, "315.124", NULL)); /* AF_CHAOS */ + cl_git_fail(p_inet_pton(INT_MAX-1, "52.472", &addr)); cl_assert_equal_i(EAFNOSUPPORT, errno); } diff --git a/tests/core/sha1.c b/tests/core/sha1.c new file mode 100644 index 000000000..c5b20f6e0 --- /dev/null +++ b/tests/core/sha1.c @@ -0,0 +1,64 @@ +#include "clar_libgit2.h" +#include "hash.h" + +#define FIXTURE_DIR "sha1" + +void test_core_sha1__initialize(void) +{ + cl_fixture_sandbox(FIXTURE_DIR); +} + +void test_core_sha1__cleanup(void) +{ + cl_fixture_cleanup(FIXTURE_DIR); +} + +static int sha1_file(git_oid *oid, const char *filename) +{ + git_hash_ctx ctx; + char buf[2048]; + int fd, ret; + ssize_t read_len; + + fd = p_open(filename, O_RDONLY); + cl_assert(fd >= 0); + + cl_git_pass(git_hash_ctx_init(&ctx)); + + while ((read_len = p_read(fd, buf, 2048)) > 0) + cl_git_pass(git_hash_update(&ctx, buf, (size_t)read_len)); + + cl_assert_equal_i(0, read_len); + p_close(fd); + + ret = git_hash_final(oid, &ctx); + git_hash_ctx_cleanup(&ctx); + + return ret; +} + +void test_core_sha1__sum(void) +{ + git_oid oid, expected; + + cl_git_pass(sha1_file(&oid, FIXTURE_DIR "/hello_c")); + git_oid_fromstr(&expected, "4e72679e3ea4d04e0c642f029e61eb8056c7ed94"); + cl_assert_equal_oid(&expected, &oid); +} + +/* test that sha1 collision detection works when enabled */ +void test_core_sha1__detect_collision_attack(void) +{ + git_oid oid, expected; + +#ifdef GIT_SHA1_COLLISIONDETECT + GIT_UNUSED(expected); + cl_git_fail(sha1_file(&oid, FIXTURE_DIR "/shattered-1.pdf")); + cl_assert_equal_s("SHA1 collision attack detected", giterr_last()->message); +#else + cl_git_pass(sha1_file(&oid, FIXTURE_DIR "/shattered-1.pdf")); + git_oid_fromstr(&expected, "38762cf7f55934b34d179ae6a4c80cadccbb7f0a"); + cl_assert_equal_oid(&expected, &oid); +#endif +} + diff --git a/tests/core/strmap.c b/tests/core/strmap.c index 3b4276aea..2fa594d43 100644 --- a/tests/core/strmap.c +++ b/tests/core/strmap.c @@ -1,8 +1,6 @@ #include "clar_libgit2.h" #include "strmap.h" -GIT__USE_STRMAP - git_strmap *g_table; void test_core_strmap__initialize(void) @@ -36,7 +34,7 @@ static void insert_strings(git_strmap *table, int count) for (j = 0, over = i / 26; over > 0; j++, over = over / 26) str[j] = 'A' + (over % 26); - git_strmap_insert(table, str, str, err); + git_strmap_insert(table, str, str, &err); cl_assert(err >= 0); } diff --git a/tests/core/structinit.c b/tests/core/structinit.c index e9f7b4a74..78503fcc6 100644 --- a/tests/core/structinit.c +++ b/tests/core/structinit.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include +#include #include #include #include @@ -96,6 +97,11 @@ void test_core_structinit__compare(void) git_diff_find_options, GIT_DIFF_FIND_OPTIONS_VERSION, \ GIT_DIFF_FIND_OPTIONS_INIT, git_diff_find_init_options); + /* filter */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_filter, GIT_FILTER_VERSION, \ + GIT_FILTER_INIT, git_filter_init); + /* merge_file_input */ CHECK_MACRO_FUNC_INIT_EQUAL( \ git_merge_file_input, GIT_MERGE_FILE_INPUT_VERSION, \ @@ -165,4 +171,9 @@ void test_core_structinit__compare(void) CHECK_MACRO_FUNC_INIT_EQUAL( \ git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \ GIT_SUBMODULE_UPDATE_OPTIONS_INIT, git_submodule_update_init_options); + + /* submodule update */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_proxy_options, GIT_PROXY_OPTIONS_VERSION, \ + GIT_PROXY_OPTIONS_INIT, git_proxy_init_options); } diff --git a/tests/diff/parse.c b/tests/diff/parse.c index a06813d1b..acb6eb8a5 100644 --- a/tests/diff/parse.c +++ b/tests/diff/parse.c @@ -196,3 +196,74 @@ void test_diff_parse__get_patch_from_diff(void) cl_git_sandbox_cleanup(); } + +static int file_cb(const git_diff_delta *delta, float progress, void *payload) +{ + int *called = (int *) payload; + GIT_UNUSED(delta); + GIT_UNUSED(progress); + (*called)++; + return 0; +} + +void test_diff_parse__foreach_works_with_parsed_patch(void) +{ + const char patch[] = + "diff --git a/obj1 b/obj2\n" + "index 1234567..7654321 10644\n" + "--- a/obj1\n" + "+++ b/obj2\n" + "@@ -1 +1 @@\n" + "-abcde\n" + "+12345\n"; + int called = 0; + git_diff *diff; + + cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch))); + cl_git_pass(git_diff_foreach(diff, file_cb, NULL, NULL, NULL, &called)); + cl_assert_equal_i(called, 1); + + git_diff_free(diff); +} + +void test_diff_parse__parsing_minimal_patch_succeeds(void) +{ + const char patch[] = + "diff --git a/obj1 b/obj2\n" + "index 1234567..7654321 10644\n" + "--- a/obj1\n" + "+++ b/obj2\n" + "@@ -1 +1 @@\n" + "-a\n" + "+\n"; + git_buf buf = GIT_BUF_INIT; + git_diff *diff; + + cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch))); + cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH)); + cl_assert_equal_s(patch, buf.ptr); + + git_diff_free(diff); + git_buf_free(&buf); +} + +void test_diff_parse__patch_roundtrip_succeeds(void) +{ + const char buf1[] = "a\n", buf2[] = "b\n"; + git_buf patchbuf = GIT_BUF_INIT, diffbuf = GIT_BUF_INIT; + git_patch *patch; + git_diff *diff; + + cl_git_pass(git_patch_from_buffers(&patch, buf1, strlen(buf1), "obj1", buf2, strlen(buf2), "obj2", NULL)); + cl_git_pass(git_patch_to_buf(&patchbuf, patch)); + + cl_git_pass(git_diff_from_buffer(&diff, patchbuf.ptr, patchbuf.size)); + cl_git_pass(git_diff_to_buf(&diffbuf, diff, GIT_DIFF_FORMAT_PATCH)); + + cl_assert_equal_s(patchbuf.ptr, diffbuf.ptr); + + git_patch_free(patch); + git_diff_free(diff); + git_buf_free(&patchbuf); + git_buf_free(&diffbuf); +} diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c index 3b750af5e..ea4b70e4a 100644 --- a/tests/fetchhead/nonetwork.c +++ b/tests/fetchhead/nonetwork.c @@ -293,7 +293,7 @@ void test_fetchhead_nonetwork__invalid_for_merge(void) cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tinvalid-merge\t\n"); cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL)); - cl_assert(git__prefixcmp(giterr_last()->message, "Invalid for-merge") == 0); + cl_assert(git__prefixcmp(giterr_last()->message, "invalid for-merge") == 0); } void test_fetchhead_nonetwork__invalid_description(void) @@ -304,7 +304,7 @@ void test_fetchhead_nonetwork__invalid_description(void) cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\n"); cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL)); - cl_assert(git__prefixcmp(giterr_last()->message, "Invalid description") == 0); + cl_assert(git__prefixcmp(giterr_last()->message, "invalid description") == 0); } static int assert_master_for_merge(const char *ref, const char *url, const git_oid *id, unsigned int is_merge, void *data) diff --git a/tests/filter/custom.c b/tests/filter/custom.c index fd1cd271c..799beef62 100644 --- a/tests/filter/custom.c +++ b/tests/filter/custom.c @@ -55,6 +55,7 @@ void test_filter_custom__initialize(void) "hero* bitflip reverse\n" "herofile text\n" "heroflip -reverse binary\n" + "villain erroneous\n" "*.bin binary\n"); } @@ -82,6 +83,11 @@ static void register_custom_filters(void) create_reverse_filter("+prereverse"), GIT_FILTER_DRIVER_PRIORITY)); + cl_git_pass(git_filter_register( + "erroneous", + create_erroneous_filter("+erroneous"), + GIT_FILTER_DRIVER_PRIORITY)); + filters_registered = 1; } } @@ -235,3 +241,18 @@ void test_filter_custom__filter_registry_failure_cases(void) cl_git_fail(git_filter_unregister(GIT_FILTER_IDENT)); cl_assert_equal_i(GIT_ENOTFOUND, git_filter_unregister("not-a-filter")); } + +void test_filter_custom__erroneous_filter_fails(void) +{ + git_filter_list *filters; + git_buf out = GIT_BUF_INIT; + git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data)); + + cl_git_pass(git_filter_list_load( + &filters, g_repo, NULL, "villain", GIT_FILTER_TO_WORKTREE, 0)); + + cl_git_fail(git_filter_list_apply_to_data(&out, filters, &in)); + + git_filter_list_free(filters); + git_buf_free(&out); +} diff --git a/tests/filter/custom_helpers.c b/tests/filter/custom_helpers.c index 2c80212be..d7f2afe7a 100644 --- a/tests/filter/custom_helpers.c +++ b/tests/filter/custom_helpers.c @@ -106,3 +106,36 @@ git_filter *create_reverse_filter(const char *attrs) return filter; } + +int erroneous_filter_stream( + git_writestream **out, + git_filter *self, + void **payload, + const git_filter_source *src, + git_writestream *next) +{ + GIT_UNUSED(out); + GIT_UNUSED(self); + GIT_UNUSED(payload); + GIT_UNUSED(src); + GIT_UNUSED(next); + return -1; +} + +static void erroneous_filter_free(git_filter *f) +{ + git__free(f); +} + +git_filter *create_erroneous_filter(const char *attrs) +{ + git_filter *filter = git__calloc(1, sizeof(git_filter)); + cl_assert(filter); + + filter->version = GIT_FILTER_VERSION; + filter->attributes = attrs; + filter->stream = erroneous_filter_stream; + filter->shutdown = erroneous_filter_free; + + return filter; +} diff --git a/tests/filter/custom_helpers.h b/tests/filter/custom_helpers.h index 13cfb23ae..537a51da2 100644 --- a/tests/filter/custom_helpers.h +++ b/tests/filter/custom_helpers.h @@ -2,6 +2,7 @@ extern git_filter *create_bitflip_filter(void); extern git_filter *create_reverse_filter(const char *attr); +extern git_filter *create_erroneous_filter(const char *attr); extern int bitflip_filter_apply( git_filter *self, diff --git a/tests/index/version.c b/tests/index/version.c index 3fd240d3c..7ada302b5 100644 --- a/tests/index/version.c +++ b/tests/index/version.c @@ -3,39 +3,135 @@ static git_repository *g_repo = NULL; +void test_index_version__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +void test_index_version__can_read_v4(void) +{ + const char *paths[] = { + "file.tx", "file.txt", "file.txz", "foo", "zzz", + }; + git_index *index; + size_t i; + + g_repo = cl_git_sandbox_init("indexv4"); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_assert_equal_sz(git_index_entrycount(index), 5); + + for (i = 0; i < ARRAY_SIZE(paths); i++) { + const git_index_entry *entry = + git_index_get_bypath(index, paths[i], GIT_INDEX_STAGE_NORMAL); + + cl_assert(entry != NULL); + } + + git_index_free(index); +} + void test_index_version__can_write_v4(void) { + const char *paths[] = { + "foo", + "foox", + "foobar", + "foobal", + "x", + "xz", + "xyzzyx" + }; + git_index_entry entry; git_index *index; - const git_index_entry *entry; + size_t i; - g_repo = cl_git_sandbox_init("filemodes"); + g_repo = cl_git_sandbox_init("empty_standard_repo"); cl_git_pass(git_repository_index(&index, g_repo)); - - cl_assert(index->on_disk); - cl_assert(git_index_version(index) == 2); - - cl_assert(git_index_entrycount(index) == 6); - cl_git_pass(git_index_set_version(index, 4)); + for (i = 0; i < ARRAY_SIZE(paths); i++) { + memset(&entry, 0, sizeof(entry)); + entry.path = paths[i]; + entry.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_index_add_frombuffer(index, &entry, paths[i], + strlen(paths[i]) + 1)); + } + cl_assert_equal_sz(git_index_entrycount(index), ARRAY_SIZE(paths)); + cl_git_pass(git_index_write(index)); git_index_free(index); cl_git_pass(git_repository_index(&index, g_repo)); cl_assert(git_index_version(index) == 4); - entry = git_index_get_bypath(index, "exec_off", 0); - cl_assert(entry); - entry = git_index_get_bypath(index, "exec_off2on_staged", 0); - cl_assert(entry); - entry = git_index_get_bypath(index, "exec_on", 0); - cl_assert(entry); + for (i = 0; i < ARRAY_SIZE(paths); i++) { + const git_index_entry *e; + + cl_assert(e = git_index_get_bypath(index, paths[i], 0)); + cl_assert_equal_s(paths[i], e->path); + } git_index_free(index); } -void test_index_version__cleanup(void) +void test_index_version__v4_uses_path_compression(void) { - cl_git_sandbox_cleanup(); - g_repo = NULL; + git_index_entry entry; + git_index *index; + char path[250], buf[1]; + struct stat st; + char i, j; + + memset(path, 'a', sizeof(path)); + memset(buf, 'a', sizeof(buf)); + + memset(&entry, 0, sizeof(entry)); + entry.path = path; + entry.mode = GIT_FILEMODE_BLOB; + + g_repo = cl_git_sandbox_init("indexv4"); + cl_git_pass(git_repository_index(&index, g_repo)); + + /* write 676 paths of 250 bytes length */ + for (i = 'a'; i <= 'z'; i++) { + for (j = 'a'; j < 'z'; j++) { + path[ARRAY_SIZE(path) - 3] = i; + path[ARRAY_SIZE(path) - 2] = j; + path[ARRAY_SIZE(path) - 1] = '\0'; + cl_git_pass(git_index_add_frombuffer(index, &entry, buf, sizeof(buf))); + } + } + + cl_git_pass(git_index_write(index)); + cl_git_pass(p_stat(git_index_path(index), &st)); + + /* + * Without path compression, the written paths would at + * least take + * + * (entries * pathlen) = len + * (676 * 250) = 169000 + * + * bytes. As index v4 uses suffix-compression and our + * written paths only differ in the last two entries, + * this number will be much smaller, e.g. + * + * (1 * pathlen) + (675 * 2) = len + * 676 + 1350 = 2026 + * + * bytes. + * + * Note that the above calculations do not include + * additional metadata of the index, e.g. OIDs or + * index extensions. Including those we get an index + * of approx. 200kB without compression and 40kB with + * compression. As this is a lot smaller than without + * compression, we can verify that path compression is + * used. + */ + cl_assert_(st.st_size < 75000, "path compression not enabled"); + + git_index_free(index); } diff --git a/tests/iterator/workdir.c b/tests/iterator/workdir.c index 28fcc0d23..f33fd98f1 100644 --- a/tests/iterator/workdir.c +++ b/tests/iterator/workdir.c @@ -613,9 +613,11 @@ void test_iterator_workdir__filesystem2(void) "heads/ident", "heads/long-file-name", "heads/master", + "heads/merge-conflict", "heads/packed-test", "heads/subtrees", "heads/test", + "heads/testrepo-worktree", "tags/e90810b", "tags/foo/bar", "tags/foo/foo/bar", @@ -628,7 +630,7 @@ void test_iterator_workdir__filesystem2(void) cl_git_pass(git_iterator_for_filesystem( &i, "testrepo/.git/refs", NULL)); - expect_iterator_items(i, 13, expect_base, 13, expect_base); + expect_iterator_items(i, 15, expect_base, 15, expect_base); git_iterator_free(i); } diff --git a/tests/merge/trees/renames.c b/tests/merge/trees/renames.c index d7721c894..853bf2fa0 100644 --- a/tests/merge/trees/renames.c +++ b/tests/merge/trees/renames.c @@ -242,6 +242,8 @@ void test_merge_trees_renames__no_rename_index(void) { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }, }; + opts.flags &= ~GIT_MERGE_FIND_RENAMES; + cl_git_pass(merge_trees_from_branches(&index, repo, BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS, &opts)); @@ -250,3 +252,25 @@ void test_merge_trees_renames__no_rename_index(void) git_index_free(index); } + +void test_merge_trees_renames__submodules(void) +{ + git_index *index; + git_merge_options *opts = NULL; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "cd3e8d4aa06bdc781f264171030bc28f2b370fee", 0, ".gitmodules" }, + { 0100644, "4dd1ef7569b18d92d93c0a35bb6b93049137b355", 1, "file.txt" }, + { 0100644, "a2d8d1824c68541cca94ffb90f79291eba495921", 2, "file.txt" }, + { 0100644, "63ec604d491161ddafdae4179843c26d54bd999a", 3, "file.txt" }, + { 0160000, "0000000000000000000000000000000000000001", 1, "submodule1" }, + { 0160000, "0000000000000000000000000000000000000002", 3, "submodule1" }, + { 0160000, "0000000000000000000000000000000000000003", 0, "submodule2" }, + }; + + cl_git_pass(merge_trees_from_branches(&index, repo, + "submodule_rename1", "submodule_rename2", + opts)); + cl_assert(merge_test_index(index, merge_index_entries, 7)); + git_index_free(index); +} diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c index 6194802af..76872e51f 100644 --- a/tests/network/remote/local.c +++ b/tests/network/remote/local.c @@ -2,6 +2,7 @@ #include "buffer.h" #include "path.h" #include "posix.h" +#include "git2/sys/repository.h" static git_repository *repo; static git_buf file_path_buf = GIT_BUF_INIT; @@ -465,3 +466,19 @@ void test_network_remote_local__push_delete(void) cl_fixture_cleanup("target.git"); cl_git_sandbox_cleanup(); } + +void test_network_remote_local__anonymous_remote_inmemory_repo(void) +{ + git_repository *inmemory; + git_remote *remote; + + git_buf_sets(&file_path_buf, cl_git_path_url(cl_fixture("testrepo.git"))); + + cl_git_pass(git_repository_new(&inmemory)); + cl_git_pass(git_remote_create_anonymous(&remote, inmemory, git_buf_cstr(&file_path_buf))); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); + cl_assert(git_remote_connected(remote)); + git_remote_disconnect(remote); + git_remote_free(remote); + git_repository_free(inmemory); +} diff --git a/tests/object/lookup.c b/tests/object/lookup.c index cfa6d4678..544f32bc4 100644 --- a/tests/object/lookup.c +++ b/tests/object/lookup.c @@ -6,13 +6,12 @@ static git_repository *g_repo; void test_object_lookup__initialize(void) { - cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); + g_repo = cl_git_sandbox_init("testrepo.git"); } void test_object_lookup__cleanup(void) { - git_repository_free(g_repo); - g_repo = NULL; + cl_git_sandbox_cleanup(); } void test_object_lookup__lookup_wrong_type_returns_enotfound(void) @@ -63,3 +62,61 @@ void test_object_lookup__lookup_wrong_type_eventually_returns_enotfound(void) GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_TAG)); } +void test_object_lookup__lookup_corrupt_object_returns_error(void) +{ + const char *commit = "8e73b769e97678d684b809b163bebdae2911720f", + *file = "objects/8e/73b769e97678d684b809b163bebdae2911720f"; + git_buf path = GIT_BUF_INIT, contents = GIT_BUF_INIT; + git_oid oid; + git_object *object; + size_t i; + + cl_git_pass(git_oid_fromstr(&oid, commit)); + cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), file)); + cl_git_pass(git_futils_readbuffer(&contents, path.ptr)); + + /* Corrupt and try to read the object */ + for (i = 0; i < contents.size; i++) { + contents.ptr[i] ^= 0x1; + cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644)); + cl_git_fail(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + contents.ptr[i] ^= 0x1; + } + + /* Restore original content and assert we can read the object */ + cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644)); + cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + + git_object_free(object); + git_buf_free(&path); + git_buf_free(&contents); +} + +void test_object_lookup__lookup_object_with_wrong_hash_returns_error(void) +{ + const char *oldloose = "objects/8e/73b769e97678d684b809b163bebdae2911720f", + *newloose = "objects/8e/73b769e97678d684b809b163bebdae2911720e", + *commit = "8e73b769e97678d684b809b163bebdae2911720e"; + git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT; + git_object *object; + git_oid oid; + + cl_git_pass(git_oid_fromstr(&oid, commit)); + + /* Copy object to another location with wrong hash */ + cl_git_pass(git_buf_joinpath(&oldpath, git_repository_path(g_repo), oldloose)); + cl_git_pass(git_buf_joinpath(&newpath, git_repository_path(g_repo), newloose)); + cl_git_pass(git_futils_cp(oldpath.ptr, newpath.ptr, 0644)); + + /* Verify that lookup fails due to a hashsum mismatch */ + cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + + /* Disable verification and try again */ + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0)); + cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1)); + + git_object_free(object); + git_buf_free(&oldpath); + git_buf_free(&newpath); +} diff --git a/tests/odb/backend/backend_helpers.c b/tests/odb/backend/backend_helpers.c new file mode 100644 index 000000000..37a8fd200 --- /dev/null +++ b/tests/odb/backend/backend_helpers.c @@ -0,0 +1,159 @@ +#include "clar_libgit2.h" +#include "git2/sys/odb_backend.h" +#include "backend_helpers.h" + +static int search_object(const fake_object **out, fake_backend *fake, const git_oid *oid, size_t len) +{ + const fake_object *obj = fake->objects, *found = NULL; + + while (obj && obj->oid) { + git_oid current_oid; + + git_oid_fromstr(¤t_oid, obj->oid); + + if (git_oid_ncmp(¤t_oid, oid, len) == 0) { + if (found) + return GIT_EAMBIGUOUS; + found = obj; + } + + obj++; + } + + if (found && out) + *out = found; + + return found ? GIT_OK : GIT_ENOTFOUND; +} + +static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid) +{ + fake_backend *fake; + + fake = (fake_backend *)backend; + + fake->exists_calls++; + + return search_object(NULL, fake, oid, GIT_OID_HEXSZ) == GIT_OK; +} + +static int fake_backend__exists_prefix( + git_oid *out, git_odb_backend *backend, const git_oid *oid, size_t len) +{ + const fake_object *obj; + fake_backend *fake; + int error; + + fake = (fake_backend *)backend; + + fake->exists_prefix_calls++; + + if ((error = search_object(&obj, fake, oid, len)) < 0) + return error; + + if (out) + git_oid_fromstr(out, obj->oid); + + return 0; +} + +static int fake_backend__read( + void **buffer_p, size_t *len_p, git_otype *type_p, + git_odb_backend *backend, const git_oid *oid) +{ + const fake_object *obj; + fake_backend *fake; + int error; + + fake = (fake_backend *)backend; + + fake->read_calls++; + + if ((error = search_object(&obj, fake, oid, GIT_OID_HEXSZ)) < 0) + return error; + + *len_p = strlen(obj->content); + *buffer_p = git__strdup(obj->content); + *type_p = GIT_OBJ_BLOB; + + return 0; +} + +static int fake_backend__read_header( + size_t *len_p, git_otype *type_p, + git_odb_backend *backend, const git_oid *oid) +{ + const fake_object *obj; + fake_backend *fake; + int error; + + fake = (fake_backend *)backend; + + fake->read_header_calls++; + + if ((error = search_object(&obj, fake, oid, GIT_OID_HEXSZ)) < 0) + return error; + + *len_p = strlen(obj->content); + *type_p = GIT_OBJ_BLOB; + + return 0; +} + +static int fake_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) +{ + const fake_object *obj; + fake_backend *fake; + int error; + + fake = (fake_backend *)backend; + + fake->read_prefix_calls++; + + if ((error = search_object(&obj, fake, short_oid, len)) < 0) + return error; + + git_oid_fromstr(out_oid, obj->oid); + *len_p = strlen(obj->content); + *buffer_p = git__strdup(obj->content); + *type_p = GIT_OBJ_BLOB; + + return 0; +} + +static void fake_backend__free(git_odb_backend *_backend) +{ + fake_backend *backend; + + backend = (fake_backend *)_backend; + + git__free(backend); +} + +int build_fake_backend( + git_odb_backend **out, + const fake_object *objects) +{ + fake_backend *backend; + + backend = git__calloc(1, sizeof(fake_backend)); + GITERR_CHECK_ALLOC(backend); + + backend->parent.version = GIT_ODB_BACKEND_VERSION; + + backend->parent.refresh = NULL; + backend->objects = objects; + + backend->parent.read = fake_backend__read; + backend->parent.read_prefix = fake_backend__read_prefix; + backend->parent.read_header = fake_backend__read_header; + backend->parent.exists = fake_backend__exists; + backend->parent.exists_prefix = fake_backend__exists_prefix; + backend->parent.free = &fake_backend__free; + + *out = (git_odb_backend *)backend; + + return 0; +} diff --git a/tests/odb/backend/backend_helpers.h b/tests/odb/backend/backend_helpers.h new file mode 100644 index 000000000..5c393c0a5 --- /dev/null +++ b/tests/odb/backend/backend_helpers.h @@ -0,0 +1,22 @@ +#include "git2/sys/odb_backend.h" + +typedef struct { + const char *oid; + const char *content; +} fake_object; + +typedef struct { + git_odb_backend parent; + + int exists_calls; + int exists_prefix_calls; + int read_calls; + int read_header_calls; + int read_prefix_calls; + + const fake_object *objects; +} fake_backend; + +int build_fake_backend( + git_odb_backend **out, + const fake_object *objects); diff --git a/tests/odb/backend/multiple.c b/tests/odb/backend/multiple.c new file mode 100644 index 000000000..1c6068df3 --- /dev/null +++ b/tests/odb/backend/multiple.c @@ -0,0 +1,121 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "backend_helpers.h" + +#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" + +static git_repository *_repo; +static git_odb_object *_obj; +static fake_backend *_fake_empty; +static fake_backend *_fake_filled; + +static git_oid _existing_oid; + +static const fake_object _objects_filled[] = { + { EXISTING_HASH, "" }, + { NULL, NULL } +}; + +static const fake_object _objects_empty[] = { + { NULL, NULL } +}; + +void test_odb_backend_multiple__initialize(void) +{ + git_odb_backend *backend; + + git_oid_fromstr(&_existing_oid, EXISTING_HASH); + + _obj = NULL; + _repo = cl_git_sandbox_init("testrepo.git"); + + cl_git_pass(build_fake_backend(&backend, _objects_filled)); + _fake_filled = (fake_backend *)backend; + + cl_git_pass(build_fake_backend(&backend, _objects_empty)); + _fake_empty = (fake_backend *)backend; +} + +void test_odb_backend_multiple__cleanup(void) +{ + git_odb_object_free(_obj); + cl_git_sandbox_cleanup(); +} + +void test_odb_backend_multiple__read_with_empty_first_succeeds(void) +{ + git_odb *odb; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 10)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 50)); + + cl_git_pass(git_odb_read(&_obj, odb, &_existing_oid)); + + cl_assert_equal_i(1, _fake_filled->read_calls); + cl_assert_equal_i(1, _fake_empty->read_calls); +} + +void test_odb_backend_multiple__read_with_first_matching_stops(void) +{ + git_odb *odb; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 10)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50)); + + cl_git_pass(git_odb_read(&_obj, odb, &_existing_oid)); + + cl_assert_equal_i(1, _fake_filled->read_calls); + cl_assert_equal_i(0, _fake_empty->read_calls); +} + +void test_odb_backend_multiple__read_prefix_with_first_empty_succeeds(void) +{ + git_odb *odb; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 10)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 50)); + + cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7)); + + cl_assert_equal_i(1, _fake_filled->read_prefix_calls); + cl_assert_equal_i(1, _fake_empty->read_prefix_calls); +} + +void test_odb_backend_multiple__read_prefix_with_first_matching_reads_both(void) +{ + git_odb *odb; + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, -10)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50)); + + cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7)); + + cl_assert_equal_i(1, _fake_filled->read_prefix_calls); + cl_assert_equal_i(1, _fake_empty->read_prefix_calls); +} + +void test_odb_backend_multiple__read_prefix_with_first_matching_succeeds_without_hash_verification(void) +{ + git_odb *odb; + + git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0); + + cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, -10)); + cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50)); + + cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7)); + + /* + * Both backends should be checked as we have to check + * for collisions + */ + cl_assert_equal_i(1, _fake_filled->read_prefix_calls); + cl_assert_equal_i(1, _fake_empty->read_prefix_calls); + + git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1); +} diff --git a/tests/odb/backend/nobackend.c b/tests/odb/backend/nobackend.c index 783641e8f..3c4f344b1 100644 --- a/tests/odb/backend/nobackend.c +++ b/tests/odb/backend/nobackend.c @@ -40,7 +40,7 @@ void test_odb_backend_nobackend__write_fails_gracefully(void) cl_git_fail(git_odb_write(&id, odb, "Hello world!\n", 13, GIT_OBJ_BLOB)); err = giterr_last(); - cl_assert_equal_s(err->message, "Cannot write object - unsupported in the loaded odb backends"); + cl_assert_equal_s(err->message, "cannot write object - unsupported in the loaded odb backends"); git_odb_free(odb); } diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c index b43529479..6abc0c6d2 100644 --- a/tests/odb/backend/nonrefreshing.c +++ b/tests/odb/backend/nonrefreshing.c @@ -1,150 +1,41 @@ #include "clar_libgit2.h" -#include "git2/sys/odb_backend.h" #include "repository.h" - -typedef struct fake_backend { - git_odb_backend parent; - - git_error_code error_code; - - int exists_calls; - int read_calls; - int read_header_calls; - int read_prefix_calls; -} fake_backend; +#include "backend_helpers.h" static git_repository *_repo; static fake_backend *_fake; -static git_oid _oid; -static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid) -{ - fake_backend *fake; +#define NONEXISTING_HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" +#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" - GIT_UNUSED(oid); +static const fake_object _objects[] = { + { EXISTING_HASH, "" }, + { NULL, NULL } +}; - fake = (fake_backend *)backend; +static git_oid _nonexisting_oid; +static git_oid _existing_oid; - fake->exists_calls++; - - return (fake->error_code == GIT_OK); -} - -static int fake_backend__read( - void **buffer_p, size_t *len_p, git_otype *type_p, - git_odb_backend *backend, const git_oid *oid) -{ - fake_backend *fake; - - GIT_UNUSED(buffer_p); - GIT_UNUSED(len_p); - GIT_UNUSED(type_p); - GIT_UNUSED(oid); - - fake = (fake_backend *)backend; - - fake->read_calls++; - - *len_p = 0; - *buffer_p = NULL; - *type_p = GIT_OBJ_BLOB; - - return fake->error_code; -} - -static int fake_backend__read_header( - size_t *len_p, git_otype *type_p, - git_odb_backend *backend, const git_oid *oid) -{ - fake_backend *fake; - - GIT_UNUSED(len_p); - GIT_UNUSED(type_p); - GIT_UNUSED(oid); - - fake = (fake_backend *)backend; - - fake->read_header_calls++; - - *len_p = 0; - *type_p = GIT_OBJ_BLOB; - - return fake->error_code; -} - -static int fake_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) -{ - fake_backend *fake; - - GIT_UNUSED(out_oid); - GIT_UNUSED(buffer_p); - GIT_UNUSED(len_p); - GIT_UNUSED(type_p); - GIT_UNUSED(short_oid); - GIT_UNUSED(len); - - fake = (fake_backend *)backend; - - fake->read_prefix_calls++; - - *len_p = 0; - *buffer_p = NULL; - *type_p = GIT_OBJ_BLOB; - - return fake->error_code; -} - -static void fake_backend__free(git_odb_backend *_backend) -{ - fake_backend *backend; - - backend = (fake_backend *)_backend; - - git__free(backend); -} - -static int build_fake_backend( - git_odb_backend **out, - git_error_code error_code) -{ - fake_backend *backend; - - backend = git__calloc(1, sizeof(fake_backend)); - GITERR_CHECK_ALLOC(backend); - - backend->parent.version = GIT_ODB_BACKEND_VERSION; - - backend->parent.refresh = NULL; - backend->error_code = error_code; - - backend->parent.read = fake_backend__read; - backend->parent.read_prefix = fake_backend__read_prefix; - backend->parent.read_header = fake_backend__read_header; - backend->parent.exists = fake_backend__exists; - backend->parent.free = &fake_backend__free; - - *out = (git_odb_backend *)backend; - - return 0; -} - -static void setup_repository_and_backend(git_error_code error_code) +static void setup_repository_and_backend(void) { git_odb *odb = NULL; git_odb_backend *backend = NULL; _repo = cl_git_sandbox_init("testrepo.git"); - cl_git_pass(build_fake_backend(&backend, error_code)); + cl_git_pass(build_fake_backend(&backend, _objects)); cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); cl_git_pass(git_odb_add_backend(odb, backend, 10)); _fake = (fake_backend *)backend; +} - cl_git_pass(git_oid_fromstr(&_oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); +void test_odb_backend_nonrefreshing__initialize(void) +{ + git_oid_fromstr(&_nonexisting_oid, NONEXISTING_HASH); + git_oid_fromstr(&_existing_oid, EXISTING_HASH); + setup_repository_and_backend(); } void test_odb_backend_nonrefreshing__cleanup(void) @@ -156,10 +47,8 @@ void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_failure(void) { git_odb *odb; - setup_repository_and_backend(GIT_ENOTFOUND); - cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); - cl_assert_equal_b(false, git_odb_exists(odb, &_oid)); + cl_assert_equal_b(false, git_odb_exists(odb, &_nonexisting_oid)); cl_assert_equal_i(1, _fake->exists_calls); } @@ -168,10 +57,8 @@ void test_odb_backend_nonrefreshing__read_is_invoked_once_on_failure(void) { git_object *obj; - setup_repository_and_backend(GIT_ENOTFOUND); - cl_git_fail_with( - git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY), + git_object_lookup(&obj, _repo, &_nonexisting_oid, GIT_OBJ_ANY), GIT_ENOTFOUND); cl_assert_equal_i(1, _fake->read_calls); @@ -181,10 +68,8 @@ void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_failure(void) { git_object *obj; - setup_repository_and_backend(GIT_ENOTFOUND); - cl_git_fail_with( - git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY), + git_object_lookup_prefix(&obj, _repo, &_nonexisting_oid, 7, GIT_OBJ_ANY), GIT_ENOTFOUND); cl_assert_equal_i(1, _fake->read_prefix_calls); @@ -196,12 +81,10 @@ void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_failure(void) size_t len; git_otype type; - setup_repository_and_backend(GIT_ENOTFOUND); - cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); cl_git_fail_with( - git_odb_read_header(&len, &type, odb, &_oid), + git_odb_read_header(&len, &type, odb, &_nonexisting_oid), GIT_ENOTFOUND); cl_assert_equal_i(1, _fake->read_header_calls); @@ -211,10 +94,8 @@ void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_success(void) { git_odb *odb; - setup_repository_and_backend(GIT_OK); - cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); - cl_assert_equal_b(true, git_odb_exists(odb, &_oid)); + cl_assert_equal_b(true, git_odb_exists(odb, &_existing_oid)); cl_assert_equal_i(1, _fake->exists_calls); } @@ -223,9 +104,7 @@ void test_odb_backend_nonrefreshing__read_is_invoked_once_on_success(void) { git_object *obj; - setup_repository_and_backend(GIT_OK); - - cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY)); + cl_git_pass(git_object_lookup(&obj, _repo, &_existing_oid, GIT_OBJ_ANY)); cl_assert_equal_i(1, _fake->read_calls); @@ -236,9 +115,7 @@ void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_success(void) { git_object *obj; - setup_repository_and_backend(GIT_OK); - - cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY)); + cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_existing_oid, 7, GIT_OBJ_ANY)); cl_assert_equal_i(1, _fake->read_prefix_calls); @@ -251,11 +128,9 @@ void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_success(void) size_t len; git_otype type; - setup_repository_and_backend(GIT_OK); - cl_git_pass(git_repository_odb__weakptr(&odb, _repo)); - cl_git_pass(git_odb_read_header(&len, &type, odb, &_oid)); + cl_git_pass(git_odb_read_header(&len, &type, odb, &_existing_oid)); cl_assert_equal_i(1, _fake->read_header_calls); } @@ -264,8 +139,6 @@ void test_odb_backend_nonrefreshing__read_is_invoked_once_when_revparsing_a_full { git_object *obj; - setup_repository_and_backend(GIT_ENOTFOUND); - cl_git_fail_with( git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"), GIT_ENOTFOUND); diff --git a/tests/odb/backend/simple.c b/tests/odb/backend/simple.c new file mode 100644 index 000000000..c0fcd403b --- /dev/null +++ b/tests/odb/backend/simple.c @@ -0,0 +1,232 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "backend_helpers.h" + +#define EMPTY_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" + +static git_repository *_repo; +static git_odb *_odb; +static git_odb_object *_obj; +static git_oid _oid; + +static void setup_backend(const fake_object *objs) +{ + git_odb_backend *backend; + + cl_git_pass(build_fake_backend(&backend, objs)); + + cl_git_pass(git_repository_odb__weakptr(&_odb, _repo)); + cl_git_pass(git_odb_add_backend(_odb, backend, 10)); +} + +static void assert_object_contains(git_odb_object *obj, const char *expected) +{ + const char *actual = (const char *) git_odb_object_data(obj); + + cl_assert_equal_s(actual, expected); +} + +void test_odb_backend_simple__initialize(void) +{ + _repo = cl_git_sandbox_init("testrepo.git"); + _odb = NULL; + _obj = NULL; +} + +void test_odb_backend_simple__cleanup(void) +{ + git_odb_object_free(_obj); + cl_git_sandbox_cleanup(); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1)); +} + +void test_odb_backend_simple__read_of_object_succeeds(void) +{ + const fake_object objs[] = { + { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + cl_git_pass(git_odb_read(&_obj, _odb, &_oid)); + + assert_object_contains(_obj, objs[0].content); +} + +void test_odb_backend_simple__read_of_nonexisting_object_fails(void) +{ + const fake_object objs[] = { + { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4633")); + cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&_obj, _odb, &_oid)); +} + +void test_odb_backend_simple__read_with_hash_mismatch_fails(void) +{ + const fake_object objs[] = { + { "1234567890123456789012345678901234567890", "nonmatching content" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + cl_git_fail_with(GIT_EMISMATCH, git_odb_read(&_obj, _odb, &_oid)); +} + +void test_odb_backend_simple__read_with_hash_mismatch_succeeds_without_verification(void) +{ + const fake_object objs[] = { + { "1234567890123456789012345678901234567890", "nonmatching content" }, + { NULL, NULL } + }; + + setup_backend(objs); + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0)); + cl_git_pass(git_odb_read(&_obj, _odb, &_oid)); + + assert_object_contains(_obj, objs[0].content); +} + +void test_odb_backend_simple__read_prefix_succeeds(void) +{ + const fake_object objs[] = { + { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4632")); + cl_git_pass(git_odb_read(&_obj, _odb, &_oid)); + + assert_object_contains(_obj, objs[0].content); +} + +void test_odb_backend_simple__read_prefix_of_nonexisting_object_fails(void) +{ + const fake_object objs[] = { + { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" }, + { NULL, NULL } + }; + char *hash = "f6ea0495187600e8"; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstrn(&_oid, hash, strlen(hash))); + cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&_obj, _odb, &_oid)); +} + +void test_odb_backend_simple__read_with_ambiguous_prefix_fails(void) +{ + const fake_object objs[] = { + { "1234567890111111111111111111111111111111", "first content" }, + { "1234567890222222222222222222222222222222", "second content" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + cl_git_fail_with(GIT_EAMBIGUOUS, git_odb_read_prefix(&_obj, _odb, &_oid, 7)); +} + +void test_odb_backend_simple__read_with_highly_ambiguous_prefix(void) +{ + const fake_object objs[] = { + { "1234567890111111111111111111111111111111", "first content" }, + { "1234567890111111111111111111111111111112", "second content" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0)); + cl_git_fail_with(GIT_EAMBIGUOUS, git_odb_read_prefix(&_obj, _odb, &_oid, 39)); + cl_git_pass(git_odb_read_prefix(&_obj, _odb, &_oid, 40)); + assert_object_contains(_obj, objs[0].content); +} + +void test_odb_backend_simple__exists_succeeds(void) +{ + const fake_object objs[] = { + { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + cl_assert(git_odb_exists(_odb, &_oid)); +} + +void test_odb_backend_simple__exists_fails_for_nonexisting_object(void) +{ + const fake_object objs[] = { + { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4633")); + cl_assert(git_odb_exists(_odb, &_oid) == 0); +} + +void test_odb_backend_simple__exists_prefix_succeeds(void) +{ + const fake_object objs[] = { + { "1234567890111111111111111111111111111111", "first content" }, + { "1234567890222222222222222222222222222222", "second content" }, + { NULL, NULL } + }; + git_oid found; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + cl_git_pass(git_odb_exists_prefix(&found, _odb, &_oid, 12)); + cl_assert(git_oid_equal(&found, &_oid)); +} + +void test_odb_backend_simple__exists_with_ambiguous_prefix_fails(void) +{ + const fake_object objs[] = { + { "1234567890111111111111111111111111111111", "first content" }, + { "1234567890222222222222222222222222222222", "second content" }, + { NULL, NULL } + }; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + cl_git_fail_with(GIT_EAMBIGUOUS, git_odb_exists_prefix(NULL, _odb, &_oid, 7)); +} + +void test_odb_backend_simple__exists_with_highly_ambiguous_prefix(void) +{ + const fake_object objs[] = { + { "1234567890111111111111111111111111111111", "first content" }, + { "1234567890111111111111111111111111111112", "second content" }, + { NULL, NULL } + }; + git_oid found; + + setup_backend(objs); + + cl_git_pass(git_oid_fromstr(&_oid, objs[0].oid)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0)); + cl_git_fail_with(GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &_oid, 39)); + cl_git_pass(git_odb_exists_prefix(&found, _odb, &_oid, 40)); + cl_assert(git_oid_equal(&found, &_oid)); +} diff --git a/tests/odb/freshen.c b/tests/odb/freshen.c index d8d6c029a..9d3cf51dc 100644 --- a/tests/odb/freshen.c +++ b/tests/odb/freshen.c @@ -17,36 +17,126 @@ void test_odb_freshen__cleanup(void) cl_git_sandbox_cleanup(); } -#define LOOSE_STR "hey\n" -#define LOOSE_ID "1385f264afb75a56a5bec74243be9b367ba4ca08" -#define LOOSE_FN "13/85f264afb75a56a5bec74243be9b367ba4ca08" +static void set_time_wayback(struct stat *out, const char *fn) +{ + git_buf fullpath = GIT_BUF_INIT; + struct p_timeval old[2]; -void test_odb_freshen__loose_object(void) + old[0].tv_sec = 1234567890; + old[0].tv_usec = 0; + old[1].tv_sec = 1234567890; + old[1].tv_usec = 0; + + git_buf_joinpath(&fullpath, "testrepo.git/objects", fn); + + cl_must_pass(p_utimes(git_buf_cstr(&fullpath), old)); + cl_must_pass(p_lstat(git_buf_cstr(&fullpath), out)); + git_buf_free(&fullpath); +} + +#define LOOSE_STR "my new file\n" +#define LOOSE_BLOB_ID "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd" +#define LOOSE_BLOB_FN "a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd" + +void test_odb_freshen__loose_blob(void) { git_oid expected_id, id; struct stat before, after; - struct p_timeval old_times[2]; - cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_ID)); + cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_BLOB_ID)); + set_time_wayback(&before, LOOSE_BLOB_FN); - old_times[0].tv_sec = 1234567890; - old_times[0].tv_usec = 0; - old_times[1].tv_sec = 1234567890; - old_times[1].tv_usec = 0; - - /* set time to way back */ - cl_must_pass(p_utimes("testrepo.git/objects/" LOOSE_FN, old_times)); - cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &before)); - - cl_git_pass(git_odb_write(&id, odb, LOOSE_STR, CONST_STRLEN(LOOSE_STR), - GIT_OBJ_BLOB)); + /* make sure we freshen a blob */ + cl_git_pass(git_blob_create_frombuffer(&id, repo, LOOSE_STR, CONST_STRLEN(LOOSE_STR))); cl_assert_equal_oid(&expected_id, &id); - cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &after)); + cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_BLOB_FN, &after)); cl_assert(before.st_atime < after.st_atime); cl_assert(before.st_mtime < after.st_mtime); } +#define UNIQUE_STR "doesnt exist in the odb yet\n" +#define UNIQUE_BLOB_ID "78a87d0b8878c5953b9a63015ff4e22a3d898826" +#define UNIQUE_BLOB_FN "78/a87d0b8878c5953b9a63015ff4e22a3d898826" + +void test_odb_freshen__readonly_object(void) +{ + git_oid expected_id, id; + struct stat before, after; + + cl_git_pass(git_oid_fromstr(&expected_id, UNIQUE_BLOB_ID)); + + cl_git_pass(git_blob_create_frombuffer(&id, repo, UNIQUE_STR, CONST_STRLEN(UNIQUE_STR))); + cl_assert_equal_oid(&expected_id, &id); + + set_time_wayback(&before, UNIQUE_BLOB_FN); + cl_assert((before.st_mode & S_IWUSR) == 0); + + cl_git_pass(git_blob_create_frombuffer(&id, repo, UNIQUE_STR, CONST_STRLEN(UNIQUE_STR))); + cl_assert_equal_oid(&expected_id, &id); + cl_must_pass(p_lstat("testrepo.git/objects/" UNIQUE_BLOB_FN, &after)); + + cl_assert(before.st_atime < after.st_atime); + cl_assert(before.st_mtime < after.st_mtime); +} + +#define LOOSE_TREE_ID "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162" +#define LOOSE_TREE_FN "94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162" + +void test_odb_freshen__loose_tree(void) +{ + git_oid expected_id, id; + git_tree *tree; + struct stat before, after; + + cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_TREE_ID)); + set_time_wayback(&before, LOOSE_TREE_FN); + + cl_git_pass(git_tree_lookup(&tree, repo, &expected_id)); + cl_git_pass(git_tree_create_updated(&id, repo, tree, 0, NULL)); + + /* make sure we freshen a tree */ + cl_assert_equal_oid(&expected_id, &id); + cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_TREE_FN, &after)); + + cl_assert(before.st_atime < after.st_atime); + cl_assert(before.st_mtime < after.st_mtime); + + git_tree_free(tree); +} + +void test_odb_freshen__tree_during_commit(void) +{ + git_oid tree_id, parent_id, commit_id; + git_tree *tree; + git_commit *parent; + git_signature *signature; + struct stat before, after; + + cl_git_pass(git_oid_fromstr(&tree_id, LOOSE_TREE_ID)); + cl_git_pass(git_tree_lookup(&tree, repo, &tree_id)); + set_time_wayback(&before, LOOSE_TREE_FN); + + cl_git_pass(git_oid_fromstr(&parent_id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750")); + cl_git_pass(git_commit_lookup(&parent, repo, &parent_id)); + + cl_git_pass(git_signature_new(&signature, + "Refresher", "refresher@example.com", 1488547083, 0)); + + cl_git_pass(git_commit_create(&commit_id, repo, NULL, + signature, signature, NULL, "New commit pointing to old tree", + tree, 1, (const git_commit **)&parent)); + + /* make sure we freshen the tree the commit points to */ + cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_TREE_FN, &after)); + cl_assert(before.st_atime < after.st_atime); + cl_assert(before.st_mtime < after.st_mtime); + + git_signature_free(signature); + git_commit_free(parent); + git_tree_free(tree); +} + #define PACKED_STR "Testing a readme.txt\n" #define PACKED_ID "6336846bd5c88d32f93ae57d846683e61ab5c530" #define PACKED_FN "pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack" diff --git a/tests/odb/loose.c b/tests/odb/loose.c index c91927c4a..2e24d6723 100644 --- a/tests/odb/loose.c +++ b/tests/odb/loose.c @@ -3,6 +3,7 @@ #include "git2/odb_backend.h" #include "posix.h" #include "loose_data.h" +#include "repository.h" #ifdef __ANDROID_API__ # define S_IREAD S_IRUSR @@ -56,11 +57,13 @@ static void test_read_object(object_data *data) void test_odb_loose__initialize(void) { + p_fsync__cnt = 0; cl_must_pass(p_mkdir("test-objects", GIT_OBJECT_DIR_MODE)); } void test_odb_loose__cleanup(void) { + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 0)); cl_fixture_cleanup("test-objects"); } @@ -150,3 +153,55 @@ void test_odb_loose__permissions_readwrite(void) { test_write_object_permission(0777, 0666, 0777, 0666); } + +static void write_object_to_loose_odb(int fsync) +{ + git_odb *odb; + git_odb_backend *backend; + git_oid oid; + + cl_git_pass(git_odb_new(&odb)); + cl_git_pass(git_odb_backend_loose(&backend, "test-objects", -1, fsync, 0777, 0666)); + cl_git_pass(git_odb_add_backend(odb, backend, 1)); + cl_git_pass(git_odb_write(&oid, odb, "Test data\n", 10, GIT_OBJ_BLOB)); + git_odb_free(odb); +} + +void test_odb_loose__does_not_fsync_by_default(void) +{ + write_object_to_loose_odb(0); + cl_assert_equal_sz(0, p_fsync__cnt); +} + +void test_odb_loose__fsync_obeys_odb_option(void) +{ + write_object_to_loose_odb(1); + cl_assert(p_fsync__cnt > 0); +} + +void test_odb_loose__fsync_obeys_global_setting(void) +{ + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 1)); + write_object_to_loose_odb(0); + cl_assert(p_fsync__cnt > 0); +} + +void test_odb_loose__fsync_obeys_repo_setting(void) +{ + git_repository *repo; + git_odb *odb; + git_oid oid; + + cl_git_pass(git_repository_init(&repo, "test-objects", 1)); + cl_git_pass(git_repository_odb__weakptr(&odb, repo)); + cl_git_pass(git_odb_write(&oid, odb, "No fsync here\n", 14, GIT_OBJ_BLOB)); + cl_assert(p_fsync__cnt == 0); + git_repository_free(repo); + + cl_git_pass(git_repository_open(&repo, "test-objects")); + cl_repo_set_bool(repo, "core.fsyncObjectFiles", true); + cl_git_pass(git_repository_odb__weakptr(&odb, repo)); + cl_git_pass(git_odb_write(&oid, odb, "Now fsync\n", 10, GIT_OBJ_BLOB)); + cl_assert(p_fsync__cnt > 0); + git_repository_free(repo); +} diff --git a/tests/online/badssl.c b/tests/online/badssl.c index aa4c24d9c..6524fcd8e 100644 --- a/tests/online/badssl.c +++ b/tests/online/badssl.c @@ -4,7 +4,7 @@ static git_repository *g_repo; -#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) +#ifdef GIT_HTTPS static bool g_has_ssl = true; #else static bool g_has_ssl = false; diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c index 49a106d98..c73d3974e 100644 --- a/tests/pack/indexer.c +++ b/tests/pack/indexer.c @@ -85,7 +85,7 @@ void test_pack_indexer__fix_thin(void) cl_assert_equal_i(stats.indexed_objects, 2); cl_assert_equal_i(stats.local_objects, 1); - git_oid_fromstr(&should_id, "11f0f69b334728fdd8bc86b80499f22f29d85b15"); + git_oid_fromstr(&should_id, "fefdb2d740a3a6b6c03a0c7d6ce431c6d5810e13"); cl_assert_equal_oid(&should_id, git_indexer_hash(idx)); git_indexer_free(idx); @@ -102,7 +102,7 @@ void test_pack_indexer__fix_thin(void) int fd; ssize_t read; struct stat st; - const char *name = "pack-11f0f69b334728fdd8bc86b80499f22f29d85b15.pack"; + const char *name = "pack-fefdb2d740a3a6b6c03a0c7d6ce431c6d5810e13.pack"; fd = p_open(name, O_RDONLY); cl_assert(fd != -1); @@ -125,3 +125,44 @@ void test_pack_indexer__fix_thin(void) git_indexer_free(idx); } } + +static int find_tmp_file_recurs(void *opaque, git_buf *path) +{ + int error = 0; + git_buf *first_tmp_file = opaque; + struct stat st; + + if ((error = p_lstat_posixly(path->ptr, &st)) < 0) + return error; + + if (S_ISDIR(st.st_mode)) + return git_path_direach(path, 0, find_tmp_file_recurs, opaque); + + /* This is the template that's used in git_futils_mktmp. */ + if (strstr(git_buf_cstr(path), "_git2_") != NULL) + return git_buf_sets(first_tmp_file, git_buf_cstr(path)); + + return 0; +} + +void test_pack_indexer__no_tmp_files(void) +{ + git_indexer *idx = NULL; + git_buf path = GIT_BUF_INIT; + git_buf first_tmp_file = GIT_BUF_INIT; + + /* Precondition: there are no temporary files. */ + cl_git_pass(git_buf_sets(&path, clar_sandbox_path())); + cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path)); + git_buf_free(&path); + cl_assert(git_buf_len(&first_tmp_file) == 0); + + cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); + git_indexer_free(idx); + + cl_git_pass(git_buf_sets(&path, clar_sandbox_path())); + cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path)); + git_buf_free(&path); + cl_assert(git_buf_len(&first_tmp_file) == 0); + git_buf_free(&first_tmp_file); +} diff --git a/tests/pack/packbuilder.c b/tests/pack/packbuilder.c index 29f3e2d64..13ee07353 100644 --- a/tests/pack/packbuilder.c +++ b/tests/pack/packbuilder.c @@ -23,6 +23,7 @@ void test_pack_packbuilder__initialize(void) cl_git_pass(git_vector_init(&_commits, 0, NULL)); _commits_is_initialized = 1; memset(&_stats, 0, sizeof(_stats)); + p_fsync__cnt = 0; } void test_pack_packbuilder__cleanup(void) @@ -30,6 +31,8 @@ void test_pack_packbuilder__cleanup(void) git_oid *o; unsigned int i; + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 0)); + if (_commits_is_initialized) { _commits_is_initialized = 0; git_vector_foreach(&_commits, i, o) { @@ -113,7 +116,7 @@ void test_pack_packbuilder__create_pack(void) * $ cd tests/resources/testrepo.git * $ git rev-list --objects HEAD | \ * git pack-objects -q --no-reuse-delta --threads=1 pack - * $ sha1sum git-80e61eb315239ef3c53033e37fee43b744d57122.pack + * $ sha1sum pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack * 5d410bdf97cf896f9007681b92868471d636954b * */ @@ -142,7 +145,7 @@ void test_pack_packbuilder__get_hash(void) git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL); git_oid_fmt(hex, git_packbuilder_hash(_packbuilder)); - cl_assert_equal_s(hex, "80e61eb315239ef3c53033e37fee43b744d57122"); + cl_assert_equal_s(hex, "7f5fa362c664d68ba7221259be1cbd187434b2f0"); } static void test_write_pack_permission(mode_t given, mode_t expected) @@ -166,10 +169,10 @@ static void test_write_pack_permission(mode_t given, mode_t expected) mask = p_umask(0); p_umask(mask); - cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.idx", &statbuf)); + cl_git_pass(p_stat("pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.idx", &statbuf)); cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask); - cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.pack", &statbuf)); + cl_git_pass(p_stat("pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack", &statbuf)); cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask); } @@ -188,6 +191,40 @@ void test_pack_packbuilder__permissions_readwrite(void) test_write_pack_permission(0666, 0666); } +void test_pack_packbuilder__does_not_fsync_by_default(void) +{ + seed_packbuilder(); + git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL); + cl_assert_equal_sz(0, p_fsync__cnt); +} + +/* We fsync the packfile and index. On non-Windows, we also fsync + * the parent directories. + */ +#ifdef GIT_WIN32 +static int expected_fsyncs = 2; +#else +static int expected_fsyncs = 4; +#endif + +void test_pack_packbuilder__fsync_global_setting(void) +{ + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 1)); + p_fsync__cnt = 0; + seed_packbuilder(); + git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL); + cl_assert_equal_sz(expected_fsyncs, p_fsync__cnt); +} + +void test_pack_packbuilder__fsync_repo_setting(void) +{ + cl_repo_set_bool(_repo, "core.fsyncObjectFiles", true); + p_fsync__cnt = 0; + seed_packbuilder(); + git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL); + cl_assert_equal_sz(expected_fsyncs, p_fsync__cnt); +} + static int foreach_cb(void *buf, size_t len, void *payload) { git_indexer *idx = (git_indexer *) payload; diff --git a/tests/rebase/submodule.c b/tests/rebase/submodule.c new file mode 100644 index 000000000..7a38ab89f --- /dev/null +++ b/tests/rebase/submodule.c @@ -0,0 +1,65 @@ +#include "clar_libgit2.h" +#include "git2/checkout.h" +#include "git2/rebase.h" +#include "posix.h" +#include "signature.h" + +#include + +static git_repository *repo; +static git_signature *signature; + +// Fixture setup and teardown +void test_rebase_submodule__initialize(void) +{ + repo = cl_git_sandbox_init("rebase-submodule"); + cl_git_pass(git_signature_new(&signature, + "Rebaser", "rebaser@rebaser.rb", 1405694510, 0)); +} + +void test_rebase_submodule__cleanup(void) +{ + git_signature_free(signature); + cl_git_sandbox_cleanup(); +} + +void test_rebase_submodule__init_untracked(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_buf untracked_path = GIT_BUF_INIT; + FILE *fp; + git_submodule *submodule; + git_config *config; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + git_repository_config(&config, repo); + + cl_git_pass(git_config_set_string(config, "submodule.my-submodule.url", git_repository_path(repo))); + + git_config_free(config); + + cl_git_pass(git_submodule_lookup(&submodule, repo, "my-submodule")); + cl_git_pass(git_submodule_update(submodule, 1, NULL)); + + git_buf_printf(&untracked_path, "%s/my-submodule/untracked", git_repository_workdir(repo)); + fp = fopen(git_buf_cstr(&untracked_path), "w"); + fprintf(fp, "An untracked file in a submodule should not block a rebase\n"); + fclose(fp); + git_buf_free(&untracked_path); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL)); + + git_submodule_free(submodule); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} diff --git a/tests/refs/branches/upstream.c b/tests/refs/branches/upstream.c index 8f2e7a2ca..82f5665d8 100644 --- a/tests/refs/branches/upstream.c +++ b/tests/refs/branches/upstream.c @@ -175,7 +175,7 @@ void test_refs_branches_upstream__no_fetch_refspec(void) cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/test")); cl_git_pass(git_reference_create(&ref, repo, "refs/remotes/matching/master", git_reference_target(branch), 1, "fetch")); cl_git_fail(git_branch_set_upstream(branch, "matching/master")); - cl_assert_equal_s("Could not determine remote for 'refs/remotes/matching/master'", + cl_assert_equal_s("could not determine remote for 'refs/remotes/matching/master'", giterr_last()->message); /* we can't set it automatically, so let's test the user setting it by hand */ diff --git a/tests/refs/create.c b/tests/refs/create.c index 6d5a5f1f6..469cddd1e 100644 --- a/tests/refs/create.c +++ b/tests/refs/create.c @@ -12,19 +12,22 @@ static git_repository *g_repo; void test_refs_create__initialize(void) { - g_repo = cl_git_sandbox_init("testrepo"); + g_repo = cl_git_sandbox_init("testrepo"); + p_fsync__cnt = 0; } void test_refs_create__cleanup(void) { - cl_git_sandbox_cleanup(); + cl_git_sandbox_cleanup(); cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, 1)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 0)); } void test_refs_create__symbolic(void) { - // create a new symbolic reference + /* create a new symbolic reference */ git_reference *new_reference, *looked_up_ref, *resolved_ref; git_repository *repo2; git_oid id; @@ -65,9 +68,57 @@ void test_refs_create__symbolic(void) git_reference_free(resolved_ref); } +void test_refs_create__symbolic_with_arbitrary_content(void) +{ + git_reference *new_reference, *looked_up_ref; + git_repository *repo2; + git_oid id; + + const char *new_head_tracker = "ANOTHER_HEAD_TRACKER"; + const char *arbitrary_target = "ARBITRARY DATA"; + + git_oid_fromstr(&id, current_master_tip); + + /* Attempt to create symbolic ref with arbitrary data in target + * fails by default + */ + cl_git_fail(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, arbitrary_target, 0, NULL)); + + git_libgit2_opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, 0); + + /* With strict target validation disabled, ref creation succeeds */ + cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, arbitrary_target, 0, NULL)); + + /* Ensure the reference can be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker)); + cl_assert(git_reference_type(looked_up_ref) & GIT_REF_SYMBOLIC); + cl_assert(reference_is_packed(looked_up_ref) == 0); + cl_assert_equal_s(looked_up_ref->name, new_head_tracker); + git_reference_free(looked_up_ref); + + /* Ensure the target is what we expect it to be */ + cl_assert_equal_s(git_reference_symbolic_target(new_reference), arbitrary_target); + + /* Similar test with a fresh new repository object */ + cl_git_pass(git_repository_open(&repo2, "testrepo")); + + /* Ensure the reference can be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker)); + cl_assert(git_reference_type(looked_up_ref) & GIT_REF_SYMBOLIC); + cl_assert(reference_is_packed(looked_up_ref) == 0); + cl_assert_equal_s(looked_up_ref->name, new_head_tracker); + + /* Ensure the target is what we expect it to be */ + cl_assert_equal_s(git_reference_symbolic_target(new_reference), arbitrary_target); + + git_repository_free(repo2); + git_reference_free(new_reference); + git_reference_free(looked_up_ref); +} + void test_refs_create__deep_symbolic(void) { - // create a deep symbolic reference + /* create a deep symbolic reference */ git_reference *new_reference, *looked_up_ref, *resolved_ref; git_oid id; @@ -87,7 +138,7 @@ void test_refs_create__deep_symbolic(void) void test_refs_create__oid(void) { - // create a new OID reference + /* create a new OID reference */ git_reference *new_reference, *looked_up_ref; git_repository *repo2; git_oid id; @@ -248,3 +299,69 @@ void test_refs_create__creating_a_loose_ref_with_invalid_windows_name(void) test_win32_name("refs/heads/com1"); } + +/* Creating a loose ref involves fsync'ing the reference, the + * reflog and (on non-Windows) the containing directories. + * Creating a packed ref involves fsync'ing the packed ref file + * and (on non-Windows) the containing directory. + */ +#ifdef GIT_WIN32 +static int expected_fsyncs_create = 2, expected_fsyncs_compress = 1; +#else +static int expected_fsyncs_create = 4, expected_fsyncs_compress = 2; +#endif + +static void count_fsyncs(size_t *create_count, size_t *compress_count) +{ + git_reference *ref = NULL; + git_refdb *refdb; + git_oid id; + + p_fsync__cnt = 0; + + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/fsync_test", &id, 0, "log message")); + git_reference_free(ref); + + *create_count = p_fsync__cnt; + p_fsync__cnt = 0; + + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); + + *compress_count = p_fsync__cnt; + p_fsync__cnt = 0; +} + +void test_refs_create__does_not_fsync_by_default(void) +{ + size_t create_count, compress_count; + count_fsyncs(&create_count, &compress_count); + + cl_assert_equal_i(0, create_count); + cl_assert_equal_i(0, compress_count); +} + +void test_refs_create__fsyncs_when_global_opt_set(void) +{ + size_t create_count, compress_count; + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 1)); + count_fsyncs(&create_count, &compress_count); + + cl_assert_equal_i(expected_fsyncs_create, create_count); + cl_assert_equal_i(expected_fsyncs_compress, compress_count); +} + +void test_refs_create__fsyncs_when_repo_config_set(void) +{ + size_t create_count, compress_count; + + cl_repo_set_bool(g_repo, "core.fsyncObjectFiles", true); + + count_fsyncs(&create_count, &compress_count); + + cl_assert_equal_i(expected_fsyncs_create, create_count); + cl_assert_equal_i(expected_fsyncs_compress, compress_count); +} diff --git a/tests/refs/list.c b/tests/refs/list.c index 374943b05..f7ca3f707 100644 --- a/tests/refs/list.c +++ b/tests/refs/list.c @@ -36,7 +36,7 @@ void test_refs_list__all(void) /* We have exactly 12 refs in total if we include the packed ones: * there is a reference that exists both in the packfile and as * loose, but we only list it once */ - cl_assert_equal_i((int)ref_list.count, 15); + cl_assert_equal_i((int)ref_list.count, 17); git_strarray_free(&ref_list); } @@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten "144344043ba4d4a405da03de3844aa829ae8be0e\n"); cl_git_pass(git_reference_list(&ref_list, g_repo)); - cl_assert_equal_i((int)ref_list.count, 15); + cl_assert_equal_i((int)ref_list.count, 17); git_strarray_free(&ref_list); } diff --git a/tests/refs/namespaces.c b/tests/refs/namespaces.c new file mode 100644 index 000000000..bb6bb1ce0 --- /dev/null +++ b/tests/refs/namespaces.c @@ -0,0 +1,36 @@ +#include "clar_libgit2.h" + +#include "repository.h" + +static git_repository *g_repo; + +void test_refs_namespaces__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_namespaces__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_namespaces__get_and_set(void) +{ + cl_assert_equal_s(NULL, git_repository_get_namespace(g_repo)); + + cl_git_pass(git_repository_set_namespace(g_repo, "namespace")); + cl_assert_equal_s("namespace", git_repository_get_namespace(g_repo)); + + cl_git_pass(git_repository_set_namespace(g_repo, NULL)); + cl_assert_equal_s(NULL, git_repository_get_namespace(g_repo)); +} + +void test_refs_namespaces__namespace_doesnt_show_normal_refs(void) +{ + static git_strarray ref_list; + + cl_git_pass(git_repository_set_namespace(g_repo, "namespace")); + cl_git_pass(git_reference_list(&ref_list, g_repo)); + cl_assert_equal_i(0, ref_list.count); + git_strarray_free(&ref_list); +} diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index fdb15502c..252242152 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -4,7 +4,7 @@ #include "git2/reflog.h" #include "reflog.h" - +static const char *merge_reflog_message = "commit (merge): Merge commit"; static const char *new_ref = "refs/heads/test-reflog"; static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; #define commit_msg "commit: bla bla" @@ -261,7 +261,7 @@ void test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_returns_error error = giterr_last(); cl_assert(error != NULL); - cl_assert_equal_s("Unable to parse OID - contains invalid characters", error->message); + cl_assert_equal_s("unable to parse OID - contains invalid characters", error->message); git_reference_free(ref); git_buf_free(&logpath); @@ -448,3 +448,45 @@ void test_refs_reflog_reflog__logallrefupdates_nonbare_set_false(void) assert_no_reflog_update(); } + +void test_refs_reflog_reflog__show_merge_for_merge_commits(void) +{ + git_oid b1_oid; + git_oid b2_oid; + git_oid merge_commit_oid; + git_commit *b1_commit; + git_commit *b2_commit; + git_signature *s; + git_commit *parent_commits[2]; + git_tree *tree; + git_reflog *log; + const git_reflog_entry *entry; + + cl_git_pass(git_signature_now(&s, "alice", "alice@example.com")); + + cl_git_pass(git_reference_name_to_id(&b1_oid, g_repo, "HEAD")); + cl_git_pass(git_reference_name_to_id(&b2_oid, g_repo, "refs/heads/test")); + + cl_git_pass(git_commit_lookup(&b1_commit, g_repo, &b1_oid)); + cl_git_pass(git_commit_lookup(&b2_commit, g_repo, &b2_oid)); + + parent_commits[0] = b1_commit; + parent_commits[1] = b2_commit; + + cl_git_pass(git_commit_tree(&tree, b1_commit)); + + cl_git_pass(git_commit_create(&merge_commit_oid, + g_repo, "HEAD", s, s, NULL, + "Merge commit", tree, + 2, (const struct git_commit **) parent_commits)); + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s(merge_reflog_message, git_reflog_entry_message(entry)); + + git_reflog_free(log); + git_tree_free(tree); + git_commit_free(b1_commit); + git_commit_free(b2_commit); + git_signature_free(s); +} diff --git a/tests/refs/revparse.c b/tests/refs/revparse.c index c22c30440..459188cf7 100644 --- a/tests/refs/revparse.c +++ b/tests/refs/revparse.c @@ -122,6 +122,14 @@ static void test_id( test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo); } +static void test_invalid_revspec(const char* invalid_spec) +{ + git_revspec revspec; + + cl_assert_equal_i( + GIT_EINVALIDSPEC, git_revparse(&revspec, g_repo, invalid_spec)); +} + void test_refs_revparse__initialize(void) { cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); @@ -749,6 +757,33 @@ void test_refs_revparse__parses_range_operator(void) "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE); + + test_id("HEAD~3..", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + GIT_REVPARSE_RANGE); + + test_id("HEAD~3...", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE); + + test_id("..HEAD~3", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + GIT_REVPARSE_RANGE); + + test_id("...HEAD~3", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", + GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE); + + test_id("...", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE); + + test_invalid_revspec(".."); } void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void) diff --git a/tests/repo/discover.c b/tests/repo/discover.c index 48aa27581..eadd055e9 100644 --- a/tests/repo/discover.c +++ b/tests/repo/discover.c @@ -33,7 +33,7 @@ static void ensure_repository_discover(const char *start_path, git_buf_attach(&resolved, p_realpath(expected_path, NULL), 0); cl_assert(resolved.size > 0); cl_git_pass(git_path_to_dir(&resolved)); - cl_git_pass(git_repository_discover(&found_path, start_path, 0, ceiling_dirs)); + cl_git_pass(git_repository_discover(&found_path, start_path, 1, ceiling_dirs)); cl_assert_equal_s(found_path.ptr, resolved.ptr); @@ -199,3 +199,12 @@ void test_repo_discover__discovery_starting_at_file_succeeds(void) ensure_repository_discover(SUB_REPOSITORY_FOLDER "/file", ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR); } + +void test_repo_discover__discovery_starting_at_system_root_causes_no_hang(void) +{ +#ifdef GIT_WIN32 + git_buf out = GIT_BUF_INIT; + cl_git_fail(git_repository_discover(&out, "C:/", 0, NULL)); + cl_git_fail(git_repository_discover(&out, "//localhost/", 0, NULL)); +#endif +} diff --git a/tests/repo/env.c b/tests/repo/env.c index 5a89c0d49..6404f88e9 100644 --- a/tests/repo/env.c +++ b/tests/repo/env.c @@ -56,8 +56,8 @@ static int GIT_FORMAT_PRINTF(2, 3) cl_setenv_printf(const char *name, const char static void env_pass_(const char *path, const char *file, int line) { git_repository *repo; - cl_git_pass_(git_repository_open_ext(NULL, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, line); - cl_git_pass_(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, line); + cl_git_expect(git_repository_open_ext(NULL, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, line); + cl_git_expect(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, line); cl_assert_at_line(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0, file, line); cl_assert_at_line(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0, file, line); cl_assert_at_line(!git_repository_is_bare(repo), file, line); @@ -98,24 +98,24 @@ static void env_check_objects_(bool a, bool t, bool p, const char *file, int lin cl_git_pass(git_oid_fromstr(&oid_a, "45141a79a77842c59a63229403220a4e4be74e3d")); cl_git_pass(git_oid_fromstr(&oid_t, "1385f264afb75a56a5bec74243be9b367ba4ca08")); cl_git_pass(git_oid_fromstr(&oid_p, "0df1a5865c8abfc09f1f2182e6a31be550e99f07")); - cl_git_pass_(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, line); + cl_git_expect(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, line); if (a) { - cl_git_pass_(git_object_lookup(&object, repo, &oid_a, GIT_OBJ_BLOB), file, line); + cl_git_expect(git_object_lookup(&object, repo, &oid_a, GIT_OBJ_BLOB), 0, file, line); git_object_free(object); } else { cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_a, GIT_OBJ_BLOB), file, line); } if (t) { - cl_git_pass_(git_object_lookup(&object, repo, &oid_t, GIT_OBJ_BLOB), file, line); + cl_git_expect(git_object_lookup(&object, repo, &oid_t, GIT_OBJ_BLOB), 0, file, line); git_object_free(object); } else { cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_t, GIT_OBJ_BLOB), file, line); } if (p) { - cl_git_pass_(git_object_lookup(&object, repo, &oid_p, GIT_OBJ_COMMIT), file, line); + cl_git_expect(git_object_lookup(&object, repo, &oid_p, GIT_OBJ_COMMIT), 0, file, line); git_object_free(object); } else { cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_p, GIT_OBJ_COMMIT), file, line); diff --git a/tests/repo/head.c b/tests/repo/head.c index 31c228777..d02116087 100644 --- a/tests/repo/head.c +++ b/tests/repo/head.c @@ -261,15 +261,19 @@ void test_repo_head__setting_head_updates_reflog(void) cl_git_pass(git_revparse_single(&tag, repo, "tags/test")); cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag))); cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked")); + cl_git_pass(git_repository_set_head(repo, "refs/tags/test")); + cl_git_pass(git_repository_set_head(repo, "refs/remotes/test/master")); - test_reflog(repo, 2, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from master to haacked"); - test_reflog(repo, 1, NULL, "tags/test^{commit}", "foo@example.com", "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d"); - test_reflog(repo, 0, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked"); + test_reflog(repo, 4, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from master to haacked"); + test_reflog(repo, 3, NULL, "tags/test^{commit}", "foo@example.com", "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d"); + test_reflog(repo, 2, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked"); + test_reflog(repo, 1, "refs/heads/haacked", "tags/test^{commit}", "foo@example.com", "checkout: moving from haacked to test"); + test_reflog(repo, 0, "tags/test^{commit}", "refs/remotes/test/master", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to test/master"); cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "haacked~0")); cl_git_pass(git_repository_set_head_detached_from_annotated(repo, annotated)); - test_reflog(repo, 0, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from haacked to haacked~0"); + test_reflog(repo, 0, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0"); git_annotated_commit_free(annotated); git_object_free(tag); diff --git a/tests/repo/open.c b/tests/repo/open.c index 6114ad2e1..3239b6fec 100644 --- a/tests/repo/open.c +++ b/tests/repo/open.c @@ -398,7 +398,8 @@ void test_repo_open__force_bare(void) cl_git_fail(git_repository_open_bare(&barerepo, "alternate/subdir/sub2")); cl_git_pass(git_repository_open_ext( - &barerepo, "alternate/subdir/sub2", GIT_REPOSITORY_OPEN_BARE, NULL)); + &barerepo, "alternate/subdir/sub2", + GIT_REPOSITORY_OPEN_BARE|GIT_REPOSITORY_OPEN_CROSS_FS, NULL)); cl_assert(git_repository_is_bare(barerepo)); git_repository_free(barerepo); } diff --git a/tests/resources/indexv4/.gitted/HEAD b/tests/resources/indexv4/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 Binary files /dev/null and b/tests/resources/indexv4/.gitted/HEAD differ diff --git a/tests/resources/indexv4/.gitted/config b/tests/resources/indexv4/.gitted/config new file mode 100644 index 000000000..515f48362 Binary files /dev/null and b/tests/resources/indexv4/.gitted/config differ diff --git a/tests/resources/indexv4/.gitted/index b/tests/resources/indexv4/.gitted/index new file mode 100644 index 000000000..e8fc61736 Binary files /dev/null and b/tests/resources/indexv4/.gitted/index differ diff --git a/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99 b/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99 new file mode 100644 index 000000000..cedd594b0 Binary files /dev/null and b/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99 differ diff --git a/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7 b/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7 new file mode 100644 index 000000000..0ddc1d1a9 Binary files /dev/null and b/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7 differ diff --git a/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 000000000..711223894 Binary files /dev/null and b/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/tests/resources/indexv4/.gitted/refs/heads/master b/tests/resources/indexv4/.gitted/refs/heads/master new file mode 100644 index 000000000..f3e960eb3 Binary files /dev/null and b/tests/resources/indexv4/.gitted/refs/heads/master differ diff --git a/tests/resources/indexv4/file.tx b/tests/resources/indexv4/file.tx new file mode 100644 index 000000000..e69de29bb diff --git a/tests/resources/indexv4/file.txt b/tests/resources/indexv4/file.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/resources/indexv4/file.txz b/tests/resources/indexv4/file.txz new file mode 100644 index 000000000..e69de29bb diff --git a/tests/resources/indexv4/foo b/tests/resources/indexv4/foo new file mode 100644 index 000000000..e69de29bb diff --git a/tests/resources/indexv4/zzz b/tests/resources/indexv4/zzz new file mode 100644 index 000000000..e69de29bb diff --git a/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0 b/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0 new file mode 100644 index 000000000..0d658237b Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765 b/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765 new file mode 100644 index 000000000..95327ed64 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355 b/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355 new file mode 100644 index 000000000..86a21ad50 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a b/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a new file mode 100644 index 000000000..bc74da5ba Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a differ diff --git a/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2 b/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2 new file mode 100644 index 000000000..809a5b38b Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a b/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a new file mode 100644 index 000000000..d4d93f508 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a differ diff --git a/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6 b/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6 new file mode 100644 index 000000000..598c6a7a6 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921 b/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921 new file mode 100644 index 000000000..2d3d94718 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb b/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb new file mode 100644 index 000000000..ae529fe87 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb differ diff --git a/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388 b/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388 new file mode 100644 index 000000000..b655d7c40 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee b/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee new file mode 100644 index 000000000..144225df1 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee differ diff --git a/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56 b/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56 new file mode 100644 index 000000000..d4ec2b972 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 000000000..711223894 Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36 b/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36 new file mode 100644 index 000000000..d785511fb Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36 differ diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1 b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1 new file mode 100644 index 000000000..0ed914fef Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1 differ diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2 b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2 new file mode 100644 index 000000000..8e020ccfc Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2 differ diff --git a/tests/resources/rebase-submodule/.gitmodules b/tests/resources/rebase-submodule/.gitmodules new file mode 100644 index 000000000..f36de77de Binary files /dev/null and b/tests/resources/rebase-submodule/.gitmodules differ diff --git a/tests/resources/rebase-submodule/.gitted/HEAD b/tests/resources/rebase-submodule/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/HEAD differ diff --git a/tests/resources/rebase-submodule/.gitted/ORIG_HEAD b/tests/resources/rebase-submodule/.gitted/ORIG_HEAD new file mode 100644 index 000000000..a1ccbb40d Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/ORIG_HEAD differ diff --git a/tests/resources/rebase-submodule/.gitted/config b/tests/resources/rebase-submodule/.gitted/config new file mode 100644 index 000000000..af2095f84 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/config differ diff --git a/tests/resources/rebase-submodule/.gitted/description b/tests/resources/rebase-submodule/.gitted/description new file mode 100644 index 000000000..498b267a8 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/description differ diff --git a/tests/resources/rebase-submodule/.gitted/index b/tests/resources/rebase-submodule/.gitted/index new file mode 100644 index 000000000..b63efabdb Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/index differ diff --git a/tests/resources/rebase-submodule/.gitted/info/exclude b/tests/resources/rebase-submodule/.gitted/info/exclude new file mode 100644 index 000000000..a5196d1be Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/info/exclude differ diff --git a/tests/resources/rebase-submodule/.gitted/info/refs b/tests/resources/rebase-submodule/.gitted/info/refs new file mode 100644 index 000000000..230a6494b Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/info/refs differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294 b/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294 new file mode 100644 index 000000000..308b38697 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294 differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12 b/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12 new file mode 100644 index 000000000..79e4c484f Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12 differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 b/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 new file mode 100644 index 000000000..99b5e6d2c Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a b/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a new file mode 100644 index 000000000..016398531 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5 b/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5 new file mode 100644 index 000000000..d4776d883 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5 differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 b/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 new file mode 100644 index 000000000..6aaf79fcb Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096 b/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096 new file mode 100644 index 000000000..ed1de3ada Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096 differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b b/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b new file mode 100644 index 000000000..ef923e714 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f b/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f new file mode 100644 index 000000000..fe8b15777 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 b/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 new file mode 100644 index 000000000..54f9b6617 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c b/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c new file mode 100644 index 000000000..5acdf362e Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f b/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f new file mode 100644 index 000000000..2bbf28f57 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd b/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd new file mode 100644 index 000000000..cd330a71f Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd differ diff --git a/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d b/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d new file mode 100644 index 000000000..f655d12ea Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d differ diff --git a/tests/resources/rebase-submodule/.gitted/packed-refs b/tests/resources/rebase-submodule/.gitted/packed-refs new file mode 100644 index 000000000..44ac842a1 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/packed-refs differ diff --git a/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus b/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus new file mode 100644 index 000000000..e6adde8e6 Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus differ diff --git a/tests/resources/rebase-submodule/.gitted/refs/heads/master b/tests/resources/rebase-submodule/.gitted/refs/heads/master new file mode 100644 index 000000000..2523d491d Binary files /dev/null and b/tests/resources/rebase-submodule/.gitted/refs/heads/master differ diff --git a/tests/resources/rebase-submodule/asparagus.txt b/tests/resources/rebase-submodule/asparagus.txt new file mode 100644 index 000000000..ffb36e513 Binary files /dev/null and b/tests/resources/rebase-submodule/asparagus.txt differ diff --git a/tests/resources/rebase-submodule/beef.txt b/tests/resources/rebase-submodule/beef.txt new file mode 100644 index 000000000..68f6182f4 Binary files /dev/null and b/tests/resources/rebase-submodule/beef.txt differ diff --git a/tests/resources/rebase-submodule/bouilli.txt b/tests/resources/rebase-submodule/bouilli.txt new file mode 100644 index 000000000..4b7c56500 Binary files /dev/null and b/tests/resources/rebase-submodule/bouilli.txt differ diff --git a/tests/resources/rebase-submodule/gravy.txt b/tests/resources/rebase-submodule/gravy.txt new file mode 100644 index 000000000..c4e6cca3e Binary files /dev/null and b/tests/resources/rebase-submodule/gravy.txt differ diff --git a/tests/resources/rebase-submodule/my-submodule b/tests/resources/rebase-submodule/my-submodule new file mode 160000 index 000000000..efad0b11c --- /dev/null +++ b/tests/resources/rebase-submodule/my-submodule @@ -0,0 +1 @@ +Subproject commit efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 diff --git a/tests/resources/rebase-submodule/oyster.txt b/tests/resources/rebase-submodule/oyster.txt new file mode 100644 index 000000000..68af1fc74 Binary files /dev/null and b/tests/resources/rebase-submodule/oyster.txt differ diff --git a/tests/resources/rebase-submodule/veal.txt b/tests/resources/rebase-submodule/veal.txt new file mode 100644 index 000000000..a7b066537 Binary files /dev/null and b/tests/resources/rebase-submodule/veal.txt differ diff --git a/tests/resources/sha1/hello_c b/tests/resources/sha1/hello_c new file mode 100644 index 000000000..45950b2ad Binary files /dev/null and b/tests/resources/sha1/hello_c differ diff --git a/tests/resources/sha1/shattered-1.pdf b/tests/resources/sha1/shattered-1.pdf new file mode 100644 index 000000000..ba9aaa145 Binary files /dev/null and b/tests/resources/sha1/shattered-1.pdf differ diff --git a/tests/resources/submodules-worktree-child/.gitted b/tests/resources/submodules-worktree-child/.gitted new file mode 100644 index 000000000..03286f522 Binary files /dev/null and b/tests/resources/submodules-worktree-child/.gitted differ diff --git a/tests/resources/submodules-worktree-child/README b/tests/resources/submodules-worktree-child/README new file mode 100644 index 000000000..a8233120f Binary files /dev/null and b/tests/resources/submodules-worktree-child/README differ diff --git a/tests/resources/submodules-worktree-child/branch_file.txt b/tests/resources/submodules-worktree-child/branch_file.txt new file mode 100644 index 000000000..3697d64be Binary files /dev/null and b/tests/resources/submodules-worktree-child/branch_file.txt differ diff --git a/tests/resources/submodules-worktree-child/new.txt b/tests/resources/submodules-worktree-child/new.txt new file mode 100644 index 000000000..a71586c1d Binary files /dev/null and b/tests/resources/submodules-worktree-child/new.txt differ diff --git a/tests/resources/submodules-worktree-parent/.gitmodules b/tests/resources/submodules-worktree-parent/.gitmodules new file mode 100644 index 000000000..78308c925 Binary files /dev/null and b/tests/resources/submodules-worktree-parent/.gitmodules differ diff --git a/tests/resources/submodules-worktree-parent/.gitted b/tests/resources/submodules-worktree-parent/.gitted new file mode 100644 index 000000000..87bd9ae29 Binary files /dev/null and b/tests/resources/submodules-worktree-parent/.gitted differ diff --git a/tests/resources/submodules-worktree-parent/deleted b/tests/resources/submodules-worktree-parent/deleted new file mode 100644 index 000000000..092bfb9bd Binary files /dev/null and b/tests/resources/submodules-worktree-parent/deleted differ diff --git a/tests/resources/submodules-worktree-parent/modified b/tests/resources/submodules-worktree-parent/modified new file mode 100644 index 000000000..092bfb9bd Binary files /dev/null and b/tests/resources/submodules-worktree-parent/modified differ diff --git a/tests/resources/submodules-worktree-parent/unmodified b/tests/resources/submodules-worktree-parent/unmodified new file mode 100644 index 000000000..092bfb9bd Binary files /dev/null and b/tests/resources/submodules-worktree-parent/unmodified differ diff --git a/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent b/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent new file mode 100644 index 000000000..65e988535 Binary files /dev/null and b/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent differ diff --git a/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent b/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent new file mode 100644 index 000000000..32b935853 Binary files /dev/null and b/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent differ diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD new file mode 100644 index 000000000..a07134b85 Binary files /dev/null and b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD differ diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD new file mode 100644 index 000000000..32b935853 Binary files /dev/null and b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD differ diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir new file mode 100644 index 000000000..aab0408ce Binary files /dev/null and b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir differ diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir new file mode 100644 index 000000000..eaaf13b95 Binary files /dev/null and b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir differ diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index new file mode 100644 index 000000000..5b68f18a4 Binary files /dev/null and b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index differ diff --git a/tests/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config index d6dcad12b..8e5571191 100644 Binary files a/tests/resources/submodules/testrepo/.gitted/config and b/tests/resources/submodules/testrepo/.gitted/config differ diff --git a/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child new file mode 100644 index 000000000..dd4650ff8 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child differ diff --git a/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child b/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child new file mode 100644 index 000000000..3d8f0a402 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child differ diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD new file mode 100644 index 000000000..ef82bd4df Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD differ diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD new file mode 100644 index 000000000..3d8f0a402 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD differ diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir new file mode 100644 index 000000000..aab0408ce Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir differ diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir new file mode 100644 index 000000000..b0ef96e11 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir differ diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index new file mode 100644 index 000000000..52a42f966 Binary files /dev/null and b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index differ diff --git a/tests/resources/testrepo-worktree/.gitted b/tests/resources/testrepo-worktree/.gitted new file mode 100644 index 000000000..fe4556a92 Binary files /dev/null and b/tests/resources/testrepo-worktree/.gitted differ diff --git a/tests/resources/testrepo-worktree/README b/tests/resources/testrepo-worktree/README new file mode 100644 index 000000000..a8233120f Binary files /dev/null and b/tests/resources/testrepo-worktree/README differ diff --git a/tests/resources/testrepo-worktree/branch_file.txt b/tests/resources/testrepo-worktree/branch_file.txt new file mode 100644 index 000000000..3697d64be Binary files /dev/null and b/tests/resources/testrepo-worktree/branch_file.txt differ diff --git a/tests/resources/testrepo-worktree/link_to_new.txt b/tests/resources/testrepo-worktree/link_to_new.txt new file mode 120000 index 000000000..c0528fd6c --- /dev/null +++ b/tests/resources/testrepo-worktree/link_to_new.txt @@ -0,0 +1 @@ +new.txt \ No newline at end of file diff --git a/tests/resources/testrepo-worktree/new.txt b/tests/resources/testrepo-worktree/new.txt new file mode 100644 index 000000000..a71586c1d Binary files /dev/null and b/tests/resources/testrepo-worktree/new.txt differ diff --git a/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree b/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree new file mode 100644 index 000000000..93ab5f06f Binary files /dev/null and b/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree differ diff --git a/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae b/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae new file mode 100644 index 000000000..13e3f581a Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae differ diff --git a/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da b/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da new file mode 100644 index 000000000..4df22ec17 Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da differ diff --git a/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3 b/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3 new file mode 100644 index 000000000..c054fc0c4 Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3 differ diff --git a/tests/resources/testrepo/.gitted/refs/heads/merge-conflict b/tests/resources/testrepo/.gitted/refs/heads/merge-conflict new file mode 100644 index 000000000..3e24a24e0 Binary files /dev/null and b/tests/resources/testrepo/.gitted/refs/heads/merge-conflict differ diff --git a/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree b/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree new file mode 100644 index 000000000..f31fe781b Binary files /dev/null and b/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree differ diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD new file mode 100644 index 000000000..1b8637e32 Binary files /dev/null and b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD differ diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir new file mode 100644 index 000000000..aab0408ce Binary files /dev/null and b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir differ diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir new file mode 100644 index 000000000..0d37a5792 Binary files /dev/null and b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir differ diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index new file mode 100644 index 000000000..41141906e Binary files /dev/null and b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index differ diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD new file mode 100644 index 000000000..3bede502e Binary files /dev/null and b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD differ diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c index 89140bc54..547050c68 100644 --- a/tests/revwalk/basic.c +++ b/tests/revwalk/basic.c @@ -177,7 +177,7 @@ void test_revwalk_basic__glob_heads_with_invalid(void) /* walking */; /* git log --branches --oneline | wc -l => 16 */ - cl_assert_equal_i(18, i); + cl_assert_equal_i(19, i); } void test_revwalk_basic__push_head(void) @@ -331,6 +331,40 @@ void test_revwalk_basic__hide_then_push(void) cl_assert_equal_i(i, 0); } +void test_revwalk_basic__topo_crash(void) +{ + git_oid oid; + git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644"); + + revwalk_basic_setup_walk(NULL); + git_revwalk_sorting(_walk, GIT_SORT_TOPOLOGICAL); + + cl_git_pass(git_revwalk_push(_walk, &oid)); + cl_git_pass(git_revwalk_hide(_walk, &oid)); + + git_revwalk_next(&oid, _walk); +} + +void test_revwalk_basic__from_new_to_old(void) +{ + git_oid from_oid, to_oid, oid; + int i = 0; + + revwalk_basic_setup_walk(NULL); + git_revwalk_sorting(_walk, GIT_SORT_TIME); + + cl_git_pass(git_oid_fromstr(&to_oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644")); + cl_git_pass(git_oid_fromstr(&from_oid, "a4a7dce85cf63874e984719f4fdd239f5145052f")); + + cl_git_pass(git_revwalk_push(_walk, &to_oid)); + cl_git_pass(git_revwalk_hide(_walk, &from_oid)); + + while (git_revwalk_next(&oid, _walk) == 0) + i++; + + cl_assert_equal_i(i, 0); +} + void test_revwalk_basic__push_range(void) { revwalk_basic_setup_walk(NULL); diff --git a/tests/status/ignore.c b/tests/status/ignore.c index c4878b2dd..23384fb1d 100644 --- a/tests/status/ignore.c +++ b/tests/status/ignore.c @@ -20,8 +20,8 @@ static void assert_ignored_( bool expected, const char *filepath, const char *file, int line) { int is_ignored = 0; - cl_git_pass_( - git_status_should_ignore(&is_ignored, g_repo, filepath), file, line); + cl_git_expect( + git_status_should_ignore(&is_ignored, g_repo, filepath), 0, file, line); clar__assert( (expected != 0) == (is_ignored != 0), file, line, "expected != is_ignored", filepath, 1); @@ -1077,3 +1077,81 @@ void test_status_ignore__negate_starstar(void) cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "code/projects/foo/bar/packages/repositories.config")); cl_assert_equal_i(0, ignored); } + +void test_status_ignore__ignore_all_toplevel_dirs_include_files(void) +{ + static const char *test_files[] = { + "empty_standard_repo/README.md", + "empty_standard_repo/src/main.c", + "empty_standard_repo/src/foo/foo.c", + "empty_standard_repo/dist/foo.o", + "empty_standard_repo/dist/main.o", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/.gitignore", + "/*/\n" + "!/src\n"); + + assert_is_ignored("dist/foo.o"); + assert_is_ignored("dist/main.o"); + + refute_is_ignored("README.md"); + refute_is_ignored("src/foo.c"); + refute_is_ignored("src/foo/foo.c"); +} + +void test_status_ignore__subdir_ignore_all_toplevel_dirs_include_files(void) +{ + static const char *test_files[] = { + "empty_standard_repo/project/README.md", + "empty_standard_repo/project/src/main.c", + "empty_standard_repo/project/src/foo/foo.c", + "empty_standard_repo/project/dist/foo.o", + "empty_standard_repo/project/dist/main.o", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/project/.gitignore", + "/*/\n" + "!/src\n"); + + assert_is_ignored("project/dist/foo.o"); + assert_is_ignored("project/dist/main.o"); + + refute_is_ignored("project/src/foo.c"); + refute_is_ignored("project/src/foo/foo.c"); + refute_is_ignored("project/README.md"); +} + +void test_status_ignore__subdir_ignore_everything_except_certain_files(void) +{ + static const char *test_files[] = { + "empty_standard_repo/project/README.md", + "empty_standard_repo/project/some_file", + "empty_standard_repo/project/src/main.c", + "empty_standard_repo/project/src/foo/foo.c", + "empty_standard_repo/project/dist/foo.o", + "empty_standard_repo/project/dist/main.o", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/project/.gitignore", + "/*\n" + "!/src\n" + "!README.md\n"); + + assert_is_ignored("project/some_file"); + assert_is_ignored("project/dist/foo.o"); + assert_is_ignored("project/dist/main.o"); + + refute_is_ignored("project/README.md"); + refute_is_ignored("project/src/foo.c"); + refute_is_ignored("project/src/foo/foo.c"); +} diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c index 148f9273e..e36fc44e0 100644 --- a/tests/submodule/lookup.c +++ b/tests/submodule/lookup.c @@ -388,3 +388,28 @@ void test_submodule_lookup__renamed(void) cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data)); cl_assert_equal_i(8, data.count); } + +void test_submodule_lookup_cached(void) { + git_submodule *sm; + git_submodule *sm2; + /* See that the simple tests still pass. */ + + git_repository_submodule_cache_all(g_repo); + test_submodule_lookup__simple_lookup(); + git_repository_submodule_cache_clear(g_repo); + test_submodule_lookup__simple_lookup(); + + /* Check that subsequent calls return different objects when cached. */ + git_repository_submodule_cache_all(g_repo); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); + cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged")); + cl_assert_equal_p(sm, sm2); + git_submodule_free(sm2); + + /* and that we get new objects again after clearing the cache. */ + git_repository_submodule_cache_clear(g_repo); + cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged")); + cl_assert(sm != sm2); + git_submodule_free(sm); + git_submodule_free(sm2); +} diff --git a/tests/submodule/open.c b/tests/submodule/open.c new file mode 100644 index 000000000..0ef01ec24 --- /dev/null +++ b/tests/submodule/open.c @@ -0,0 +1,90 @@ +#include "clar_libgit2.h" +#include "submodule_helpers.h" +#include "path.h" + +static git_repository *g_parent; +static git_repository *g_child; +static git_submodule *g_module; + +void test_submodule_open__initialize(void) +{ + g_parent = setup_fixture_submod2(); +} + +void test_submodule_open__cleanup(void) +{ + git_submodule_free(g_module); + git_repository_free(g_child); + cl_git_sandbox_cleanup(); + g_parent = NULL; + g_child = NULL; + g_module = NULL; +} + +static void assert_sm_valid(git_repository *parent, git_repository *child, const char *sm_name) +{ + git_buf expected = GIT_BUF_INIT, actual = GIT_BUF_INIT; + + /* assert working directory */ + cl_git_pass(git_buf_joinpath(&expected, git_repository_workdir(parent), sm_name)); + cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL)); + cl_git_pass(git_buf_sets(&actual, git_repository_workdir(child))); + cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL)); + cl_assert_equal_s(expected.ptr, actual.ptr); + + git_buf_clear(&expected); + git_buf_clear(&actual); + + /* assert common directory */ + cl_git_pass(git_buf_joinpath(&expected, git_repository_commondir(parent), "modules")); + cl_git_pass(git_buf_joinpath(&expected, expected.ptr, sm_name)); + cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL)); + cl_git_pass(git_buf_sets(&actual, git_repository_commondir(child))); + cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL)); + cl_assert_equal_s(expected.ptr, actual.ptr); + + /* assert git directory */ + cl_git_pass(git_buf_sets(&actual, git_repository_path(child))); + cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL)); + cl_assert_equal_s(expected.ptr, actual.ptr); + + git_buf_free(&expected); + git_buf_free(&actual); +} + +void test_submodule_open__opening_via_lookup_succeeds(void) +{ + cl_git_pass(git_submodule_lookup(&g_module, g_parent, "sm_unchanged")); + cl_git_pass(git_submodule_open(&g_child, g_module)); + assert_sm_valid(g_parent, g_child, "sm_unchanged"); +} + +void test_submodule_open__direct_open_succeeds(void) +{ + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged")); + cl_git_pass(git_repository_open(&g_child, path.ptr)); + assert_sm_valid(g_parent, g_child, "sm_unchanged"); + + git_buf_free(&path); +} + +void test_submodule_open__direct_open_succeeds_for_broken_sm_with_gitdir(void) +{ + git_buf path = GIT_BUF_INIT; + + /* + * This is actually not a valid submodule, but we + * encountered at least one occasion where the gitdir + * file existed inside of a submodule's gitdir. As we are + * now able to open these submodules correctly, we still + * add a test for this. + */ + cl_git_mkfile("submod2/.git/modules/sm_unchanged/gitdir", ".git"); + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged")); + cl_git_pass(git_repository_open(&g_child, path.ptr)); + assert_sm_valid(g_parent, g_child, "sm_unchanged"); + + git_buf_free(&path); +} diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c index 6c2b9cf78..cd541ea86 100644 --- a/tests/submodule/submodule_helpers.c +++ b/tests/submodule/submodule_helpers.c @@ -204,7 +204,7 @@ void assert__submodule_exists( git_submodule *sm; int error = git_submodule_lookup(&sm, repo, name); if (error) - cl_git_report_failure(error, file, line, msg); + cl_git_report_failure(error, 0, file, line, msg); cl_assert_at_line(sm != NULL, file, line); git_submodule_free(sm); } diff --git a/tests/threads/basic.c b/tests/threads/basic.c index a9310bbd4..af6049090 100644 --- a/tests/threads/basic.c +++ b/tests/threads/basic.c @@ -54,12 +54,6 @@ static void *return_normally(void *param) { return param; } - -static void *exit_abruptly(void *param) -{ - git_thread_exit(param); - return NULL; -} #endif void test_threads_basic__exit(void) diff --git a/tests/threads/diff.c b/tests/threads/diff.c index c32811469..256040265 100644 --- a/tests/threads/diff.c +++ b/tests/threads/diff.c @@ -19,12 +19,27 @@ static git_repository *_repo; static git_tree *_a, *_b; static git_atomic _counts[4]; static int _check_counts; +#ifdef GIT_WIN32 +static int _retries; +#endif #define THREADS 20 +void test_threads_diff__initialize(void) +{ +#ifdef GIT_WIN32 + _retries = git_win32__retries; + git_win32__retries = 1; +#endif +} + void test_threads_diff__cleanup(void) { cl_git_sandbox_cleanup(); + +#ifdef GIT_WIN32 + git_win32__retries = _retries; +#endif } static void setup_trees(void) diff --git a/tests/worktree/config.c b/tests/worktree/config.c new file mode 100644 index 000000000..3ab317bb5 --- /dev/null +++ b/tests/worktree/config.c @@ -0,0 +1,45 @@ +#include "clar_libgit2.h" +#include "worktree_helpers.h" + +#define COMMON_REPO "testrepo" +#define WORKTREE_REPO "testrepo-worktree" + +static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); + +void test_worktree_config__initialize(void) +{ + setup_fixture_worktree(&fixture); +} + +void test_worktree_config__cleanup(void) +{ + cleanup_fixture_worktree(&fixture); +} + +void test_worktree_config__open(void) +{ + git_config *cfg; + + cl_git_pass(git_repository_config(&cfg, fixture.worktree)); + cl_assert(cfg != NULL); + + git_config_free(cfg); +} + +void test_worktree_config__set(void) +{ + git_config *cfg; + int32_t val; + + cl_git_pass(git_repository_config(&cfg, fixture.worktree)); + cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5)); + git_config_free(cfg); + + // reopen to verify configuration has been set in the + // common dir + cl_git_pass(git_repository_config(&cfg, fixture.repo)); + cl_git_pass(git_config_get_int32(&val, cfg, "core.dummy")); + cl_assert_equal_i(val, 5); + git_config_free(cfg); +} diff --git a/tests/worktree/merge.c b/tests/worktree/merge.c new file mode 100644 index 000000000..36cc2a6c1 --- /dev/null +++ b/tests/worktree/merge.c @@ -0,0 +1,121 @@ +#include "clar_libgit2.h" + +#include "worktree_helpers.h" +#include "merge/merge_helpers.h" + +#define COMMON_REPO "testrepo" +#define WORKTREE_REPO "testrepo-worktree" + +#define MASTER_BRANCH "refs/heads/master" +#define CONFLICT_BRANCH "refs/heads/merge-conflict" + +#define CONFLICT_BRANCH_FILE_TXT \ + "<<<<<<< HEAD\n" \ + "hi\n" \ + "bye!\n" \ + "=======\n" \ + "conflict\n" \ + ">>>>>>> merge-conflict\n" \ + +static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); + +static const char *merge_files[] = { + GIT_MERGE_HEAD_FILE, + GIT_ORIG_HEAD_FILE, + GIT_MERGE_MODE_FILE, + GIT_MERGE_MSG_FILE, +}; + +void test_worktree_merge__initialize(void) +{ + setup_fixture_worktree(&fixture); +} + +void test_worktree_merge__cleanup(void) +{ + cleanup_fixture_worktree(&fixture); +} + +void test_worktree_merge__merge_head(void) +{ + git_reference *theirs_ref, *ref; + git_annotated_commit *theirs; + + cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH)); + cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref)); + cl_git_pass(git_merge(fixture.worktree, (const git_annotated_commit **)&theirs, 1, NULL, NULL)); + + cl_git_pass(git_reference_lookup(&ref, fixture.worktree, GIT_MERGE_HEAD_FILE)); + + git_reference_free(ref); + git_reference_free(theirs_ref); + git_annotated_commit_free(theirs); +} + +void test_worktree_merge__merge_setup(void) +{ + git_reference *ours_ref, *theirs_ref; + git_annotated_commit *ours, *theirs; + git_buf path = GIT_BUF_INIT; + unsigned i; + + cl_git_pass(git_reference_lookup(&ours_ref, fixture.worktree, MASTER_BRANCH)); + cl_git_pass(git_annotated_commit_from_ref(&ours, fixture.worktree, ours_ref)); + + cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH)); + cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref)); + + cl_git_pass(git_merge__setup(fixture.worktree, + ours, (const git_annotated_commit **)&theirs, 1)); + + for (i = 0; i < ARRAY_SIZE(merge_files); i++) { + git_buf_clear(&path); + cl_git_pass(git_buf_printf(&path, "%s/%s", + fixture.worktree->gitdir, merge_files[i])); + cl_assert(git_path_exists(path.ptr)); + } + + git_buf_free(&path); + git_reference_free(ours_ref); + git_reference_free(theirs_ref); + git_annotated_commit_free(ours); + git_annotated_commit_free(theirs); +} + +void test_worktree_merge__merge_conflict(void) +{ + git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT; + git_reference *theirs_ref; + git_annotated_commit *theirs; + git_index *index; + const git_index_entry *entry; + size_t i, conflicts = 0; + + cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH)); + cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref)); + + cl_git_pass(git_merge(fixture.worktree, + (const git_annotated_commit **)&theirs, 1, NULL, NULL)); + + cl_git_pass(git_repository_index(&index, fixture.worktree)); + for (i = 0; i < git_index_entrycount(index); i++) { + cl_assert(entry = git_index_get_byindex(index, i)); + + if (git_index_entry_is_conflict(entry)) + conflicts++; + } + cl_assert_equal_sz(conflicts, 3); + + git_reference_free(theirs_ref); + git_annotated_commit_free(theirs); + git_index_free(index); + + cl_git_pass(git_buf_joinpath(&path, fixture.worktree->workdir, "branch_file.txt")); + cl_git_pass(git_futils_readbuffer(&buf, path.ptr)); + cl_assert_equal_s(buf.ptr, CONFLICT_BRANCH_FILE_TXT); + + git_buf_free(&path); + git_buf_free(&buf); +} + diff --git a/tests/worktree/open.c b/tests/worktree/open.c new file mode 100644 index 000000000..74b9007d9 --- /dev/null +++ b/tests/worktree/open.c @@ -0,0 +1,143 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "worktree.h" +#include "worktree_helpers.h" + +#define COMMON_REPO "testrepo" +#define WORKTREE_REPO "testrepo-worktree" + +static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); + +static void assert_worktree_valid(git_repository *wt, const char *parentdir, const char *wtdir) +{ + git_buf path = GIT_BUF_INIT; + + cl_assert(wt->is_worktree); + + cl_git_pass(git_buf_joinpath(&path, clar_sandbox_path(), wtdir)); + cl_git_pass(git_path_prettify(&path, path.ptr, NULL)); + cl_git_pass(git_path_to_dir(&path)); + cl_assert_equal_s(wt->workdir, path.ptr); + + cl_git_pass(git_buf_joinpath(&path, path.ptr, ".git")); + cl_git_pass(git_path_prettify(&path, path.ptr, NULL)); + cl_assert_equal_s(wt->gitlink, path.ptr); + + cl_git_pass(git_buf_joinpath(&path, clar_sandbox_path(), parentdir)); + cl_git_pass(git_buf_joinpath(&path, path.ptr, ".git")); + cl_git_pass(git_buf_joinpath(&path, path.ptr, "worktrees")); + cl_git_pass(git_buf_joinpath(&path, path.ptr, wtdir)); + cl_git_pass(git_path_prettify(&path, path.ptr, NULL)); + cl_git_pass(git_path_to_dir(&path)); + cl_assert_equal_s(wt->gitdir, path.ptr); + + git_buf_free(&path); +} + +void test_worktree_open__initialize(void) +{ + setup_fixture_worktree(&fixture); +} + +void test_worktree_open__cleanup(void) +{ + cleanup_fixture_worktree(&fixture); +} + +void test_worktree_open__repository(void) +{ + assert_worktree_valid(fixture.worktree, COMMON_REPO, WORKTREE_REPO); +} + +void test_worktree_open__repository_through_workdir(void) +{ + git_repository *wt; + + cl_git_pass(git_repository_open(&wt, WORKTREE_REPO)); + assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO); + + git_repository_free(wt); +} + +void test_worktree_open__repository_through_gitlink(void) +{ + git_repository *wt; + + cl_git_pass(git_repository_open(&wt, WORKTREE_REPO "/.git")); + assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO); + + git_repository_free(wt); +} + +void test_worktree_open__repository_through_gitdir(void) +{ + git_buf gitdir_path = GIT_BUF_INIT; + git_repository *wt; + + cl_git_pass(git_buf_joinpath(&gitdir_path, COMMON_REPO, ".git")); + cl_git_pass(git_buf_joinpath(&gitdir_path, gitdir_path.ptr, "worktrees")); + cl_git_pass(git_buf_joinpath(&gitdir_path, gitdir_path.ptr, "testrepo-worktree")); + + cl_git_pass(git_repository_open(&wt, gitdir_path.ptr)); + assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO); + + git_buf_free(&gitdir_path); + git_repository_free(wt); +} + +void test_worktree_open__open_discovered_worktree(void) +{ + git_buf path = GIT_BUF_INIT; + git_repository *repo; + + cl_git_pass(git_repository_discover(&path, + git_repository_workdir(fixture.worktree), false, NULL)); + cl_git_pass(git_repository_open(&repo, path.ptr)); + cl_assert_equal_s(git_repository_workdir(fixture.worktree), + git_repository_workdir(repo)); + + git_buf_free(&path); + git_repository_free(repo); +} + +void test_worktree_open__repository_with_nonexistent_parent(void) +{ + git_repository *repo; + + cleanup_fixture_worktree(&fixture); + + cl_fixture_sandbox(WORKTREE_REPO); + cl_git_pass(p_chdir(WORKTREE_REPO)); + cl_git_pass(cl_rename(".gitted", ".git")); + cl_git_pass(p_chdir("..")); + + cl_git_fail(git_repository_open(&repo, WORKTREE_REPO)); + + cl_fixture_cleanup(WORKTREE_REPO); +} + +void test_worktree_open__open_from_repository(void) +{ + git_worktree *opened, *lookedup; + + cl_git_pass(git_worktree_open_from_repository(&opened, fixture.worktree)); + cl_git_pass(git_worktree_lookup(&lookedup, fixture.repo, WORKTREE_REPO)); + + cl_assert_equal_s(opened->name, lookedup->name); + cl_assert_equal_s(opened->gitdir_path, lookedup->gitdir_path); + cl_assert_equal_s(opened->gitlink_path, lookedup->gitlink_path); + cl_assert_equal_s(opened->parent_path, lookedup->parent_path); + cl_assert_equal_s(opened->commondir_path, lookedup->commondir_path); + cl_assert_equal_i(opened->locked, lookedup->locked); + + git_worktree_free(opened); + git_worktree_free(lookedup); +} + +void test_worktree_open__open_from_nonworktree_fails(void) +{ + git_worktree *wt; + + cl_git_fail(git_worktree_open_from_repository(&wt, fixture.repo)); +} diff --git a/tests/worktree/reflog.c b/tests/worktree/reflog.c new file mode 100644 index 000000000..6152eb385 --- /dev/null +++ b/tests/worktree/reflog.c @@ -0,0 +1,65 @@ +#include "clar_libgit2.h" +#include "worktree_helpers.h" + +#include "reflog.h" + +#define COMMON_REPO "testrepo" +#define WORKTREE_REPO "testrepo-worktree" + +#define REFLOG "refs/heads/testrepo-worktree" +#define REFLOG_MESSAGE "reflog message" + +static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); + +void test_worktree_reflog__initialize(void) +{ + setup_fixture_worktree(&fixture); +} + +void test_worktree_reflog__cleanup(void) +{ + cleanup_fixture_worktree(&fixture); +} + +void test_worktree_reflog__read(void) +{ + git_reflog *reflog; + const git_reflog_entry *entry; + + cl_git_pass(git_reflog_read(&reflog, fixture.worktree, REFLOG)); + cl_assert_equal_i(git_reflog_entrycount(reflog), 1); + + entry = git_reflog_entry_byindex(reflog, 0); + cl_assert(entry != NULL); + cl_assert_equal_s(git_reflog_entry_message(entry), "branch: Created from HEAD"); + + git_reflog_free(reflog); +} + +void test_worktree_reflog__append_then_read(void) +{ + git_reflog *reflog, *parent_reflog; + const git_reflog_entry *entry; + git_reference *head; + git_signature *sig; + const git_oid *oid; + + cl_git_pass(git_repository_head(&head, fixture.worktree)); + cl_assert((oid = git_reference_target(head)) != NULL); + cl_git_pass(git_signature_now(&sig, "foo", "foo@bar")); + + cl_git_pass(git_reflog_read(&reflog, fixture.worktree, REFLOG)); + cl_git_pass(git_reflog_append(reflog, oid, sig, REFLOG_MESSAGE)); + git_reflog_write(reflog); + + cl_git_pass(git_reflog_read(&parent_reflog, fixture.repo, REFLOG)); + entry = git_reflog_entry_byindex(parent_reflog, 0); + cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0); + cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0); + + git_reference_free(head); + git_signature_free(sig); + git_reflog_free(reflog); + git_reflog_free(parent_reflog); +} diff --git a/tests/worktree/refs.c b/tests/worktree/refs.c new file mode 100644 index 000000000..a10f50a2c --- /dev/null +++ b/tests/worktree/refs.c @@ -0,0 +1,173 @@ +#include "clar_libgit2.h" +#include "path.h" +#include "refs.h" +#include "worktree.h" +#include "worktree_helpers.h" + +#define COMMON_REPO "testrepo" +#define WORKTREE_REPO "testrepo-worktree" + +static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); + +void test_worktree_refs__initialize(void) +{ + setup_fixture_worktree(&fixture); +} + +void test_worktree_refs__cleanup(void) +{ + cleanup_fixture_worktree(&fixture); +} + +void test_worktree_refs__list(void) +{ + git_strarray refs, wtrefs; + unsigned i, j; + int error = 0; + + cl_git_pass(git_reference_list(&refs, fixture.repo)); + cl_git_pass(git_reference_list(&wtrefs, fixture.worktree)); + + if (refs.count != wtrefs.count) + { + error = GIT_ERROR; + goto exit; + } + + for (i = 0; i < refs.count; i++) + { + int found = 0; + + for (j = 0; j < wtrefs.count; j++) + { + if (!strcmp(refs.strings[i], wtrefs.strings[j])) + { + found = 1; + break; + } + } + + if (!found) + { + error = GIT_ERROR; + goto exit; + } + } + +exit: + git_strarray_free(&refs); + git_strarray_free(&wtrefs); + cl_git_pass(error); +} + +void test_worktree_refs__read_head(void) +{ + git_reference *head; + + cl_git_pass(git_repository_head(&head, fixture.worktree)); + + git_reference_free(head); +} + +void test_worktree_refs__set_head_fails_when_worktree_wants_linked_repos_HEAD(void) +{ + git_reference *head; + + cl_git_pass(git_repository_head(&head, fixture.repo)); + cl_git_fail(git_repository_set_head(fixture.worktree, git_reference_name(head))); + + git_reference_free(head); +} + +void test_worktree_refs__set_head_fails_when_main_repo_wants_worktree_head(void) +{ + git_reference *head; + + cl_git_pass(git_repository_head(&head, fixture.worktree)); + cl_git_fail(git_repository_set_head(fixture.repo, git_reference_name(head))); + + git_reference_free(head); +} + +void test_worktree_refs__set_head_works_for_current_HEAD(void) +{ + git_reference *head; + + cl_git_pass(git_repository_head(&head, fixture.repo)); + cl_git_pass(git_repository_set_head(fixture.repo, git_reference_name(head))); + + git_reference_free(head); +} + +void test_worktree_refs__set_head_fails_when_already_checked_out(void) +{ + cl_git_fail(git_repository_set_head(fixture.repo, "refs/heads/testrepo-worktree")); +} + +void test_worktree_refs__delete_fails_for_checked_out_branch(void) +{ + git_reference *branch; + + cl_git_pass(git_branch_lookup(&branch, fixture.repo, + "testrepo-worktree", GIT_BRANCH_LOCAL)); + cl_git_fail(git_branch_delete(branch)); + + git_reference_free(branch); +} + +void test_worktree_refs__delete_succeeds_after_pruning_worktree(void) +{ + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + git_reference *branch; + git_worktree *worktree; + + opts.flags = GIT_WORKTREE_PRUNE_VALID; + + cl_git_pass(git_worktree_lookup(&worktree, fixture.repo, fixture.worktreename)); + cl_git_pass(git_worktree_prune(worktree, &opts)); + git_worktree_free(worktree); + + cl_git_pass(git_branch_lookup(&branch, fixture.repo, + "testrepo-worktree", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); +} + +void test_worktree_refs__renaming_reference_updates_worktree_heads(void) +{ + git_reference *head, *branch, *renamed; + + cl_git_pass(git_branch_lookup(&branch, fixture.repo, + "testrepo-worktree", GIT_BRANCH_LOCAL)); + cl_git_pass(git_reference_rename(&renamed, branch, "refs/heads/renamed", 0, NULL)); + cl_git_pass(git_repository_head(&head, fixture.worktree)); + + git_reference_free(head); + git_reference_free(branch); + git_reference_free(renamed); +} + +void test_worktree_refs__creating_refs_uses_commondir(void) +{ + git_reference *head, *branch, *lookup; + git_commit *commit; + git_buf refpath = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&refpath, + git_repository_commondir(fixture.worktree), "refs/heads/testbranch")); + cl_assert(!git_path_exists(refpath.ptr)); + + cl_git_pass(git_repository_head(&head, fixture.worktree)); + cl_git_pass(git_commit_lookup(&commit, fixture.worktree, git_reference_target(head))); + cl_git_pass(git_branch_create(&branch, fixture.worktree, "testbranch", commit, 0)); + cl_git_pass(git_branch_lookup(&lookup, fixture.worktree, "testbranch", GIT_BRANCH_LOCAL)); + cl_assert(git_reference_cmp(branch, lookup) == 0); + cl_assert(git_path_exists(refpath.ptr)); + + git_reference_free(lookup); + git_reference_free(branch); + git_reference_free(head); + git_commit_free(commit); + git_buf_free(&refpath); +} diff --git a/tests/worktree/repository.c b/tests/worktree/repository.c new file mode 100644 index 000000000..5c7595c64 --- /dev/null +++ b/tests/worktree/repository.c @@ -0,0 +1,63 @@ +#include "clar_libgit2.h" +#include "worktree_helpers.h" +#include "submodule/submodule_helpers.h" + +#include "repository.h" + +#define COMMON_REPO "testrepo" +#define WORKTREE_REPO "testrepo-worktree" + +static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); + +void test_worktree_repository__initialize(void) +{ + setup_fixture_worktree(&fixture); +} + +void test_worktree_repository__cleanup(void) +{ + cleanup_fixture_worktree(&fixture); +} + +void test_worktree_repository__head(void) +{ + git_reference *ref, *head; + + cl_git_pass(git_reference_lookup(&ref, fixture.repo, "refs/heads/testrepo-worktree")); + cl_git_pass(git_repository_head_for_worktree(&head, fixture.repo, "testrepo-worktree")); + cl_assert(git_reference_cmp(ref, head) == 0); + + git_reference_free(ref); + git_reference_free(head); +} + +void test_worktree_repository__head_fails_for_invalid_worktree(void) +{ + git_reference *head = NULL; + + cl_git_fail(git_repository_head_for_worktree(&head, fixture.repo, "invalid")); + cl_assert(head == NULL); +} + +void test_worktree_repository__head_detached(void) +{ + git_reference *ref, *head; + + cl_git_pass(git_reference_lookup(&ref, fixture.repo, "refs/heads/testrepo-worktree")); + cl_git_pass(git_repository_set_head_detached(fixture.worktree, &ref->target.oid)); + + cl_assert(git_repository_head_detached(fixture.worktree)); + cl_assert(git_repository_head_detached_for_worktree(fixture.repo, "testrepo-worktree")); + cl_git_fail(git_repository_head_for_worktree(&head, fixture.repo, "testrepo-worktree")); + + git_reference_free(ref); +} + +void test_worktree_repository__head_detached_fails_for_invalid_worktree(void) +{ + git_reference *head = NULL; + + cl_git_fail(git_repository_head_detached_for_worktree(fixture.repo, "invalid")); + cl_assert(head == NULL); +} diff --git a/tests/worktree/submodule.c b/tests/worktree/submodule.c new file mode 100644 index 000000000..294385226 --- /dev/null +++ b/tests/worktree/submodule.c @@ -0,0 +1,92 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "worktree.h" +#include "worktree_helpers.h" + +#define WORKTREE_PARENT "submodules-worktree-parent" +#define WORKTREE_CHILD "submodules-worktree-child" + +static worktree_fixture parent + = WORKTREE_FIXTURE_INIT("submodules", WORKTREE_PARENT); +static worktree_fixture child + = WORKTREE_FIXTURE_INIT(NULL, WORKTREE_CHILD); + +void test_worktree_submodule__initialize(void) +{ + setup_fixture_worktree(&parent); + + cl_git_pass(p_rename( + "submodules/testrepo/.gitted", + "submodules/testrepo/.git")); + + setup_fixture_worktree(&child); +} + +void test_worktree_submodule__cleanup(void) +{ + cleanup_fixture_worktree(&child); + cleanup_fixture_worktree(&parent); +} + +void test_worktree_submodule__submodule_worktree_parent(void) +{ + cl_assert(git_repository_path(parent.worktree) != NULL); + cl_assert(git_repository_workdir(parent.worktree) != NULL); + + cl_assert(!parent.repo->is_worktree); + cl_assert(parent.worktree->is_worktree); +} + +void test_worktree_submodule__submodule_worktree_child(void) +{ + cl_assert(!parent.repo->is_worktree); + cl_assert(parent.worktree->is_worktree); + cl_assert(child.worktree->is_worktree); +} + +void test_worktree_submodule__open_discovered_submodule_worktree(void) +{ + git_buf path = GIT_BUF_INIT; + git_repository *repo; + + cl_git_pass(git_repository_discover(&path, + git_repository_workdir(child.worktree), false, NULL)); + cl_git_pass(git_repository_open(&repo, path.ptr)); + cl_assert_equal_s(git_repository_workdir(child.worktree), + git_repository_workdir(repo)); + + git_buf_free(&path); + git_repository_free(repo); +} + +void test_worktree_submodule__resolve_relative_url(void) +{ + git_buf wt_path = GIT_BUF_INIT; + git_buf sm_relative_path = GIT_BUF_INIT, wt_relative_path = GIT_BUF_INIT; + git_repository *repo; + git_worktree *wt; + + cl_git_pass(git_futils_mkdir("subdir", 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_path_prettify_dir(&wt_path, "subdir", NULL)); + cl_git_pass(git_buf_joinpath(&wt_path, wt_path.ptr, "wt")); + + /* Open child repository, which is a submodule */ + cl_git_pass(git_repository_open(&child.repo, WORKTREE_CHILD)); + + /* Create worktree of submodule repository */ + cl_git_pass(git_worktree_add(&wt, child.repo, "subdir", wt_path.ptr, NULL)); + cl_git_pass(git_repository_open_from_worktree(&repo, wt)); + + cl_git_pass(git_submodule_resolve_url(&sm_relative_path, repo, + "../" WORKTREE_CHILD)); + cl_git_pass(git_submodule_resolve_url(&wt_relative_path, child.repo, + "../" WORKTREE_CHILD)); + + cl_assert_equal_s(sm_relative_path.ptr, wt_relative_path.ptr); + + git_worktree_free(wt); + git_repository_free(repo); + git_buf_free(&wt_path); + git_buf_free(&sm_relative_path); + git_buf_free(&wt_relative_path); +} diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c new file mode 100644 index 000000000..4ac3b8bba --- /dev/null +++ b/tests/worktree/worktree.c @@ -0,0 +1,583 @@ +#include "clar_libgit2.h" +#include "worktree_helpers.h" +#include "submodule/submodule_helpers.h" + +#include "checkout.h" +#include "repository.h" +#include "worktree.h" + +#define COMMON_REPO "testrepo" +#define WORKTREE_REPO "testrepo-worktree" + +static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); + +void test_worktree_worktree__initialize(void) +{ + setup_fixture_worktree(&fixture); +} + +void test_worktree_worktree__cleanup(void) +{ + cleanup_fixture_worktree(&fixture); +} + +void test_worktree_worktree__list(void) +{ + git_strarray wts; + + cl_git_pass(git_worktree_list(&wts, fixture.repo)); + cl_assert_equal_i(wts.count, 1); + cl_assert_equal_s(wts.strings[0], "testrepo-worktree"); + + git_strarray_free(&wts); +} + +void test_worktree_worktree__list_with_invalid_worktree_dirs(void) +{ + const char *filesets[3][2] = { + { "gitdir", "commondir" }, + { "gitdir", "HEAD" }, + { "HEAD", "commondir" }, + }; + git_buf path = GIT_BUF_INIT; + git_strarray wts; + unsigned i, j, len; + + cl_git_pass(git_buf_printf(&path, "%s/worktrees/invalid", + fixture.repo->commondir)); + cl_git_pass(p_mkdir(path.ptr, 0755)); + + len = path.size; + + for (i = 0; i < ARRAY_SIZE(filesets); i++) { + + for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) { + git_buf_truncate(&path, len); + cl_git_pass(git_buf_joinpath(&path, path.ptr, filesets[i][j])); + cl_git_pass(p_close(p_creat(path.ptr, 0644))); + } + + cl_git_pass(git_worktree_list(&wts, fixture.worktree)); + cl_assert_equal_i(wts.count, 1); + cl_assert_equal_s(wts.strings[0], "testrepo-worktree"); + git_strarray_free(&wts); + + for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) { + git_buf_truncate(&path, len); + cl_git_pass(git_buf_joinpath(&path, path.ptr, filesets[i][j])); + p_unlink(path.ptr); + } + } + + git_buf_free(&path); +} + +void test_worktree_worktree__list_in_worktree_repo(void) +{ + git_strarray wts; + + cl_git_pass(git_worktree_list(&wts, fixture.worktree)); + cl_assert_equal_i(wts.count, 1); + cl_assert_equal_s(wts.strings[0], "testrepo-worktree"); + + git_strarray_free(&wts); +} + +void test_worktree_worktree__list_bare(void) +{ + git_repository *repo; + git_strarray wts; + + repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_worktree_list(&wts, repo)); + cl_assert_equal_i(wts.count, 0); + + git_repository_free(repo); +} + +void test_worktree_worktree__list_without_worktrees(void) +{ + git_repository *repo; + git_strarray wts; + + repo = cl_git_sandbox_init("testrepo2"); + cl_git_pass(git_worktree_list(&wts, repo)); + cl_assert_equal_i(wts.count, 0); + + git_repository_free(repo); +} + +void test_worktree_worktree__lookup(void) +{ + git_worktree *wt; + git_buf gitdir_path = GIT_BUF_INIT; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + + cl_git_pass(git_buf_joinpath(&gitdir_path, fixture.repo->commondir, "worktrees/testrepo-worktree/")); + + cl_assert_equal_s(wt->gitdir_path, gitdir_path.ptr); + cl_assert_equal_s(wt->parent_path, fixture.repo->workdir); + cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink); + cl_assert_equal_s(wt->commondir_path, fixture.repo->gitdir); + cl_assert_equal_s(wt->commondir_path, fixture.repo->commondir); + + git_buf_free(&gitdir_path); + git_worktree_free(wt); +} + +void test_worktree_worktree__lookup_nonexistent_worktree(void) +{ + git_worktree *wt; + + cl_git_fail(git_worktree_lookup(&wt, fixture.repo, "nonexistent")); + cl_assert_equal_p(wt, NULL); +} + +void test_worktree_worktree__open(void) +{ + git_worktree *wt; + git_repository *repo; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + + cl_git_pass(git_repository_open_from_worktree(&repo, wt)); + cl_assert_equal_s(git_repository_workdir(repo), + git_repository_workdir(fixture.worktree)); + + git_repository_free(repo); + git_worktree_free(wt); +} + +void test_worktree_worktree__open_invalid_commondir(void) +{ + git_worktree *wt; + git_repository *repo; + git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + + cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/commondir")); + cl_git_pass(git_buf_printf(&path, + "%s/worktrees/testrepo-worktree/commondir", + fixture.repo->commondir)); + cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644)); + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_fail(git_repository_open_from_worktree(&repo, wt)); + + git_buf_free(&buf); + git_buf_free(&path); + git_worktree_free(wt); +} + +void test_worktree_worktree__open_invalid_gitdir(void) +{ + git_worktree *wt; + git_repository *repo; + git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + + cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir")); + cl_git_pass(git_buf_printf(&path, + "%s/worktrees/testrepo-worktree/gitdir", + fixture.repo->commondir)); + cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644)); + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_fail(git_repository_open_from_worktree(&repo, wt)); + + git_buf_free(&buf); + git_buf_free(&path); + git_worktree_free(wt); +} + +void test_worktree_worktree__open_invalid_parent(void) +{ + git_worktree *wt; + git_repository *repo; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir")); + cl_git_pass(git_futils_writebuffer(&buf, + fixture.worktree->gitlink, O_RDWR, 0644)); + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_fail(git_repository_open_from_worktree(&repo, wt)); + + git_buf_free(&buf); + git_worktree_free(wt); +} + +void test_worktree_worktree__init(void) +{ + git_worktree *wt; + git_repository *repo; + git_reference *branch; + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new")); + cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL)); + + /* Open and verify created repo */ + cl_git_pass(git_repository_open(&repo, path.ptr)); + cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-new/") == 0); + cl_git_pass(git_branch_lookup(&branch, repo, "worktree-new", GIT_BRANCH_LOCAL)); + + git_buf_free(&path); + git_worktree_free(wt); + git_reference_free(branch); + git_repository_free(repo); +} + +void test_worktree_worktree__add_locked(void) +{ + git_worktree *wt; + git_repository *repo; + git_reference *branch; + git_buf path = GIT_BUF_INIT; + git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT; + + opts.lock = 1; + + cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-locked")); + cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-locked", path.ptr, &opts)); + + /* Open and verify created repo */ + cl_assert(git_worktree_is_locked(NULL, wt)); + cl_git_pass(git_repository_open(&repo, path.ptr)); + cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-locked/") == 0); + cl_git_pass(git_branch_lookup(&branch, repo, "worktree-locked", GIT_BRANCH_LOCAL)); + + git_buf_free(&path); + git_worktree_free(wt); + git_reference_free(branch); + git_repository_free(repo); +} + +void test_worktree_worktree__init_existing_branch(void) +{ + git_reference *head, *branch; + git_commit *commit; + git_worktree *wt; + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_repository_head(&head, fixture.repo)); + cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid)); + cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false)); + + cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new")); + cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL)); + + git_buf_free(&path); + git_commit_free(commit); + git_reference_free(head); + git_reference_free(branch); +} + +void test_worktree_worktree__init_existing_worktree(void) +{ + git_worktree *wt; + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new")); + cl_git_fail(git_worktree_add(&wt, fixture.repo, "testrepo-worktree", path.ptr, NULL)); + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink); + + git_buf_free(&path); + git_worktree_free(wt); +} + +void test_worktree_worktree__init_existing_path(void) +{ + const char *wtfiles[] = { "HEAD", "commondir", "gitdir", "index" }; + git_worktree *wt; + git_buf path = GIT_BUF_INIT; + unsigned i; + + /* Delete files to verify they have not been created by + * the init call */ + for (i = 0; i < ARRAY_SIZE(wtfiles); i++) { + cl_git_pass(git_buf_joinpath(&path, + fixture.worktree->gitdir, wtfiles[i])); + cl_git_pass(p_unlink(path.ptr)); + } + + cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../testrepo-worktree")); + cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL)); + + /* Verify files have not been re-created */ + for (i = 0; i < ARRAY_SIZE(wtfiles); i++) { + cl_git_pass(git_buf_joinpath(&path, + fixture.worktree->gitdir, wtfiles[i])); + cl_assert(!git_path_exists(path.ptr)); + } + + git_buf_free(&path); +} + +void test_worktree_worktree__init_submodule(void) +{ + git_repository *repo, *sm, *wt; + git_worktree *worktree; + git_buf path = GIT_BUF_INIT; + + cleanup_fixture_worktree(&fixture); + repo = setup_fixture_submod2(); + + cl_git_pass(git_buf_joinpath(&path, repo->workdir, "sm_unchanged")); + cl_git_pass(git_repository_open(&sm, path.ptr)); + cl_git_pass(git_buf_joinpath(&path, repo->workdir, "../worktree/")); + cl_git_pass(git_worktree_add(&worktree, sm, "repo-worktree", path.ptr, NULL)); + cl_git_pass(git_repository_open_from_worktree(&wt, worktree)); + + cl_git_pass(git_path_prettify_dir(&path, path.ptr, NULL)); + cl_assert_equal_s(path.ptr, wt->workdir); + cl_git_pass(git_path_prettify_dir(&path, sm->commondir, NULL)); + cl_assert_equal_s(sm->commondir, wt->commondir); + + cl_git_pass(git_buf_joinpath(&path, sm->gitdir, "worktrees/repo-worktree/")); + cl_assert_equal_s(path.ptr, wt->gitdir); + + git_buf_free(&path); + git_worktree_free(worktree); + git_repository_free(sm); + git_repository_free(wt); +} + +void test_worktree_worktree__validate(void) +{ + git_worktree *wt; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_pass(git_worktree_validate(wt)); + + git_worktree_free(wt); +} + +void test_worktree_worktree__validate_invalid_commondir(void) +{ + git_worktree *wt; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + git__free(wt->commondir_path); + wt->commondir_path = "/path/to/invalid/commondir"; + + cl_git_fail(git_worktree_validate(wt)); + + wt->commondir_path = NULL; + git_worktree_free(wt); +} + +void test_worktree_worktree__validate_invalid_gitdir(void) +{ + git_worktree *wt; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + git__free(wt->gitdir_path); + wt->gitdir_path = "/path/to/invalid/gitdir"; + cl_git_fail(git_worktree_validate(wt)); + + wt->gitdir_path = NULL; + git_worktree_free(wt); +} + +void test_worktree_worktree__validate_invalid_parent(void) +{ + git_worktree *wt; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + git__free(wt->parent_path); + wt->parent_path = "/path/to/invalid/parent"; + cl_git_fail(git_worktree_validate(wt)); + + wt->parent_path = NULL; + git_worktree_free(wt); +} + +void test_worktree_worktree__lock_with_reason(void) +{ + git_worktree *wt; + git_buf reason = GIT_BUF_INIT; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + + cl_assert(!git_worktree_is_locked(NULL, wt)); + cl_git_pass(git_worktree_lock(wt, "because")); + cl_assert(git_worktree_is_locked(&reason, wt) > 0); + cl_assert_equal_s(reason.ptr, "because"); + cl_assert(wt->locked); + + git_buf_free(&reason); + git_worktree_free(wt); +} + +void test_worktree_worktree__lock_without_reason(void) +{ + git_worktree *wt; + git_buf reason = GIT_BUF_INIT; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + + cl_assert(!git_worktree_is_locked(NULL, wt)); + cl_git_pass(git_worktree_lock(wt, NULL)); + cl_assert(git_worktree_is_locked(&reason, wt) > 0); + cl_assert_equal_i(reason.size, 0); + cl_assert(wt->locked); + + git_buf_free(&reason); + git_worktree_free(wt); +} + +void test_worktree_worktree__unlock_unlocked_worktree(void) +{ + git_worktree *wt; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_assert(!git_worktree_is_locked(NULL, wt)); + cl_assert(git_worktree_unlock(wt) == 0); + cl_assert(!wt->locked); + + git_worktree_free(wt); +} + +void test_worktree_worktree__unlock_locked_worktree(void) +{ + git_worktree *wt; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_pass(git_worktree_lock(wt, NULL)); + cl_assert(git_worktree_is_locked(NULL, wt)); + cl_git_pass(git_worktree_unlock(wt)); + cl_assert(!wt->locked); + + git_worktree_free(wt); +} + +void test_worktree_worktree__prune_without_opts_fails(void) +{ + git_worktree *wt; + git_repository *repo; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_fail(git_worktree_prune(wt, NULL)); + + /* Assert the repository is still valid */ + cl_git_pass(git_repository_open_from_worktree(&repo, wt)); + + git_worktree_free(wt); + git_repository_free(repo); +} + +void test_worktree_worktree__prune_valid(void) +{ + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + git_worktree *wt; + git_repository *repo; + + opts.flags = GIT_WORKTREE_PRUNE_VALID; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_pass(git_worktree_prune(wt, &opts)); + + /* Assert the repository is not valid anymore */ + cl_git_fail(git_repository_open_from_worktree(&repo, wt)); + + git_worktree_free(wt); + git_repository_free(repo); +} + +void test_worktree_worktree__prune_locked(void) +{ + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + git_worktree *wt; + git_repository *repo; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_pass(git_worktree_lock(wt, NULL)); + + opts.flags = GIT_WORKTREE_PRUNE_VALID; + cl_git_fail(git_worktree_prune(wt, &opts)); + /* Assert the repository is still valid */ + cl_git_pass(git_repository_open_from_worktree(&repo, wt)); + + opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_LOCKED; + cl_git_pass(git_worktree_prune(wt, &opts)); + + git_worktree_free(wt); + git_repository_free(repo); +} + +void test_worktree_worktree__prune_gitdir_only(void) +{ + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + git_worktree *wt; + + opts.flags = GIT_WORKTREE_PRUNE_VALID; + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_pass(git_worktree_prune(wt, &opts)); + + cl_assert(!git_path_exists(wt->gitdir_path)); + cl_assert(git_path_exists(wt->gitlink_path)); + + git_worktree_free(wt); +} + +void test_worktree_worktree__prune_worktree(void) +{ + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + git_worktree *wt; + + opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_pass(git_worktree_prune(wt, &opts)); + + cl_assert(!git_path_exists(wt->gitdir_path)); + cl_assert(!git_path_exists(wt->gitlink_path)); + + git_worktree_free(wt); +} + +static int read_head_ref(git_repository *repo, const char *path, void *payload) +{ + git_vector *refs = (git_vector *) payload; + git_reference *head; + + GIT_UNUSED(repo); + + cl_git_pass(git_reference__read_head(&head, repo, path)); + + git_vector_insert(refs, head); + + return 0; +} + +void test_worktree_worktree__foreach_head_gives_same_results_in_wt_and_repo(void) +{ + git_vector repo_refs = GIT_VECTOR_INIT, worktree_refs = GIT_VECTOR_INIT; + git_reference *heads[2]; + size_t i; + + cl_git_pass(git_reference_lookup(&heads[0], fixture.repo, GIT_HEAD_FILE)); + cl_git_pass(git_reference_lookup(&heads[1], fixture.worktree, GIT_HEAD_FILE)); + + cl_git_pass(git_repository_foreach_head(fixture.repo, read_head_ref, &repo_refs)); + cl_git_pass(git_repository_foreach_head(fixture.worktree, read_head_ref, &worktree_refs)); + + cl_assert_equal_i(repo_refs.length, ARRAY_SIZE(heads)); + cl_assert_equal_i(worktree_refs.length, ARRAY_SIZE(heads)); + + for (i = 0; i < ARRAY_SIZE(heads); i++) { + cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name); + cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name); + cl_assert_equal_s(heads[i]->name, ((git_reference *) worktree_refs.contents[i])->name); + + git_reference_free(heads[i]); + git_reference_free(repo_refs.contents[i]); + git_reference_free(worktree_refs.contents[i]); + } + + git_vector_free(&repo_refs); + git_vector_free(&worktree_refs); +} diff --git a/tests/worktree/worktree_helpers.c b/tests/worktree/worktree_helpers.c new file mode 100644 index 000000000..6d4cdbaeb --- /dev/null +++ b/tests/worktree/worktree_helpers.c @@ -0,0 +1,30 @@ +#include "clar_libgit2.h" +#include "worktree_helpers.h" + +void cleanup_fixture_worktree(worktree_fixture *fixture) +{ + if (!fixture) + return; + + if (fixture->repo) { + git_repository_free(fixture->repo); + fixture->repo = NULL; + } + if (fixture->worktree) { + git_repository_free(fixture->worktree); + fixture->worktree = NULL; + } + + if (fixture->reponame) + cl_fixture_cleanup(fixture->reponame); + if (fixture->worktreename) + cl_fixture_cleanup(fixture->worktreename); +} + +void setup_fixture_worktree(worktree_fixture *fixture) +{ + if (fixture->reponame) + fixture->repo = cl_git_sandbox_init(fixture->reponame); + if (fixture->worktreename) + fixture->worktree = cl_git_sandbox_init(fixture->worktreename); +} diff --git a/tests/worktree/worktree_helpers.h b/tests/worktree/worktree_helpers.h new file mode 100644 index 000000000..35ea9ed4c --- /dev/null +++ b/tests/worktree/worktree_helpers.h @@ -0,0 +1,11 @@ +typedef struct { + const char *reponame; + const char *worktreename; + git_repository *repo; + git_repository *worktree; +} worktree_fixture; + +#define WORKTREE_FIXTURE_INIT(repo, worktree) { (repo), (worktree), NULL, NULL } + +void cleanup_fixture_worktree(worktree_fixture *fixture); +void setup_fixture_worktree(worktree_fixture *fixture);