diff options
44 files changed, 919 insertions, 315 deletions
diff --git a/.editorconfig b/.editorconfig index 34c5e9234..2230fd860 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,7 @@ indent_size = 2 indent_style = space indent_size = 4 trim_trailing_whitespace = false + +[*.py] +indent_style = space +indent_size = 4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8765a97b5..113e554d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,6 @@ OPTION(USE_SHA1 "Enable SHA1. Can be set to CollisionDetection(ON)/HTTPS/Gene OPTION(USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF) OPTION(USE_STANDALONE_FUZZERS "Enable standalone fuzzers (compatible with gcc)" OFF) OPTION(USE_LEAK_CHECKER "Run tests with leak checker" OFF) -OPTION(VALGRIND "Configure build for valgrind" OFF) OPTION(DEBUG_POOL "Enable debug pool allocator" OFF) OPTION(ENABLE_WERROR "Enable compilation with -Werror" OFF) OPTION(USE_BUNDLED_ZLIB "Use the bundled version of zlib" OFF) @@ -111,8 +110,8 @@ STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_V STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_REV "${GIT2_HEADER}") SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}") -FILE(STRINGS "${libgit2_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$") -STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}") +FILE(STRINGS "${libgit2_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION \"([0-9.]+)\"$") +STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION \"([0-9.]+)\"$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}") IF (DEPRECATE_HARD) ADD_DEFINITIONS(-DGIT_DEPRECATE_HARD) @@ -4,9 +4,8 @@ libgit2 - the Git linkable library | Build Status | | | ------------ | - | | **master** branch CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=master)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=master) | +| **v0.99 branch** CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=maint/v0.99)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=maint/v0.99) | | **v0.28 branch** CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=maint/v0.28)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=maint/v0.28) | -| **v0.27 branch** CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=maint/v0.27)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=maint/v0.27) | -| **v0.26 branch** CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=maint/v0.26)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=maint/v0.26) | | **Nightly** builds | [![Azure Pipelines Build Status](https://libgit2.visualstudio.com/libgit2/_apis/build/status/nightly?branchName=master&label=Full+Build)](https://libgit2.visualstudio.com/libgit2/_build/latest?definitionId=9&branchName=master) [![Coverity Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/coverity?branchName=master&label=Coverity+Build)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=21?branchName=master) [![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639) | `libgit2` is a portable, pure C implementation of the Git core methods @@ -330,6 +329,8 @@ Here are the bindings to libgit2 that are currently available: * hgit2 <https://github.com/jwiegley/gitlib> * Java * Jagged <https://github.com/ethomson/jagged> +* Javascript / WebAssembly ( browser and nodejs ) + * WASM-git <https://github.com/petersalomonsen/wasm-git> * Julia * LibGit2.jl <https://github.com/JuliaLang/julia/tree/master/stdlib/LibGit2> * Lua diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2b593dd2c..2575f475f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,7 +19,7 @@ jobs: environmentVariables: | CC=gcc CMAKE_GENERATOR=Ninja - CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on -DUSE_GSSAPI=ON + CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }} - job: linux_amd64_xenial_gcc_mbedtls @@ -35,7 +35,7 @@ jobs: environmentVariables: | CC=gcc CMAKE_GENERATOR=Ninja - CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on -DUSE_GSSAPI=ON + CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }} - job: linux_amd64_xenial_clang_openssl @@ -51,7 +51,7 @@ jobs: environmentVariables: | CC=clang CMAKE_GENERATOR=Ninja - CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on -DUSE_GSSAPI=ON + CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }} - job: linux_amd64_xenial_clang_mbedtls @@ -67,7 +67,7 @@ jobs: environmentVariables: | CC=clang CMAKE_GENERATOR=Ninja - CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on -DUSE_GSSAPI=ON + CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }} - job: macos diff --git a/azure-pipelines/docker.yml b/azure-pipelines/docker.yml index 0e1988c8a..0f0885770 100644 --- a/azure-pipelines/docker.yml +++ b/azure-pipelines/docker.yml @@ -13,9 +13,9 @@ steps: if [ -f /tmp/dockercache/${{parameters.docker.image}}.tar ]; then docker load < /tmp/dockercache/${{parameters.docker.image}}.tar; fi displayName: 'Load Docker cache' - script: | - cd $(Build.SourcesDirectory)/azure-pipelines/docker - docker build -t libgit2/${{parameters.docker.image}} --build-arg BASE=${{parameters.docker.base}} -f ${{parameters.docker.image}} . - if [ ! -d /tmp/dockercache ]; then mkdir /tmp/dockercache; fi + cd $(Build.SourcesDirectory)/azure-pipelines/docker && + docker build -t libgit2/${{parameters.docker.image}} --build-arg BASE=${{parameters.docker.base}} -f ${{parameters.docker.image}} . && + if [ ! -d /tmp/dockercache ]; then mkdir /tmp/dockercache; fi && docker save libgit2/${{parameters.docker.image}} $(docker history -q libgit2/${{parameters.docker.image}} | grep -v '<missing>') > /tmp/dockercache/${{parameters.docker.image}}.tar displayName: 'Build Docker image' - task: docker@0 diff --git a/azure-pipelines/docker/bionic b/azure-pipelines/docker/bionic index 648bda704..33175a0b7 100644 --- a/azure-pipelines/docker/bionic +++ b/azure-pipelines/docker/bionic @@ -1,13 +1,12 @@ ARG BASE -FROM $BASE +FROM $BASE AS apt RUN apt-get update && \ - apt-get install -y --no-install-recommends \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ clang \ cmake \ curl \ gcc \ git \ - gosu \ libcurl4-openssl-dev \ libpcre3-dev \ libssh2-1-dev \ @@ -19,13 +18,16 @@ RUN apt-get update && \ openssl \ pkgconf \ python \ + sudo \ valgrind \ && \ rm -rf /var/lib/apt/lists/* -RUN mkdir /var/run/sshd +FROM apt AS mbedtls RUN cd /tmp && \ - curl --location --silent https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ + curl --location http://secure.globalsign.com/cacert/gsrsaovsslca2018.crt | openssl x509 -inform der -out /tmp/cacert.pem && \ + curl --location https://curl.haxx.se/ca/cacert.pem >> /tmp/cacert.pem && \ + curl --location --silent https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz --cacert /tmp/cacert.pem | \ tar -xz && \ cd mbedtls-2.16.2 && \ scripts/config.pl set MBEDTLS_MD4_C 1 && \ @@ -34,7 +36,9 @@ RUN cd /tmp && \ cd .. && \ rm -rf mbedtls-2.16.2 +FROM mbedtls AS configure COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod a+x /usr/local/bin/entrypoint.sh +RUN mkdir /var/run/sshd ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/azure-pipelines/docker/entrypoint.sh b/azure-pipelines/docker/entrypoint.sh index 38eedf02b..8d96e3acd 100644 --- a/azure-pipelines/docker/entrypoint.sh +++ b/azure-pipelines/docker/entrypoint.sh @@ -1,4 +1,4 @@ #!/bin/bash -e useradd --shell /bin/bash libgit2 -chown -R $(id -u libgit2) /home/libgit2 -exec gosu libgit2 "$@" +chown --recursive libgit2:libgit2 /home/libgit2 +exec sudo --preserve-env --set-home --user=libgit2 "$@" diff --git a/azure-pipelines/docker/xenial b/azure-pipelines/docker/xenial index cb5d4919a..475956f3d 100644 --- a/azure-pipelines/docker/xenial +++ b/azure-pipelines/docker/xenial @@ -8,7 +8,6 @@ RUN apt-get update && \ curl \ gcc \ git \ - gosu \ krb5-user \ libcurl4-gnutls-dev \ libgcrypt20-dev \ @@ -23,11 +22,16 @@ RUN apt-get update && \ openssl \ pkgconf \ python \ - valgrind + sudo \ + valgrind \ + && \ + rm -rf /var/lib/apt/lists/* FROM apt AS mbedtls RUN cd /tmp && \ - curl --location --silent https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ + curl --location http://secure.globalsign.com/cacert/gsrsaovsslca2018.crt | openssl x509 -inform der -out /tmp/cacert.pem && \ + curl --location https://curl.haxx.se/ca/cacert.pem >> /tmp/cacert.pem && \ + curl --location --silent https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz --cacert /tmp/cacert.pem | \ tar -xz && \ cd mbedtls-2.16.2 && \ scripts/config.pl set MBEDTLS_MD4_C 1 && \ diff --git a/azure-pipelines/setup-linux.sh b/azure-pipelines/setup-linux.sh deleted file mode 100755 index c5ecb550b..000000000 --- a/azure-pipelines/setup-linux.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -set -e -set -x - -TMPDIR=${TMPDIR:-/tmp} - -if [ -z "$SKIP_APT" ]; then - apt-get update - apt-get -y install build-essential pkg-config clang cmake openssl libssl-dev libssh2-1-dev libcurl4-gnutls-dev openssh-server -fi - -mkdir -p /var/run/sshd - -if [ "$MBEDTLS" ]; then - MBEDTLS_DIR=${MBEDTLS_DIR:-$(mktemp -d ${TMPDIR}/mbedtls.XXXXXXXX)} - - git clone --depth 10 --single-branch --branch mbedtls-2.6.1 https://github.com/ARMmbed/mbedtls.git ${MBEDTLS_DIR} - cd ${MBEDTLS_DIR} - - CFLAGS=-fPIC cmake -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . - cmake --build . - - if [ -z "$SKIP_MBEDTLS_INSTALL" ]; then - make install - fi -fi diff --git a/cmake/Modules/PkgBuildConfig.cmake b/cmake/Modules/PkgBuildConfig.cmake index 1c0079eb0..e4c574431 100644 --- a/cmake/Modules/PkgBuildConfig.cmake +++ b/cmake/Modules/PkgBuildConfig.cmake @@ -1,10 +1,5 @@ # pkg-config file generation # -# Uses the following globals: -# - PKG_BUILD_PREFIX: the build location (aka prefix). Defaults to CMAKE_INSTALL_PREFIX -# - PKG_BUILD_LIBDIR: the libdir location. Defaults to ${prefix}/lib. -# - PKG_BUILD_INCLUDEDIR: the includedir location. Defaults to ${prefix}/include. -# function(pkg_build_config) set(options) @@ -29,37 +24,11 @@ function(pkg_build_config) message(FATAL_ERROR "Missing VERSION argument") endif() - if (DEFINED PKG_BUILD_PREFIX) - set(PKGCONFIG_PREFIX "${PKG_BUILD_PREFIX}") - else() - set(PKGCONFIG_PREFIX "${CMAKE_INSTALL_PREFIX}") - endif() - - if(DEFINED PKG_BUILD_LIBDIR) - if (IS_ABSOLUTE ${PKG_BUILD_LIBDIR}) - set(PKGCONFIG_LIBDIR ${PKG_BUILD_LIBDIR}) - else() - set(PKGCONFIG_LIBDIR "\${prefix}/${PKG_BUILD_LIBDIR}") - endif() - else() - set(PKGCONFIG_LIBDIR "\${prefix}/lib") - endif() - - if(DEFINED PKG_BUILD_INCLUDEDIR) - if (IS_ABSOLUTE ${PKG_BUILD_INCLUDEDIR}) - set(PKGCONFIG_INCLUDEDIR ${PKG_BUILD_INCLUDEDIR}) - else() - set(PKGCONFIG_INCLUDEDIR "\${prefix}/${PKG_BUILD_INCLUDEDIR}") - endif() - else() - set(PKGCONFIG_INCLUDEDIR "\${prefix}/include") - endif() - # Write .pc "header" file(WRITE "${PKGCONFIG_FILE}" - "prefix=\"${PKGCONFIG_PREFIX}\"\n" - "libdir=\"${PKGCONFIG_LIBDIR}\"\n" - "includedir=\"${PKGCONFIG_INCLUDEDIR}\"\n" + "prefix=\"${CMAKE_INSTALL_PREFIX}\"\n" + "libdir=\"${LIB_INSTALL_DIR}\"\n" + "includedir=\"${INCLUDE_INSTALL_DIR}\"\n" "\n" "Name: ${PKGCONFIG_NAME}\n" "Description: ${PKGCONFIG_DESCRIPTION}\n" @@ -105,6 +74,6 @@ function(pkg_build_config) # Install .pc file install(FILES "${PKGCONFIG_FILE}" - DESTINATION "${PKGCONFIG_PREFIX}/${PKGCONFIG_LIBDIR}/pkgconfig" + DESTINATION "${LIB_INSTALL_DIR}/pkgconfig" ) endfunction() diff --git a/deps/ntlmclient/compat.h b/deps/ntlmclient/compat.h index efdf34514..555fa3fe4 100644 --- a/deps/ntlmclient/compat.h +++ b/deps/ntlmclient/compat.h @@ -22,8 +22,30 @@ #endif #ifdef __linux__ +/* See man page endian(3) */ # include <endian.h> # define htonll htobe64 +#elif defined(__OpenBSD__) +/* See man page htobe64(3) */ +# include <endian.h> +# define htonll htobe64 +#elif defined(__FreeBSD__) +/* See man page bwaps64(9) */ +# include <sys/endian.h> +# define htonll htobe64 +#elif defined(sun) || defined(__sun) +/* See man page byteorder(3SOCKET) */ +# include <sys/types.h> +# include <netinet/in.h> +# include <inttypes.h> + +# if !defined(htonll) +# if defined(_BIG_ENDIAN) +# define htonll(x) (x) +# else +# define htonll(x) ((((uint64_t)htonl(x)) << 32) + htonl((uint64_t)(x) >> 32)) +# endif +# endif #endif #ifndef MIN diff --git a/docs/changelog.md b/docs/changelog.md index c75164901..2c50a6611 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,289 @@ -v0.28 + 1 ---------- +v0.99 +----- + +This is v0.99 "Torschlusspanik". This will be the last minor release +before libgit2 v1.0. We expect to only respond to bugs in this release, +to stabilize it for next major release. + +It contains significant refactorings, but is expected to be API-compatible +with v0.28.0. + +### Changes or improvements + +* When fetching from an anonymous remote using a URL with authentication + information provided in the URL (eg `https://foo:bar@example.com/repo`), + we would erroneously include the literal URL in the FETCH_HEAD file. + We now remove that to match git's behavior. + +* Some credential structures, enums and values have been renamed: + `git_cred` is now `git_credential`. `git_credtype_t` is now + `git_credential_t`. Functions and types beginning with + `git_cred_` now begin with `git_credential`, and constants beginning + with `GIT_CREDTYPE` now begin with `GIT_CREDENTIAL`. The former names + are deprecated. + +* Several function signatures have been changed to return an `int` to + indicate error conditions. We encourage you to check them for errors + in the standard way. + + * `git_attr_cache_flush` + * `git_error_set_str` + * `git_index_name_clear` + * `git_index_reuc_clear` + * `git_libgit2_version` + * `git_mempack_reset` + * `git_oid_cpy` + * `git_oid_fmt` + * `git_oid_fromraw` + * `git_oid_nfmt` + * `git_oid_pathfmt` + * `git_remote_stop` + * `git_remote_disconnect` + * `git_repository__cleanup` + * `git_repository_set_config` + * `git_repository_set_index` + * `git_repository_set_odb` + * `git_repository_set_refdb` + * `git_revwalk_reset` + * `git_revwalk_simplify_first_parent` + * `git_revwalk_sorting` + * `git_treebuilder_clear` + * `git_treebuilder_filter` + +* The NTLM and Negotiate authentication mechanisms are now supported when + talking to git implementations hosted on Apache or nginx servers. + +* The `HEAD` symbolic reference can no longer be deleted. + +* `git_merge_driver_source_repo` no longer returns a `const git_repository *`, + it now returns a non-`const` `git_repository *`. + +* Relative symbolic links are now supported on Windows when `core.symlinks` + is enabled. + +* Servers that provide query parameters with a redirect are now supported. + +* `git_submodule_sync` will now resolve relative URLs. + +* When creating git endpoint URLs, double-slashes are no longer used when + the given git URL has a trailing slash. + +* On Windows, a `DllMain` function is no longer included and thread-local + storage has moved to fiber-local storage in order to prevent race + conditions during shutdown. + +* The tracing mechanism (`GIT_TRACE`) is now enabled by default and does + not need to be explicitly enabled in CMake. + +* The size of Git objects is now represented by `git_object_size_t` + instead of `off_t`. + +* Binary patches without data can now be parsed. + +* A configuration snapshot can now be created from another configuration + snapshot, not just a "true" configuration object. + +* The `git_commit_with_signature` API will now ensure that referenced + objects exist in the object database. + +* Stash messages containing newlines will now be replaced with spaces; + they will no longer be (erroneously) written to the repository. + +* `git_commit_create_with_signature` now verifies the commit information + to ensure that it points to a valid tree and valid parents. + +* `git_apply` has an option `GIT_APPLY_CHECK` that will only do a dry-run. + The index and working directory will remain unmodified, and application + will report if it would have worked. + +* Patches produced by Mercurial (those that lack some git extended headers) + can now be parsed and applied. + +* Reference locks are obeyed correctly on POSIX platforms, instead of + being removed. + +* Patches with empty new files can now be read and applied. + +* `git_apply_to_tree` can now correctly apply patches that add new files. + +* The program data configuration on Windows (`C:\ProgramData\Git\config`) + must be owned by an administrator, a system account or the current user + to be read. + +* `git_blob_filtered_content` is now deprecated in favor of `git_blob_filter`. + +* Configuration files can now be included conditionally using the + `onbranch` conditional. + +* Checkout can now properly create and remove symbolic links to directories + on Windows. + +* Stash no longer recomputes trees when committing a worktree, for + improved performance. + +* Repository templates can now include a `HEAD` file to default the + initial default branch. + +* Some configuration structures, enums and values have been renamed: + `git_cvar_map` is now `git_configmap`, `git_cvar_t` is now + `git_configmap_t`, `GIT_CVAR_FALSE` is now `GIT_CONFIGMAP_FALSE`, + `GIT_CVAR_TRUE` is now `GIT_CONFIGMAP_TRUE`, `GIT_CVAR_INT32` is now + `GIT_CONFIGMAP_INT32`, and `GIT_CVAR_STRING` is now `GIT_CONFIGMAP_STRING`. + The former names are deprecated. + +* Repositories can now be created at the root of a Windows drive. + +* Configuration lookups are now more efficiently cached. + +* `git_commit_create_with_signature` now supports a `NULL` signature, + which will create a commit without adding a signature. + +* When a repository lacks an `info` "common directory", we will no + longer erroneously return `GIT_ENOTFOUND` for all attribute lookups. + +* Several attribute macros have been renamed: `GIT_ATTR_TRUE` is now + `GIT_ATTR_IS_TRUE`, `GIT_ATTR_FALSE` is now `GIT_ATTR_IS_FALSE`, + `GIT_ATTR_UNSPECIFIED` is now `GIT_ATTR_IS_UNSPECIFIED`. The + attribute enum `git_attr_t` is now `git_attr_value_t` and its + values have been renamed: `GIT_ATTR_UNSPECIFIED_T` is now + `GIT_ATTR_VALUE_UNSPECIFIED`, `GIT_ATTR_TRUE_T` is now + `GIT_ATTR_VALUE_TRUE`, `GIT_ATTR_FALSE_T` is now `GIT_ATTR_VALUE_FALSE`, + and `GIT_ATTR_VALUE_T` is now `GIT_ATTR_VALUE_STRING`. The + former names are deprecated. + +* `git_object__size` is now `git_object_size`. The former name is + deprecated. + +* `git_tag_create_frombuffer` is now `git_tag_create_from_buffer`. The + former name is deprecated. + +* Several blob creation functions have been renamed: + `git_blob_create_frombuffer` is now named `git_blob_create_from_buffer`, + `git_blob_create_fromdisk` is now named `git_blob_create_from_disk`, + `git_blob_create_fromworkdir` is now named `git_blob_create_from_workdir`, + `git_blob_create_fromstream` is now named `git_blob_create_from_stream`, + and `git_blob_create_fromstream_commit` is now named + `git_blob_create_from_stream_commit`. The former names are deprecated. + +* The function `git_oid_iszero` is now named `git_oid_is_zero`. The + former name is deprecated. + +* Pattern matching is now done using `wildmatch` instead of `fnmatch` + for compatibility with git. + +* The option initialization functions suffixed by `init_options` are now + suffixed with `options_init`. (For example, `git_checkout_init_options` + is now `git_checkout_options_init`.) The former names are deprecated. + +* NTLM2 authentication is now supported on non-Windows platforms. + +* The `git_cred_sign_callback` callback is now named `git_cred_sign_cb`. + The `git_cred_ssh_interactive_callback` callback is now named + `git_cred_ssh_interactive_cb`. + +* Ignore files now: + + * honor escaped trailing whitespace. + * do not incorrectly negate sibling paths of a negated pattern. + * honor rules that stop ignoring files after a wildcard + +* Attribute files now: + + * honor leading and trailing whitespace. + * treat paths beginning with `\` as absolute only on Windows. + * properly handle escaped characters. + * stop reading macros defined in subdirectories + +* The C locale is now correctly used when parsing regular expressions. + +* The system PCRE2 or PCRE regular expression libraries are now used + when `regcomp_l` is not available on the system. If none of these + are available on the system, an included version of PCRE is used. + +* Wildcards in reference specifications are now supported beyond simply + a bare wildcard (`*`) for compatibility with git. + +* When `git_ignore_path_is_ignored` is provided a path with a trailing + slash (eg, `dir/`), it will now treat it as a directory for the + purposes of ignore matching. + +* Patches that add or remove a file with a space in the path can now + be correctly parsed. + +* The `git_remote_completion_type` type is now `git_remote_completion_t`. + The former name is deprecated. + +* The `git_odb_backend_malloc` is now `git_odb_backend_data_alloc`. The + former name is deprecated. + +* The `git_transfer_progress_cb` callback is now `git_indexer_progress_cb` + and the `git_transfer_progress` structure is now `git_indexer_progress`. + The former names are deprecated. + +* The example projects are now contained in a single `lg2` executable + for ease of use. + +* libgit2 now correctly handles more URLs, such as + `http://example.com:/repo.git` (colon but no port), + `http://example.com` (no path), + and `http://example.com:8080/` (path is /, nonstandard port). + +* A carefully constructed commit object with a very large number + of parents may lead to potential out-of-bounds writes or + potential denial of service. + +* The ProgramData configuration file is always read for compatibility + with Git for Windows and Portable Git installations. The ProgramData + location is not necessarily writable only by administrators, so we + now ensure that the configuration file is owned by the administrator + or the current user. + +### API additions + +* The SSH host key now supports SHA-256 when `GIT_CERT_SSH_SHA256` is set. + +* The diff format option `GIT_DIFF_FORMAT_PATCH_ID` can now be used to + emit an output like `git patch-id`. + +* The `git_apply_options_init` function will initialize a + `git_apply_options` structure. + +* The remote callbacks structure adds a `git_url_resolve_cb` callback + that is invoked when connecting to a server, so that applications + may edit or replace the URL before connection. + +* The information about the original `HEAD` in a rebase operation is + available with `git_rebase_orig_head_name`. Its ID is available with + `git_rebase_orig_head_id`. The `onto` reference name is available with + `git_rebase_onto_name` and its ID is available with `git_rebase_onto_id`. + +* ODB backends can now free backend data when an error occurs during its + backend data creation using `git_odb_backend_data_free`. + +* Options may be specified to `git_repository_foreach_head` to control + its behavior: `GIT_REPOSITORY_FOREACH_HEAD_SKIP_REPO` will not skip + the main repository's HEAD reference, while + `GIT_REPOSITORY_FOREACH_HEAD_SKIP_WORKTREES` will now skip the + worktree HEAD references. + +* The `GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS` option can be specified to + `git_libgit2_opts()` to avoid looking for `.keep` files that correspond + to packfiles. This setting can improve performance when packfiles are + stored on high-latency filesystems like network filesystems. + +* Blobs can now be filtered with `git_blob_filter`, which allows for + options to be set with `git_blob_filter_options`, including + `GIT_FILTER_NO_SYSTEM_ATTRIBUTES` to disable filtering with system-level + attributes in `/etc/gitattributes` and `GIT_ATTR_CHECK_INCLUDE_HEAD` to + enable filtering with `.gitattributes` files in the HEAD revision. + +### API removals + +* The unused `git_headlist_cb` function declaration was removed. + +* The unused `git_time_monotonic` API is removed. + +* The erroneously exported `inttypes.h` header was removed. # Security Fixes @@ -82,22 +366,62 @@ v0.28 + 1 "CollisionDetection". If you were using `SHA1_BACKEND` previously, you'll need to check the value you've used, or switch to the autodetection. -### Changes or improvements - -* libgit2 now correctly handles more URLs, such as - `http://example.com:/repo.git` (colon but no port), - `http://example.com` (no path), - and `http://example.com:8080/` (path is /, nonstandard port). - -* A carefully constructed commit object with a very large number - of parents may lead to potential out-of-bounds writes or - potential denial of service. - -* The ProgramData configuration file is always read for compatibility - with Git for Windows and Portable Git installations. The ProgramData - location is not necessarily writable only by administrators, so we - now ensure that the configuration file is owned by the administrator - or the current user. +### Authors + +The following individuals provided changes that were included in this +release: + +* Aaron Patterson +* Alberto Fanjul +* Anders Borum +* Augie Fackler +* Augustin Fabre +* Ayush Shridhar +* brian m. carlson +* buddyspike +* Carlos MartÃn Nieto +* cheese1 +* Dan Skorupski +* Daniel Cohen Gindi +* Dave Lee +* David Brooks +* David Turner +* Denis Laxalde +* Dhruva Krishnamurthy +* Dominik Ritter +* Drew DeVault +* Edward Thomson +* Eric Huss +* Erik Aigner +* Etienne Samson +* Gregory Herrero +* Heiko Voigt +* Ian Hattendorf +* Jacques Germishuys +* Janardhan Pulivarthi +* Jason Haslam +* Johannes Schindelin +* Jordan Wallet +* Josh Bleecher Snyder +* kas +* kdj0c +* Laurence McGlashan +* lhchavez +* Lukas Berk +* Max Kostyukevich +* Patrick Steinhardt +* pcpthm +* Remy Suen +* Robert Coup +* romkatv +* Scott Furry +* Sebastian Henke +* Stefan Widgren +* Steve King Jr +* Sven Strickroth +* Tobias Nießen +* Tyler Ang-Wanek +* Tyler Wanek v0.28 ----- diff --git a/docs/fuzzing.md b/docs/fuzzing.md index cd825766b..25b32cb04 100644 --- a/docs/fuzzing.md +++ b/docs/fuzzing.md @@ -3,7 +3,7 @@ libgit2 is currently using [libFuzzer](https://libfuzzer.info) to perform automated fuzz testing. libFuzzer only works with clang. -## Prerequisites** for building fuzz targets: +## Prerequisites for building fuzz targets: 1. All the prerequisites for [building libgit2](https://github.com/libgit2/libgit2). 2. A recent version of clang. 6.0 is preferred. [pre-build Debian/Ubuntu @@ -27,14 +27,15 @@ automated fuzz testing. libFuzzer only works with clang. ## Run the fuzz targets -1. `ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolize-6.0 +1. `ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolize LSAN_OPTIONS=allocator_may_return_null=1 - ASAN_OPTIONS=allocator_may_return_null=1 ./build/fuzz/fuzz_packfile_raw - fuzz/corpora/fuzz_packfile_raw/` + ASAN_OPTIONS=allocator_may_return_null=1 ./build/fuzzers/packfile_fuzzer + fuzzers/corpora/packfile/` The `LSAN_OPTIONS` and `ASAN_OPTIONS` are there to allow `malloc(3)` to return -`NULL`. The `LLVM_PROFILE_FILE` is there to override the path where libFuzzer -will write the coverage report. +`NULL`, which is expected if a huge chunk of memory is allocated. The +`LLVM_PROFILE_FILE` environment string can also be added to override the path +where libFuzzer will write the coverage report. ## Get coverage diff --git a/include/git2/version.h b/include/git2/version.h index 148808ca0..4a824ffea 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.28.0" +#define LIBGIT2_VERSION "0.99.0" #define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 28 +#define LIBGIT2_VER_MINOR 99 #define LIBGIT2_VER_REVISION 0 #define LIBGIT2_VER_PATCH 0 -#define LIBGIT2_SOVERSION 28 +#define LIBGIT2_SOVERSION "0.99" #endif diff --git a/script/release.py b/script/release.py new file mode 100755 index 000000000..e0f29538e --- /dev/null +++ b/script/release.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python + +from collections import namedtuple + +import argparse +import base64 +import copy +import json +import subprocess +import sys +import urllib.parse +import urllib.request +import urllib.error + +class Error(Exception): + pass + +class Version(object): + def __init__(self, version): + versions = version.split(sep='.') + if len(versions) < 2 or len(versions) > 3: + raise Error("Invalid version string '{}'".format(version)) + self.major = int(versions[0]) + self.minor = int(versions[1]) + self.revision = int(versions[2]) if len(versions) == 3 else 0 + + def __str__(self): + return '{}.{}.{}'.format(self.major, self.minor, self.revision) + + def __eq__(self, other): + return self.major == other.major and self.minor == other.minor and self.revision == other.revision + +def verify_version(version): + expected = { + 'VERSION': [ '"{}"'.format(version), None ], + 'VER_MAJOR': [ str(version.major), None ], + 'VER_MINOR': [ str(version.minor), None ], + 'VER_REVISION': [ str(version.revision), None ], + 'VER_PATCH': [ '0', None ], + 'SOVERSION': [ '"{}.{}"'.format(version.major, version.minor), None ], + } + + with open('include/git2/version.h') as f: + lines = f.readlines() + + for key in expected.keys(): + define = '#define LIBGIT2_{} '.format(key) + for line in lines: + if line.startswith(define): + expected[key][1] = line[len(define):].strip() + break + else: + raise Error("version.h: missing define for '{}'".format(key)) + + for k, v in expected.items(): + if v[0] != v[1]: + raise Error("version.h: define '{}' does not match (got '{}', expected '{}')".format(k, v[0], v[1])) + +def generate_relnotes(tree, version): + with open('docs/changelog.md') as f: + lines = f.readlines() + + if not lines[0].startswith('v'): + raise Error("changelog.md: missing section for v{}".format(version)) + try: + v = Version(lines[0][1:].strip()) + except: + raise Error("changelog.md: invalid version string {}".format(lines[0].strip())) + if v != version: + raise Error("changelog.md: changelog version doesn't match (got {}, expected {})".format(v, version)) + if not lines[1].startswith('----'): + raise Error("changelog.md: missing version header") + if lines[2] != '\n': + raise Error("changelog.md: missing newline after version header") + + for i, line in enumerate(lines[3:]): + if not line.startswith('v'): + continue + try: + Version(line[1:].strip()) + break + except: + continue + else: + raise Error("changelog.md: cannot find section header of preceding release") + + return ''.join(lines[3:i + 3]).strip() + +def git(*args): + process = subprocess.run([ 'git', *args ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if process.returncode != 0: + raise Error('Failed executing git {}: {}'.format(' '.join(args), process.stderr.decode())) + return process.stdout + +def post(url, data, contenttype, user, password): + request = urllib.request.Request(url, data=data) + request.add_header('Accept', 'application/json') + request.add_header('Content-Type', contenttype) + request.add_header('Content-Length', len(data)) + request.add_header('Authorization', 'Basic ' + base64.b64encode('{}:{}'.format(user, password).encode()).decode()) + + try: + response = urllib.request.urlopen(request) + if response.getcode() != 201: + raise Error("POST to '{}' failed: {}".format(url, response.reason)) + except urllib.error.URLError as e: + raise Error("POST to '{}' failed: {}".format(url, e)) + data = json.load(response) + + return data + +def generate_asset(version, tree, archive_format): + Asset = namedtuple('Asset', ['name', 'label', 'mimetype', 'data']) + mimetype = 'application/{}'.format('gzip' if archive_format == 'tar.gz' else 'zip') + return Asset( + "libgit2-{}.{}".format(version, archive_format), "Release sources: libgit2-{}.{}".format(version, archive_format), mimetype, + git('archive', '--format', archive_format, '--prefix', 'libgit2-{}/'.format(version), tree) + ) + +def release(args): + params = { + "tag_name": 'v' + str(args.version), + "name": 'libgit2 v' + str(args.version), + "target_commitish": git('rev-parse', args.tree).decode().strip(), + "body": generate_relnotes(args.tree, args.version), + } + assets = [ + generate_asset(args.version, args.tree, 'tar.gz'), + generate_asset(args.version, args.tree, 'zip'), + ] + + if args.dryrun: + for k, v in params.items(): + print('{}: {}'.format(k, v)) + for asset in assets: + print('asset: name={}, label={}, mimetype={}, bytes={}'.format(asset.name, asset.label, asset.mimetype, len(asset.data))) + return + + try: + url = 'https://api.github.com/repos/{}/releases'.format(args.repository) + response = post(url, json.dumps(params).encode(), 'application/json', args.user, args.password) + except Error as e: + raise Error('Could not create release: ' + str(e)) + + for asset in assets: + try: + url = list(urllib.parse.urlparse(response['upload_url'].split('{?')[0])) + url[4] = urllib.parse.urlencode({ 'name': asset.name, 'label': asset.label }) + post(urllib.parse.urlunparse(url), asset.data, asset.mimetype, args.user, args.password) + except Error as e: + raise Error('Could not upload asset: ' + str(e)) + +def main(): + parser = argparse.ArgumentParser(description='Create a libgit2 release') + parser.add_argument('--tree', default='HEAD', help='tree to create release for (default: HEAD)') + parser.add_argument('--dryrun', action='store_true', help='generate release, but do not post it') + parser.add_argument('--repository', default='libgit2/libgit2', help='GitHub repository to create repository in') + parser.add_argument('--user', help='user to authenitcate as') + parser.add_argument('--password', help='password to authenticate with') + parser.add_argument('version', type=Version, help='version of the new release') + args = parser.parse_args() + + verify_version(args.version) + release(args) + +if __name__ == '__main__': + try: + main() + except Error as e: + print(e) + sys.exit(1) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de24b975a..1915e8a9a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -140,7 +140,7 @@ ELSEIF(REGEX_BACKEND STREQUAL "pcre2") LIST(APPEND LIBGIT2_SYSTEM_INCLUDES ${PCRE2_INCLUDE_DIRS}) LIST(APPEND LIBGIT2_LIBS ${PCRE2_LIBRARIES}) - LIST(APPEND LIBGIT2_PC_REQUIRES "libpcre2") + LIST(APPEND LIBGIT2_PC_REQUIRES "libpcre2-8") ELSEIF(REGEX_BACKEND STREQUAL "pcre") ADD_FEATURE_INFO(regex ON "using system PCRE") SET(GIT_REGEX_PCRE 1) @@ -288,14 +288,13 @@ IF (WIN32 AND NOT CYGWIN) ELSEIF (AMIGA) ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP) ELSE() - ADD_FEATURE_INFO(valgrind VALGRIND "valgrind hints") - IF (VALGRIND) - ADD_DEFINITIONS(-DVALGRIND) - ENDIF() - FILE(GLOB SRC_OS unix/*.c unix/*.h) ENDIF() +IF (USE_LEAK_CHECKER STREQUAL "valgrind") + ADD_DEFINITIONS(-DVALGRIND) +ENDIF() + FILE(GLOB SRC_GIT2 *.c *.h allocators/*.c allocators/*.h streams/*.c streams/*.h diff --git a/src/cache.c b/src/cache.c index 32ba993b0..af42b3959 100644 --- a/src/cache.c +++ b/src/cache.c @@ -208,10 +208,14 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) entry = stored_entry; } else if (stored_entry->flags == GIT_CACHE_STORE_RAW && entry->flags == GIT_CACHE_STORE_PARSED) { - git_cached_obj_decref(stored_entry); - git_cached_obj_incref(entry); - - git_oidmap_set(cache->map, &entry->oid, entry); + if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) { + git_cached_obj_decref(stored_entry); + git_cached_obj_incref(entry); + } else { + git_cached_obj_decref(entry); + git_cached_obj_incref(stored_entry); + entry = stored_entry; + } } else { /* NO OP */ } diff --git a/src/indexer.c b/src/indexer.c index 717549fa2..68fdd85c5 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -150,11 +150,11 @@ int git_indexer_new( idx->progress_cb = opts.progress_cb; idx->progress_payload = opts.progress_cb_payload; idx->mode = mode ? mode : GIT_PACK_FILE_MODE; - git_hash_ctx_init(&idx->hash_ctx); - git_hash_ctx_init(&idx->trailer); git_buf_init(&idx->entry_data, 0); - if ((error = git_oidmap_new(&idx->expected_oids)) < 0) + if ((error = git_hash_ctx_init(&idx->hash_ctx)) < 0 || + (error = git_hash_ctx_init(&idx->trailer)) < 0 || + (error = git_oidmap_new(&idx->expected_oids)) < 0) goto cleanup; idx->do_verify = opts.verify; diff --git a/src/notes.c b/src/notes.c index 4633a16ea..68d2ae9ec 100644 --- a/src/notes.c +++ b/src/notes.c @@ -808,8 +808,11 @@ int git_note_next( git_oid_cpy(note_id, &item->id); - if (!(error = process_entry_path(item->path, annotated_id))) - git_iterator_advance(NULL, it); + if ((error = process_entry_path(item->path, annotated_id)) < 0) + return error; - return error; + if ((error = git_iterator_advance(NULL, it)) < 0 && error != GIT_ITEROVER) + return error; + + return 0; } diff --git a/src/odb_pack.c b/src/odb_pack.c index c93d07c46..86c858df1 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -15,7 +15,6 @@ #include "hash.h" #include "odb.h" #include "delta.h" -#include "sha1_lookup.h" #include "mwindow.h" #include "pack.h" diff --git a/src/pack-objects.c b/src/pack-objects.c index bdd5171a8..49b4e4772 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -374,7 +374,9 @@ static int write_object( GIT_ERROR_CHECK_ALLOC(zbuf); git_zstream_reset(&pb->zstream); - git_zstream_set_input(&pb->zstream, data, data_len); + + if ((error = git_zstream_set_input(&pb->zstream, data, data_len)) < 0) + goto done; while (!git_zstream_done(&pb->zstream)) { if ((error = git_zstream_get_output(zbuf, &zbuf_len, &pb->zstream)) < 0 || diff --git a/src/pack.c b/src/pack.c index 7c6fc2cdb..fcf64f57d 100644 --- a/src/pack.c +++ b/src/pack.c @@ -12,7 +12,6 @@ #include "mwindow.h" #include "odb.h" #include "oid.h" -#include "sha1_lookup.h" /* Option to bypass checking existence of '.keep' files */ bool git_disable_pack_keep_file_checks = false; @@ -1239,6 +1238,27 @@ int git_pack_foreach_entry( return error; } +static int sha1_position(const void *table, size_t stride, unsigned lo, + unsigned hi, const unsigned char *key) +{ + const unsigned char *base = table; + + while (lo < hi) { + unsigned mi = (lo + hi) / 2; + int cmp = git_oid__hashcmp(base + mi * stride, key); + + if (!cmp) + return mi; + + if (cmp > 0) + hi = mi; + else + lo = mi+1; + } + + return -((int)lo)-1; +} + static int pack_entry_find_offset( off64_t *offset_out, git_oid *found_oid, diff --git a/src/push.c b/src/push.c index 67ebcfb3e..34867c2e4 100644 --- a/src/push.c +++ b/src/push.c @@ -349,8 +349,9 @@ static int queue_objects(git_push *push) if (git_oid_is_zero(&head->oid)) continue; - /* TODO */ - git_revwalk_hide(rw, &head->oid); + if ((error = git_revwalk_hide(rw, &head->oid)) < 0 && + error != GIT_ENOTFOUND && error != GIT_EINVALIDSPEC && error != GIT_EPEEL) + goto on_error; } error = git_packbuilder_insert_walk(push->pb, rw); diff --git a/src/repository.c b/src/repository.c index 2469e13e6..fe0d696c6 100644 --- a/src/repository.c +++ b/src/repository.c @@ -189,19 +189,25 @@ void git_repository_free(git_repository *repo) * * Open a repository object from its path */ -static bool valid_repository_path(git_buf *repository_path, git_buf *common_path) +static int is_valid_repository_path(bool *out, git_buf *repository_path, git_buf *common_path) { + int error; + + *out = 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 ((error = git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE)) < 0 || + (error = git_futils_readbuffer(&common_link, common_link.ptr)) < 0) + return error; + git_buf_rtrim(&common_link); if (git_path_is_relative(common_link.ptr)) { - git_buf_joinpath(common_path, repository_path->ptr, common_link.ptr); + if ((error = git_buf_joinpath(common_path, repository_path->ptr, common_link.ptr)) < 0) + return error; } else { git_buf_swap(common_path, &common_link); } @@ -209,24 +215,26 @@ static bool valid_repository_path(git_buf *repository_path, git_buf *common_path git_buf_dispose(&common_link); } else { - git_buf_set(common_path, repository_path->ptr, repository_path->size); + if ((error = git_buf_set(common_path, repository_path->ptr, repository_path->size)) < 0) + return error; } /* 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, '/'); + if ((error = git_buf_putc(common_path, '/')) < 0) + return error; /* Ensure HEAD file exists */ if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false) - return false; - + return 0; /* Check files in common dir */ if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false) - return false; + return 0; if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false) - return false; + return 0; - return true; + *out = true; + return 0; } static git_repository *repository_alloc(void) @@ -441,15 +449,15 @@ static int find_repo( uint32_t flags, const char *ceiling_dirs) { - 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; + bool in_dot_git, is_valid; size_t ceiling_offset = 0; + int error; git_buf_clear(gitdir_path); @@ -475,9 +483,8 @@ static int find_repo( for (;;) { if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) { if (!in_dot_git) { - error = git_buf_joinpath(&path, path.ptr, DOT_GIT); - if (error < 0) - break; + if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) + goto out; } in_dot_git = !in_dot_git; } @@ -491,28 +498,33 @@ static int find_repo( break; if (S_ISDIR(st.st_mode)) { - if (valid_repository_path(&path, &common_link)) { - git_path_to_dir(&path); - git_buf_set(gitdir_path, path.ptr, path.size); + if ((error = is_valid_repository_path(&is_valid, &path, &common_link)) < 0) + goto out; + + if (is_valid) { + if ((error = git_path_to_dir(&path)) < 0 || + (error = git_buf_set(gitdir_path, path.ptr, path.size)) < 0) + goto out; if (gitlink_path) - git_buf_attach(gitlink_path, - git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0); + if ((error = git_buf_attach(gitlink_path, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0) + goto out; if (commondir_path) git_buf_swap(&common_link, commondir_path); break; } - } - else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) { - error = read_gitfile(&repo_link, path.ptr); - if (error < 0) - break; - if (valid_repository_path(&repo_link, &common_link)) { + } else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) { + if ((error = read_gitfile(&repo_link, path.ptr)) < 0 || + (error = is_valid_repository_path(&is_valid, &repo_link, &common_link)) < 0) + goto out; + + if (is_valid) { git_buf_swap(gitdir_path, &repo_link); if (gitlink_path) - error = git_buf_put(gitlink_path, path.ptr, path.size); + if ((error = git_buf_put(gitlink_path, path.ptr, path.size)) < 0) + goto out; if (commondir_path) git_buf_swap(&common_link, commondir_path); } @@ -523,10 +535,8 @@ static int find_repo( /* Move up one directory. If we're in_dot_git, we'll search the * parent itself next. If we're !in_dot_git, we'll search .git * in the parent directory next (added at the top of the loop). */ - if (git_path_dirname_r(&path, path.ptr) < 0) { - error = -1; - break; - } + if ((error = git_path_dirname_r(&path, path.ptr)) < 0) + goto out; /* Once we've checked the directory (and .git if applicable), * find the ceiling for a search. */ @@ -534,31 +544,28 @@ static int find_repo( ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs); /* Check if we should stop searching here. */ - if (min_iterations == 0 - && (path.ptr[ceiling_offset] == 0 - || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH))) + if (min_iterations == 0 && + (path.ptr[ceiling_offset] == 0 || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH))) break; } - if (!error && workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { + if (workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { if (!git_buf_len(gitdir_path)) git_buf_clear(workdir_path); - else { - git_path_dirname_r(workdir_path, path.ptr); - git_path_to_dir(workdir_path); - } - if (git_buf_oom(workdir_path)) - return -1; + else if ((error = git_path_dirname_r(workdir_path, path.ptr)) < 0 || + (error = git_path_to_dir(workdir_path)) < 0) + goto out; } /* If we didn't find the repository, and we don't have any other error * to report, report that. */ - if (!git_buf_len(gitdir_path) && !error) { - git_error_set(GIT_ERROR_REPOSITORY, - "could not find repository from '%s'", start_path); + if (!git_buf_len(gitdir_path)) { + git_error_set(GIT_ERROR_REPOSITORY, "could not find repository from '%s'", start_path); error = GIT_ENOTFOUND; + goto out; } +out: git_buf_dispose(&path); git_buf_dispose(&repo_link); git_buf_dispose(&common_link); @@ -569,14 +576,16 @@ int git_repository_open_bare( git_repository **repo_ptr, const char *bare_path) { - int error; git_buf path = GIT_BUF_INIT, common_path = GIT_BUF_INIT; git_repository *repo = NULL; + bool is_valid; + int error; - if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0) + if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0 || + (error = is_valid_repository_path(&is_valid, &path, &common_path)) < 0) return error; - if (!valid_repository_path(&path, &common_path)) { + if (!is_valid) { git_buf_dispose(&path); git_buf_dispose(&common_path); git_error_set(GIT_ERROR_REPOSITORY, "path is not a repository: %s", bare_path); @@ -2055,6 +2064,7 @@ int git_repository_init_ext( git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT, common_path = GIT_BUF_INIT, head_path = GIT_BUF_INIT; const char *wd; + bool is_valid; int error; assert(out && given_repo && opts); @@ -2066,7 +2076,10 @@ int git_repository_init_ext( wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path); - if (valid_repository_path(&repo_path, &common_path)) { + if ((error = is_valid_repository_path(&is_valid, &repo_path, &common_path)) < 0) + goto out; + + if (is_valid) { if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) { git_error_set(GIT_ERROR_REPOSITORY, "attempt to reinitialize '%s'", given_repo); diff --git a/src/revwalk.c b/src/revwalk.c index 4587b5acc..abbd65ac2 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -58,7 +58,7 @@ int git_revwalk__push_commit(git_revwalk *walk, const git_oid *oid, const git_re return 0; git_error_set(GIT_ERROR_INVALID, "object is not a committish"); - return -1; + return error; } if (error < 0) return error; diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c deleted file mode 100644 index 14fcb40e5..000000000 --- a/src/sha1_lookup.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "sha1_lookup.h" - -#include <stdio.h> - -#include "oid.h" - -int sha1_position(const void *table, - size_t stride, - unsigned lo, unsigned hi, - const unsigned char *key) -{ - const unsigned char *base = table; - - while (lo < hi) { - unsigned mi = (lo + hi) / 2; - int cmp = git_oid__hashcmp(base + mi * stride, key); - - if (!cmp) - return mi; - - if (cmp > 0) - hi = mi; - else - lo = mi+1; - } - - return -((int)lo)-1; -} diff --git a/src/sha1_lookup.h b/src/sha1_lookup.h deleted file mode 100644 index 841ea5b22..000000000 --- a/src/sha1_lookup.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#ifndef INCLUDE_sha1_lookup_h__ -#define INCLUDE_sha1_lookup_h__ - -#include "common.h" - -#include <stdlib.h> - -int sha1_position(const void *table, - size_t stride, - unsigned lo, unsigned hi, - const unsigned char *key); - -#endif diff --git a/src/streams/openssl.c b/src/streams/openssl.c index 98a3635af..5b66352ca 100644 --- a/src/streams/openssl.c +++ b/src/streams/openssl.c @@ -30,10 +30,6 @@ #include <openssl/x509v3.h> #include <openssl/bio.h> -#ifdef VALGRIND -# include <valgrind/memcheck.h> -#endif - 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" @@ -160,7 +156,7 @@ static void openssl_locking_function( lock = mode & CRYPTO_LOCK; if (lock) { - git_mutex_lock(&openssl_locks[n]); + (void)git_mutex_lock(&openssl_locks[n]); } else { git_mutex_unlock(&openssl_locks[n]); } @@ -200,16 +196,69 @@ static void shutdown_ssl(void) } } +#ifdef VALGRIND +#ifdef OPENSSL_LEGACY_API +static void *git_openssl_malloc(size_t bytes) +{ + return git__calloc(1, bytes); +} + +static void *git_openssl_realloc(void *mem, size_t size) +{ + return git__realloc(mem, size); +} + +static void git_openssl_free(void *mem) +{ + return git__free(mem); +} +#else +static void *git_openssl_malloc(size_t bytes, const char *file, int line) +{ + GIT_UNUSED(file); + GIT_UNUSED(line); + return git__calloc(1, bytes); +} + +static void *git_openssl_realloc(void *mem, size_t size, const char *file, int line) +{ + GIT_UNUSED(file); + GIT_UNUSED(line); + return git__realloc(mem, size); +} + +static void git_openssl_free(void *mem, const char *file, int line) +{ + GIT_UNUSED(file); + GIT_UNUSED(line); + return git__free(mem); +} +#endif +#endif + int git_openssl_stream_global_init(void) { long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; const char *ciphers = git_libgit2__ssl_ciphers(); +#ifdef VALGRIND + static bool allocators_initialized = false; +#endif /* Older OpenSSL and MacOS OpenSSL doesn't have this */ #ifdef SSL_OP_NO_COMPRESSION ssl_opts |= SSL_OP_NO_COMPRESSION; #endif +#ifdef VALGRIND + /* Swap in our own allocator functions that initialize allocated memory */ + if (!allocators_initialized && + CRYPTO_set_mem_functions(git_openssl_malloc, + git_openssl_realloc, + git_openssl_free) != 1) + goto error; + allocators_initialized = true; +#endif + OPENSSL_init_ssl(0, NULL); /* @@ -314,11 +363,6 @@ static int bio_read(BIO *b, char *buf, int len) static int bio_write(BIO *b, const char *buf, int len) { git_stream *io = (git_stream *) BIO_get_data(b); - -#ifdef VALGRIND - VALGRIND_MAKE_MEM_DEFINED(buf, len); -#endif - return (int) git_stream_write(io, buf, len, 0); } @@ -595,10 +639,6 @@ static int openssl_connect(git_stream *stream) BIO_set_data(bio, st->io); SSL_set_bio(st->ssl, bio, bio); -#ifdef VALGRIND - VALGRIND_MAKE_MEM_DEFINED(st->ssl, sizeof(SSL)); -#endif - /* specify the host in case SNI is needed */ #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME SSL_set_tlsext_host_name(st->ssl, st->host); @@ -609,10 +649,6 @@ static int openssl_connect(git_stream *stream) st->connected = true; -#ifdef VALGRIND - VALGRIND_MAKE_MEM_DEFINED(st->ssl, sizeof(SSL)); -#endif - return verify_server_cert(st->ssl, st->host); } @@ -679,10 +715,6 @@ static ssize_t openssl_read(git_stream *stream, void *data, size_t len) if ((ret = SSL_read(st->ssl, data, len)) <= 0) return ssl_set_error(st->ssl, ret); -#ifdef VALGRIND - VALGRIND_MAKE_MEM_DEFINED(data, ret); -#endif - return ret; } diff --git a/src/transports/auth_ntlm.c b/src/transports/auth_ntlm.c index 02a861f07..d134a3db6 100644 --- a/src/transports/auth_ntlm.c +++ b/src/transports/auth_ntlm.c @@ -50,10 +50,10 @@ static int ntlm_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cr cred = (git_credential_userpass_plaintext *)_cred; if ((sep = strchr(cred->username, '\\')) != NULL) { - domain = strndup(cred->username, (sep - cred->username)); + domain = git__strndup(cred->username, (sep - cred->username)); GIT_ERROR_CHECK_ALLOC(domain); - domainuser = strdup(sep + 1); + domainuser = git__strdup(sep + 1); GIT_ERROR_CHECK_ALLOC(domainuser); username = domainuser; diff --git a/src/transports/httpclient.c b/src/transports/httpclient.c index f5c2ce58d..3008aaaad 100644 --- a/src/transports/httpclient.c +++ b/src/transports/httpclient.c @@ -29,7 +29,18 @@ static git_http_auth_scheme auth_schemes[] = { { GIT_HTTP_AUTH_BASIC, "Basic", GIT_CREDENTIAL_USERPASS_PLAINTEXT, git_http_auth_basic }, }; -#define GIT_READ_BUFFER_SIZE 8192 +/* + * Use a 16kb read buffer to match the maximum size of a TLS packet. This + * is critical for compatibility with SecureTransport, which will always do + * a network read on every call, even if it has data buffered to return to + * you. That buffered data may be the _end_ of a keep-alive response, so + * if SecureTransport performs another network read, it will wait until the + * server ultimately times out before it returns that buffered data to you. + * Since SecureTransport only reads a single TLS packet at a time, by + * calling it with a read buffer that is the maximum size of a TLS packet, + * we ensure that it will never buffer. + */ +#define GIT_READ_BUFFER_SIZE (16 * 1024) typedef struct { git_net_url url; diff --git a/src/unix/posix.h b/src/unix/posix.h index d1f902489..4fa725013 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -33,7 +33,7 @@ typedef int GIT_SOCKET; # define st_atime_nsec st_atim.tv_nsec # define st_mtime_nsec st_mtim.tv_nsec # define st_ctime_nsec st_ctim.tv_nsec -#elif !defined(GIT_USE_STAT_MTIME_NSEC) && defined(GIT_USE_NEC) +#elif !defined(GIT_USE_STAT_MTIME_NSEC) && defined(GIT_USE_NSEC) # error GIT_USE_NSEC defined but unknown struct stat nanosecond type #endif diff --git a/src/worktree.c b/src/worktree.c index ef4ebfda8..e171afbb2 100644 --- a/src/worktree.c +++ b/src/worktree.c @@ -136,11 +136,11 @@ static int open_worktree_dir(git_worktree **out, const char *parent, const char 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 - || (parent && (wt->parent_path = git__strdup(parent)) == NULL) - || (wt->worktree_path = git_path_dirname(wt->gitlink_path)) == NULL) { + 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 || + (parent && (wt->parent_path = git__strdup(parent)) == NULL) || + (wt->worktree_path = git_path_dirname(wt->gitlink_path)) == NULL) { error = -1; goto out; } @@ -149,7 +149,10 @@ static int open_worktree_dir(git_worktree **out, const char *parent, const char goto out; wt->gitdir_path = git_buf_detach(&gitdir); - wt->locked = !!git_worktree_is_locked(NULL, wt); + if ((error = git_worktree_is_locked(NULL, wt)) < 0) + goto out; + wt->locked = !!error; + error = 0; *out = wt; @@ -403,20 +406,24 @@ out: int git_worktree_lock(git_worktree *wt, const char *reason) { git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; - int err; + int error; assert(wt); - if ((err = git_worktree_is_locked(NULL, wt)) < 0) + if ((error = git_worktree_is_locked(NULL, wt)) < 0) + goto out; + if (error) { + error = GIT_ELOCKED; goto out; + } - if ((err = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) + if ((error = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) goto out; if (reason) git_buf_attach_notowned(&buf, reason, strlen(reason)); - if ((err = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0) + if ((error = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0) goto out; wt->locked = 1; @@ -424,16 +431,19 @@ int git_worktree_lock(git_worktree *wt, const char *reason) out: git_buf_dispose(&path); - return err; + return error; } int git_worktree_unlock(git_worktree *wt) { git_buf path = GIT_BUF_INIT; + int error; assert(wt); - if (!git_worktree_is_locked(NULL, wt)) + if ((error = git_worktree_is_locked(NULL, wt)) < 0) + return error; + if (!error) return 1; if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0) @@ -454,22 +464,25 @@ int git_worktree_unlock(git_worktree *wt) int git_worktree_is_locked(git_buf *reason, const git_worktree *wt) { git_buf path = GIT_BUF_INIT; - int ret; + int error, locked; assert(wt); if (reason) git_buf_clear(reason); - if ((ret = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) + if ((error = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0) + goto out; + locked = git_path_exists(path.ptr); + if (locked && reason && + (error = git_futils_readbuffer(reason, path.ptr)) < 0) goto out; - if ((ret = git_path_exists(path.ptr)) && reason) - git_futils_readbuffer(reason, path.ptr); + error = locked; out: git_buf_dispose(&path); - return ret; + return error; } const char *git_worktree_name(const git_worktree *wt) @@ -502,7 +515,6 @@ int git_worktree_pruneinit_options(git_worktree_prune_options *opts, 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; GIT_ERROR_CHECK_VERSION( @@ -512,20 +524,24 @@ int git_worktree_is_prunable(git_worktree *wt, 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); - git_error_set(GIT_ERROR_WORKTREE, "not pruning locked working tree: '%s'", reason.ptr); - git_buf_dispose(&reason); + if ((popts.flags & GIT_WORKTREE_PRUNE_LOCKED) == 0) { + git_buf reason = GIT_BUF_INIT; + int error; - return 0; + if ((error = git_worktree_is_locked(&reason, wt)) < 0) + return error; + + if (error) { + if (!reason.size) + git_buf_attach_notowned(&reason, "no reason given", 15); + git_error_set(GIT_ERROR_WORKTREE, "not pruning locked working tree: '%s'", reason.ptr); + git_buf_dispose(&reason); + return 0; + } } if ((popts.flags & GIT_WORKTREE_PRUNE_VALID) == 0 && - git_worktree_validate(wt) == 0) - { + git_worktree_validate(wt) == 0) { git_error_set(GIT_ERROR_WORKTREE, "not pruning valid working tree"); return 0; } diff --git a/tests/blame/blame_helpers.c b/tests/blame/blame_helpers.c index 61e87350c..6b3ce677d 100644 --- a/tests/blame/blame_helpers.c +++ b/tests/blame/blame_helpers.c @@ -31,13 +31,13 @@ void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx, } if (hunk->final_start_line_number != start_line) { - hunk_message(idx, hunk, "mismatched start line number: expected %d, got %d", + hunk_message(idx, hunk, "mismatched start line number: expected %"PRIuZ", got %"PRIuZ, start_line, hunk->final_start_line_number); } cl_assert_equal_i(hunk->final_start_line_number, start_line); if (hunk->lines_in_hunk != len) { - hunk_message(idx, hunk, "mismatched line count: expected %d, got %d", + hunk_message(idx, hunk, "mismatched line count: expected %"PRIuZ", got %"PRIuZ, len, hunk->lines_in_hunk); } cl_assert_equal_i(hunk->lines_in_hunk, len); diff --git a/tests/blame/blame_helpers.h b/tests/blame/blame_helpers.h index fd5a35d2c..5b34b4aef 100644 --- a/tests/blame/blame_helpers.h +++ b/tests/blame/blame_helpers.h @@ -1,7 +1,7 @@ #include "clar_libgit2.h" #include "blame.h" -void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...); +void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...) GIT_FORMAT_PRINTF(3, 4); void check_blame_hunk_index( git_repository *repo, diff --git a/tests/config/global.c b/tests/config/global.c index b64b71677..ed47d0251 100644 --- a/tests/config/global.c +++ b/tests/config/global.c @@ -81,7 +81,7 @@ void test_config_global__lock_missing_global_config(void) git_config_entry *entry; git_transaction *transaction; - p_unlink("home/.gitconfig"); /* No global config */ + (void)p_unlink("home/.gitconfig"); /* No global config */ cl_git_pass(git_config_open_default(&cfg)); cl_git_pass(git_config_lock(&transaction, cfg)); diff --git a/tests/config/new.c b/tests/config/new.c index 2f5d83d52..22c330f6f 100644 --- a/tests/config/new.c +++ b/tests/config/new.c @@ -30,5 +30,5 @@ void test_config_new__write_new_config(void) git_buf_dispose(&buf); git_config_free(config); - p_unlink(TEST_CONFIG); + cl_must_pass(p_unlink(TEST_CONFIG)); } diff --git a/tests/diff/tree.c b/tests/diff/tree.c index 2359a834b..dfe4d254c 100644 --- a/tests/diff/tree.c +++ b/tests/diff/tree.c @@ -524,3 +524,52 @@ void test_diff_tree__diff_configs(void) cl_assert_equal_i(7, expect.line_adds); cl_assert_equal_i(15, expect.line_dels); } + +void test_diff_tree__diff_tree_with_empty_dir_entry_succeeds(void) +{ + const char *content = "This is a blob\n"; + const git_diff_delta *delta; + git_oid empty_tree, invalid_tree, blob; + git_buf patch = GIT_BUF_INIT; + git_treebuilder *builder; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(git_blob_create_from_buffer(&blob, g_repo, content, strlen(content))); + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); + cl_git_pass(git_treebuilder_write(&empty_tree, builder)); + cl_git_pass(git_treebuilder_insert(NULL, builder, "empty_tree", &empty_tree, GIT_FILEMODE_TREE)); + cl_git_pass(git_treebuilder_insert(NULL, builder, "blob", &blob, GIT_FILEMODE_BLOB)); + cl_git_pass(git_treebuilder_write(&invalid_tree, builder)); + + cl_git_pass(git_tree_lookup(&a, g_repo, &empty_tree)); + cl_git_pass(git_tree_lookup(&b, g_repo, &invalid_tree)); + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL)); + + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect)); + cl_assert_equal_i(1, expect.files); + cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, expect.hunks); + cl_assert_equal_i(1, expect.lines); + cl_assert_equal_i(0, expect.line_ctxt); + cl_assert_equal_i(1, expect.line_adds); + cl_assert_equal_i(0, expect.line_dels); + + cl_git_pass(git_diff_to_buf(&patch, diff, GIT_DIFF_FORMAT_PATCH)); + cl_assert_equal_s(patch.ptr, + "diff --git a/blob b/blob\n" + "new file mode 100644\n" + "index 0000000..bbf2e80\n" + "--- /dev/null\n" + "+++ b/blob\n" + "@@ -0,0 +1 @@\n" + "+This is a blob\n"); + + cl_assert_equal_i(git_diff_num_deltas(diff), 1); + delta = git_diff_get_delta(diff, 0); + cl_assert_equal_s(delta->new_file.path, "blob"); + + git_treebuilder_free(builder); + git_buf_dispose(&patch); +} diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 8b0445214..71b2e91a7 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -2160,3 +2160,46 @@ void test_diff_workdir__symlink_changed_on_non_symlink_platform(void) git_tree_free(tree); git_vector_free(&pathlist); } + +void test_diff_workdir__order(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_buf patch = GIT_BUF_INIT; + git_oid tree_oid, blob_oid; + git_treebuilder *builder; + git_tree *tree; + git_diff *diff; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + /* Build tree with a single file "abc.txt" */ + cl_git_pass(git_blob_create_from_buffer(&blob_oid, g_repo, "foo\n", 4)); + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); + cl_git_pass(git_treebuilder_insert(NULL, builder, "abc.txt", &blob_oid, GIT_FILEMODE_BLOB)); + cl_git_pass(git_treebuilder_write(&tree_oid, builder)); + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); + + /* Create a directory that sorts before and one that sorts after "abc.txt" */ + cl_git_mkfile("empty_standard_repo/abc.txt", "bar\n"); + cl_must_pass(p_mkdir("empty_standard_repo/abb", 0777)); + cl_must_pass(p_mkdir("empty_standard_repo/abd", 0777)); + + opts.flags = GIT_DIFF_INCLUDE_UNTRACKED; + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts)); + + cl_assert_equal_i(1, git_diff_num_deltas(diff)); + cl_git_pass(git_diff_to_buf(&patch, diff, GIT_DIFF_FORMAT_PATCH)); + cl_assert_equal_s(patch.ptr, + "diff --git a/abc.txt b/abc.txt\n" + "index 257cc56..5716ca5 100644\n" + "--- a/abc.txt\n" + "+++ b/abc.txt\n" + "@@ -1 +1 @@\n" + "-foo\n" + "+bar\n"); + + git_treebuilder_free(builder); + git_buf_dispose(&patch); + git_diff_free(diff); + git_tree_free(tree); +} diff --git a/tests/ignore/path.c b/tests/ignore/path.c index 864fba41b..e23ac7712 100644 --- a/tests/ignore/path.c +++ b/tests/ignore/path.c @@ -255,7 +255,7 @@ void test_ignore_path__globs_without_star(void) void test_ignore_path__skip_gitignore_directory(void) { cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder"); - p_unlink("attr/.gitignore"); + cl_must_pass(p_unlink("attr/.gitignore")); cl_assert(!git_path_exists("attr/.gitignore")); p_mkdir("attr/.gitignore", 0777); cl_git_mkfile("attr/.gitignore/garbage.txt", "new_file\n"); @@ -268,12 +268,11 @@ void test_ignore_path__skip_gitignore_directory(void) void test_ignore_path__subdirectory_gitignore(void) { - p_unlink("attr/.gitignore"); + cl_must_pass(p_unlink("attr/.gitignore")); cl_assert(!git_path_exists("attr/.gitignore")); cl_git_mkfile( "attr/.gitignore", "file1\n"); - p_mkdir("attr/dir", 0777); cl_git_mkfile( "attr/dir/.gitignore", "file2/\n"); diff --git a/tests/index/nsec.c b/tests/index/nsec.c index dee1509e1..6edcf030a 100644 --- a/tests/index/nsec.c +++ b/tests/index/nsec.c @@ -54,7 +54,7 @@ static bool should_expect_nsecs(void) expect = try_create_file_with_nsec_timestamp(nsec_path.ptr); - p_unlink(nsec_path.ptr); + cl_must_pass(p_unlink(nsec_path.ptr)); git_buf_dispose(&nsec_path); diff --git a/tests/index/tests.c b/tests/index/tests.c index 4c9deaaba..d9d8371dd 100644 --- a/tests/index/tests.c +++ b/tests/index/tests.c @@ -566,8 +566,7 @@ void test_index_tests__cannot_add_invalid_filename(void) { git_repository *repo; - p_mkdir("invalid", 0700); - + cl_must_pass(p_mkdir("invalid", 0700)); cl_git_pass(git_repository_init(&repo, "./invalid", 0)); cl_must_pass(p_mkdir("./invalid/subdir", 0777)); diff --git a/tests/network/refspecs.c b/tests/network/refspecs.c index 734759060..5c8eb1502 100644 --- a/tests/network/refspecs.c +++ b/tests/network/refspecs.c @@ -153,7 +153,7 @@ static void assert_invalid_rtransform(const char *refspec, const char *name) git_refspec spec; git_buf buf = GIT_BUF_INIT; - git_refspec__parse(&spec, refspec, true); + cl_git_pass(git_refspec__parse(&spec, refspec, true)); cl_git_fail(git_refspec_rtransform(&buf, &spec, name)); git_buf_dispose(&buf); diff --git a/tests/notes/notes.c b/tests/notes/notes.c index f2457665c..af9686790 100644 --- a/tests/notes/notes.c +++ b/tests/notes/notes.c @@ -400,15 +400,11 @@ void test_notes_notes__can_read_a_note_from_a_commit(void) git_note *note; cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045")); - cl_git_pass(git_note_commit_create(¬es_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1)); - - git_commit_lookup(¬es_commit, _repo, ¬es_commit_oid); - + cl_git_pass(git_commit_lookup(¬es_commit, _repo, ¬es_commit_oid)); cl_assert(notes_commit); cl_git_pass(git_note_commit_read(¬e, _repo, notes_commit, &oid)); - cl_assert_equal_s(git_note_message(note), "I decorate 4a20\n"); git_commit_free(notes_commit); @@ -457,7 +453,7 @@ void test_notes_notes__can_insert_a_note_in_an_existing_fanout(void) git_note *_note; cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125")); - + for (i = 0; i < MESSAGES_COUNT; i++) { cl_git_pass(git_note_create(¬e_oid, _repo, "refs/notes/fanout", _sig, _sig, &target_oid, messages[i], 0)); cl_git_pass(git_note_read(&_note, _repo, "refs/notes/fanout", &target_oid)); @@ -511,7 +507,7 @@ void test_notes_notes__can_remove_a_note_from_commit(void) cl_git_pass(git_note_commit_create(¬es_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0)); - git_commit_lookup(&existing_notes_commit, _repo, ¬es_commit_oid); + cl_git_pass(git_commit_lookup(&existing_notes_commit, _repo, ¬es_commit_oid)); cl_assert(existing_notes_commit); @@ -547,7 +543,7 @@ void test_notes_notes__removing_a_note_which_doesnt_exists_returns_ENOTFOUND(voi cl_git_pass(git_oid_fromstr(&target_oid, "8496071c1b46c854b31185ea97743be6a8774479")); cl_git_pass(git_note_remove(_repo, "refs/notes/fanout", _sig, _sig, &target_oid)); - + error = git_note_remove(_repo, "refs/notes/fanout", _sig, _sig, &target_oid); cl_git_fail(error); cl_assert_equal_i(GIT_ENOTFOUND, error); diff --git a/tests/object/cache.c b/tests/object/cache.c index 3ccc325e4..08bf03648 100644 --- a/tests/object/cache.c +++ b/tests/object/cache.c @@ -244,15 +244,15 @@ static void *cache_quick(void *arg) void test_object_cache__fast_thread_rush(void) { - int try, th, data[THREADCOUNT*2]; + int try, th, data[THREADCOUNT]; #ifdef GIT_THREADS - git_thread t[THREADCOUNT*2]; + git_thread t[THREADCOUNT]; #endif for (try = 0; try < REPEAT; ++try) { cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); - for (th = 0; th < THREADCOUNT*2; ++th) { + for (th = 0; th < THREADCOUNT; ++th) { data[th] = th; #ifdef GIT_THREADS cl_git_pass( @@ -263,7 +263,7 @@ void test_object_cache__fast_thread_rush(void) } #ifdef GIT_THREADS - for (th = 0; th < THREADCOUNT*2; ++th) { + for (th = 0; th < THREADCOUNT; ++th) { void *rval; cl_git_pass(git_thread_join(&t[th], &rval)); cl_assert_equal_i(th, *((int *)rval)); |