diff --git a/CMakeLists.txt b/CMakeLists.txt index 113e554d2..826415893 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgLibraries) INCLUDE(FindThreads) INCLUDE(FindStatNsec) +INCLUDE(GNUInstallDirs) INCLUDE(IdeSplitSources) INCLUDE(FeatureSummary) INCLUDE(EnableWarnings) diff --git a/README.md b/README.md index 9dafffec0..b5f04403b 100644 --- a/README.md +++ b/README.md @@ -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 @@ -248,9 +247,9 @@ For more advanced use or questions about CMake please read * Java * Jagged +* Javascript / WebAssembly ( browser and nodejs ) + * WASM-git * Julia * LibGit2.jl * Lua diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2575f475f..20335b33a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,7 +9,7 @@ jobs: - job: linux_amd64_xenial_gcc_openssl displayName: 'Linux (amd64; Xenial; GCC; OpenSSL)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: azure-pipelines/docker.yml parameters: @@ -25,7 +25,7 @@ jobs: - job: linux_amd64_xenial_gcc_mbedtls displayName: 'Linux (amd64; Xenial; GCC; mbedTLS)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: azure-pipelines/docker.yml parameters: @@ -41,7 +41,7 @@ jobs: - job: linux_amd64_xenial_clang_openssl displayName: 'Linux (amd64; Xenial; Clang; OpenSSL)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: azure-pipelines/docker.yml parameters: @@ -57,7 +57,7 @@ jobs: - job: linux_amd64_xenial_clang_mbedtls displayName: 'Linux (amd64; Xenial; Clang; mbedTLS)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: azure-pipelines/docker.yml parameters: @@ -71,9 +71,9 @@ jobs: GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }} - job: macos - displayName: 'macOS' + displayName: 'macOS (amd64; 10.15)' pool: - vmImage: 'macOS 10.13' + vmImage: 'macOS-10.15' steps: - bash: . '$(Build.SourcesDirectory)/azure-pipelines/setup-osx.sh' displayName: Setup @@ -89,31 +89,34 @@ jobs: - job: windows_vs_amd64 displayName: 'Windows (amd64; Visual Studio)' - pool: Hosted + pool: + vmImage: 'vs2017-win2016' steps: - template: azure-pipelines/bash.yml parameters: environmentVariables: - CMAKE_GENERATOR: Visual Studio 12 2013 Win64 - CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON + CMAKE_GENERATOR: Visual Studio 15 2017 + CMAKE_OPTIONS: -A x64 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - job: windows_vs_x86 displayName: 'Windows (x86; Visual Studio)' - pool: Hosted + pool: + vmImage: 'vs2017-win2016' steps: - template: azure-pipelines/bash.yml parameters: environmentVariables: - CMAKE_GENERATOR: Visual Studio 12 2013 - CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS + CMAKE_GENERATOR: Visual Studio 15 2017 + CMAKE_OPTIONS: -A Win32 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - job: windows_mingw_amd64 displayName: 'Windows (amd64; MinGW)' - pool: Hosted + pool: + vmImage: 'vs2017-win2016' steps: - bash: . '$(Build.SourcesDirectory)\azure-pipelines\setup-mingw.sh' displayName: Setup @@ -131,7 +134,8 @@ jobs: - job: windows_mingw_x86 displayName: 'Windows (x86; MinGW)' - pool: Hosted + pool: + vmImage: 'vs2017-win2016' steps: - bash: . '$(Build.SourcesDirectory)\azure-pipelines\setup-mingw.sh' displayName: Setup @@ -151,7 +155,7 @@ jobs: - job: documentation displayName: 'Generate Documentation' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - script: | cd $(Build.SourcesDirectory)/azure-pipelines/docker diff --git a/azure-pipelines/coverity.sh b/azure-pipelines/coverity.sh index 14faadc49..c68b6f8cc 100755 --- a/azure-pipelines/coverity.sh +++ b/azure-pipelines/coverity.sh @@ -25,7 +25,7 @@ TOOL_DIR=${BUILD_DIR}/coverity-tools if ! test -d "$TOOL_DIR" then mkdir -p "$TOOL_DIR" - curl --silent --location --data "project=libgit2&token=$COVERITY_TOKEN" "$SCAN_TOOL" | + curl --silent --show-error --location --data "project=libgit2&token=$COVERITY_TOKEN" "$SCAN_TOOL" | tar -xzC "$TOOL_DIR" ln -s "$(find "$TOOL_DIR" -type d -name 'cov-analysis*')" "$TOOL_DIR"/cov-analysis fi @@ -44,7 +44,7 @@ COVERITY_UNSUPPORTED=1 \ tar -czf libgit2.tgz cov-int REVISION=$(cd ${SOURCE_DIR} && git rev-parse --short HEAD) HTML="$(curl \ - --silent \ + --silent --show-error \ --write-out "\n%{http_code}" \ --form token="$COVERITY_TOKEN" \ --form email=libgit2@gmail.com \ diff --git a/azure-pipelines/coverity.yml b/azure-pipelines/coverity.yml index 10450af5d..a8747db73 100644 --- a/azure-pipelines/coverity.yml +++ b/azure-pipelines/coverity.yml @@ -5,7 +5,7 @@ jobs: - job: coverity displayName: 'Coverity' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - script: | cd $(Build.SourcesDirectory)/azure-pipelines/docker diff --git a/azure-pipelines/docker/bionic b/azure-pipelines/docker/bionic index ae9604de7..65a14063a 100644 --- a/azure-pipelines/docker/bionic +++ b/azure-pipelines/docker/bionic @@ -25,7 +25,7 @@ RUN apt-get update && \ FROM apt AS mbedtls RUN cd /tmp && \ - curl --location --silent https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ + curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ tar -xz && \ cd mbedtls-2.16.2 && \ scripts/config.pl set MBEDTLS_MD4_C 1 && \ diff --git a/azure-pipelines/docker/docurium b/azure-pipelines/docker/docurium index 15d10a71e..54a4202b6 100644 --- a/azure-pipelines/docker/docurium +++ b/azure-pipelines/docker/docurium @@ -1,6 +1,3 @@ -FROM debian:jessie-slim -ARG CACHEBUST=1 -RUN apt-get update -RUN apt install -y cmake pkg-config ruby ruby-dev llvm libclang-3.5-dev libssl-dev python-pygments -ARG CACHEBUST=1 +FROM ubuntu:bionic +RUN apt update && apt install -y cmake pkg-config ruby ruby-dev llvm libclang-dev libssl-dev python-pygments RUN gem install docurium diff --git a/azure-pipelines/docker/entrypoint.sh b/azure-pipelines/docker/entrypoint.sh index 830df49e9..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 --recursive libgit2:libgit2 /home/libgit2 -exec sudo --preserve-env --user=libgit2 "$@" +exec sudo --preserve-env --set-home --user=libgit2 "$@" diff --git a/azure-pipelines/docker/xenial b/azure-pipelines/docker/xenial index 8b2501de6..bfb96d9db 100644 --- a/azure-pipelines/docker/xenial +++ b/azure-pipelines/docker/xenial @@ -29,7 +29,7 @@ RUN apt-get update && \ FROM apt AS mbedtls RUN cd /tmp && \ - curl --location --silent https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ + curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ tar -xz && \ cd mbedtls-2.16.2 && \ scripts/config.pl set MBEDTLS_MD4_C 1 && \ @@ -40,7 +40,7 @@ RUN cd /tmp && \ FROM mbedtls AS libssh2 RUN cd /tmp && \ - curl --location --silent https://www.libssh2.org/download/libssh2-1.8.2.tar.gz | \ + curl --insecure --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.2.tar.gz | \ tar -xz && \ cd libssh2-1.8.2 && \ CFLAGS=-fPIC cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt . && \ @@ -50,7 +50,7 @@ RUN cd /tmp && \ FROM libssh2 AS valgrind RUN cd /tmp && \ - curl --location --silent https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \ + curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \ tar -xj && \ cd valgrind-3.15.0 && \ ./configure && \ diff --git a/azure-pipelines/nightly.yml b/azure-pipelines/nightly.yml index a193747b5..a75a9cc24 100644 --- a/azure-pipelines/nightly.yml +++ b/azure-pipelines/nightly.yml @@ -5,7 +5,7 @@ jobs: - job: linux_amd64_xenial_gcc_openssl displayName: 'Linux (amd64; Xenial; GCC; OpenSSL)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: docker.yml parameters: @@ -21,7 +21,7 @@ jobs: - job: linux_amd64_xenial_gcc_mbedtls displayName: 'Linux (amd64; Xenial; GCC; mbedTLS)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: docker.yml parameters: @@ -37,7 +37,7 @@ jobs: - job: linux_amd64_xenial_clang_openssl displayName: 'Linux (amd64; Xenial; Clang; OpenSSL)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: docker.yml parameters: @@ -53,7 +53,7 @@ jobs: - job: linux_amd64_xenial_clang_mbedtls displayName: 'Linux (amd64; Xenial; Clang; mbedTLS)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: docker.yml parameters: @@ -67,9 +67,9 @@ jobs: RUN_INVASIVE_TESTS=true - job: macos - displayName: 'macOS' + displayName: 'macOS (amd64; 10.15)' pool: - vmImage: 'macOS 10.13' + vmImage: 'macOS-10.15' steps: - bash: . '$(Build.SourcesDirectory)/azure-pipelines/setup-osx.sh' displayName: Setup @@ -85,31 +85,34 @@ jobs: - job: windows_vs_amd64 displayName: 'Windows (amd64; Visual Studio)' - pool: Hosted + pool: + vmImage: 'vs2017-win2016' steps: - template: bash.yml parameters: environmentVariables: - CMAKE_GENERATOR: Visual Studio 12 2013 Win64 - CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON + CMAKE_GENERATOR: Visual Studio 15 2017 + CMAKE_OPTIONS: -A x64 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON RUN_INVASIVE_TESTS: true SKIP_SSH_TESTS: true - job: windows_vs_x86 displayName: 'Windows (x86; Visual Studio)' - pool: Hosted + pool: + vmImage: 'vs2017-win2016' steps: - template: bash.yml parameters: environmentVariables: - CMAKE_GENERATOR: Visual Studio 12 2013 - CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS + CMAKE_GENERATOR: Visual Studio 15 2017 + CMAKE_OPTIONS: -A Win32 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS RUN_INVASIVE_TESTS: true SKIP_SSH_TESTS: true - job: windows_mingw_amd64 displayName: 'Windows (amd64; MinGW)' - pool: Hosted + pool: + vmImage: 'vs2017-win2016' steps: - bash: . '$(Build.SourcesDirectory)\azure-pipelines\setup-mingw.sh' displayName: Setup @@ -127,7 +130,8 @@ jobs: - job: windows_mingw_x86 displayName: 'Windows (x86; MinGW)' - pool: Hosted + pool: + vmImage: 'vs2017-win2016' steps: - bash: . '$(Build.SourcesDirectory)\azure-pipelines\setup-mingw.sh' displayName: Setup @@ -147,7 +151,7 @@ jobs: - job: linux_x86_bionic_gcc_openssl displayName: 'Linux (x86; Bionic; GCC; OpenSSL)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: docker.yml parameters: @@ -164,7 +168,7 @@ jobs: - job: linux_x86_bionic_clang_openssl displayName: 'Linux (x86; Bionic; Clang; OpenSSL)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: docker.yml parameters: @@ -181,7 +185,7 @@ jobs: - job: linux_arm32_bionic_gcc_openssl displayName: 'Linux (arm32; Bionic; GCC; OpenSSL)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: docker.yml parameters: @@ -199,7 +203,7 @@ jobs: - job: linux_arm64_bionic_gcc_openssl displayName: 'Linux (arm64; Bionic; GCC; OpenSSL)' pool: - vmImage: 'Ubuntu 16.04' + vmImage: 'ubuntu-18.04' steps: - template: docker.yml parameters: diff --git a/azure-pipelines/test.sh b/azure-pipelines/test.sh index 96832d64c..2b43ba198 100755 --- a/azure-pipelines/test.sh +++ b/azure-pipelines/test.sh @@ -85,7 +85,7 @@ if [ -z "$SKIP_GITDAEMON_TESTS" ]; then fi if [ -z "$SKIP_PROXY_TESTS" ]; then - curl --location --silent https://github.com/ethomson/poxyproxy/releases/download/v0.7.0/poxyproxy-0.7.0.jar >poxyproxy.jar + curl --location --silent --show-error https://github.com/ethomson/poxyproxy/releases/download/v0.7.0/poxyproxy-0.7.0.jar >poxyproxy.jar echo "" echo "Starting HTTP proxy (Basic)..." @@ -97,7 +97,7 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then fi if [ -z "$SKIP_NTLM_TESTS" ]; then - curl --location --silent https://github.com/ethomson/poxygit/releases/download/v0.4.0/poxygit-0.4.0.jar >poxygit.jar + curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.4.0/poxygit-0.4.0.jar >poxygit.jar echo "" echo "Starting HTTP server..." diff --git a/cmake/Modules/PkgBuildConfig.cmake b/cmake/Modules/PkgBuildConfig.cmake index 1c0079eb0..54c5e294c 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=\"${CMAKE_INSTALL_FULL_LIBDIR}\"\n" + "includedir=\"${CMAKE_INSTALL_FULL_INCLUDEDIR}\"\n" "\n" "Name: ${PKGCONFIG_NAME}\n" "Description: ${PKGCONFIG_DESCRIPTION}\n" @@ -104,7 +73,5 @@ function(pkg_build_config) file(APPEND "${PKGCONFIG_FILE}" "Cflags: -I\${includedir} ${PKGCONFIG_CFLAGS}\n") # Install .pc file - install(FILES "${PKGCONFIG_FILE}" - DESTINATION "${PKGCONFIG_PREFIX}/${PKGCONFIG_LIBDIR}/pkgconfig" - ) + install(FILES "${PKGCONFIG_FILE}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") endfunction() diff --git a/cmake/Modules/SanitizeBool.cmake b/cmake/Modules/SanitizeBool.cmake new file mode 100644 index 000000000..b5b99a690 --- /dev/null +++ b/cmake/Modules/SanitizeBool.cmake @@ -0,0 +1,20 @@ +FUNCTION(SanitizeBool VAR) + STRING(TOLOWER "${${VAR}}" VALUE) + IF(VALUE STREQUAL "on") + SET(${VAR} "ON" PARENT_SCOPE) + ELSEIF(VALUE STREQUAL "yes") + SET(${VAR} "ON" PARENT_SCOPE) + ELSEIF(VALUE STREQUAL "true") + SET(${VAR} "ON" PARENT_SCOPE) + ELSEIF(VALUE STREQUAL "1") + SET(${VAR} "ON" PARENT_SCOPE) + ELSEIF(VALUE STREQUAL "off") + SET(${VAR} "OFF" PARENT_SCOPE) + ELSEIF(VALUE STREQUAL "no") + SET(${VAR} "OFF" PARENT_SCOPE) + ELSEIF(VALUE STREQUAL "false") + SET(${VAR} "OFF" PARENT_SCOPE) + ELSEIF(VALUE STREQUAL "0") + SET(${VAR} "OFF" PARENT_SCOPE) + ENDIF() +ENDFUNCTION() diff --git a/cmake/Modules/SelectGSSAPI.cmake b/cmake/Modules/SelectGSSAPI.cmake index 857c449e7..9b2bb1fc5 100644 --- a/cmake/Modules/SelectGSSAPI.cmake +++ b/cmake/Modules/SelectGSSAPI.cmake @@ -2,12 +2,15 @@ # We try to find any packages our backends might use +INCLUDE(SanitizeBool) + FIND_PACKAGE(GSSAPI) IF (CMAKE_SYSTEM_NAME MATCHES "Darwin") INCLUDE(FindGSSFramework) ENDIF() # Auto-select GSS backend +SanitizeBool(USE_GSSAPI) IF (USE_GSSAPI STREQUAL ON) IF (GSSFRAMEWORK_FOUND) SET(GSS_BACKEND "GSS.framework") diff --git a/cmake/Modules/SelectHTTPSBackend.cmake b/cmake/Modules/SelectHTTPSBackend.cmake index c7f6b8f1d..f9b0b1cdc 100644 --- a/cmake/Modules/SelectHTTPSBackend.cmake +++ b/cmake/Modules/SelectHTTPSBackend.cmake @@ -1,5 +1,7 @@ # Select the backend to use +INCLUDE(SanitizeBool) + # We try to find any packages our backends might use FIND_PACKAGE(OpenSSL) FIND_PACKAGE(mbedTLS) @@ -9,6 +11,7 @@ IF (CMAKE_SYSTEM_NAME MATCHES "Darwin") ENDIF() # Auto-select TLS backend +SanitizeBool(USE_HTTPS) IF (USE_HTTPS STREQUAL ON) IF (SECURITY_FOUND) IF (SECURITY_HAS_SSLCREATECONTEXT) diff --git a/cmake/Modules/SelectHashes.cmake b/cmake/Modules/SelectHashes.cmake index ce28ac23c..a1339c173 100644 --- a/cmake/Modules/SelectHashes.cmake +++ b/cmake/Modules/SelectHashes.cmake @@ -1,7 +1,10 @@ # Select a hash backend +INCLUDE(SanitizeBool) + # USE_SHA1=CollisionDetection(ON)/HTTPS/Generic/OFF +SanitizeBool(USE_SHA1) IF(USE_SHA1 STREQUAL ON OR USE_SHA1 STREQUAL "CollisionDetection") SET(SHA1_BACKEND "CollisionDetection") ELSEIF(USE_SHA1 STREQUAL "HTTPS") diff --git a/docs/changelog.md b/docs/changelog.md index 2c50a6611..97cf94c6b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,54 @@ +v1.0 +---- + +This is release v1.0 "Luftschloss", which is the first stabe release of +libgit2. The API will stay compatible across all releases of the same major +version. This release includes bugfixes only and supersedes v0.99, which will +stop being maintained. Both v0.27 and v0.28 stay supported in accordance with +our release policy. + +### Changes or improvements + +- CMake was converted to make use of the GNUInstallDirs module for both our + pkgconfig and install targets in favor of our custom build options + `BIN_INSTALL_DIR`, `LIB_INSTALL_DIR` and `INCLUDE_INSTALL_DIR`. Instead, you + can now use CMakes standard variables `CMAKE_INSTALL_BINDIR`, + `CMAKE_INSTALL_LIBDIR` and `CMAKE_INSTALL_INCLUDEDIR`. + +- Some CMake build options accepted either a specific value or a boolean value + to disable the option altogether or use automatic detection. We only accepted + "ON" or "OFF", but none of the other values CMake recognizes as boolean. This + was aligned with CMake's understanding of booleans. + +- The installed pkgconfig file contained incorrect values for both `libdir` and + `includedir` variables. + +- If using pcre2 for regular expressions, then we incorrectly added "pcre2" + instead of "pcre2-8" to our pkgconfig dependencies, which was corrected. + +- Fixed building the bundled ntlmclient dependency on FreeBSD, OpenBSD and + SunOS. + +- When writing symlinks on Windows, we incorrectly handled relative symlink + targets, which was corrected. + +- When using the HTTP protocol via macOS' SecureTransport implementation, reads + could stall at the end of the session and only continue after a timeout of 60 + seconds was reached. + +- The filesystem-based reference callback didn't corectly initialize the backend + version. + +- A segmentation fault was fixed when calling `git_blame_buffer()` for files + that were modified and added to the index. + +- A backwards-incompatible change was introduced when we moved some structures + from "git2/credentials.h" into "git2/sys/credentials.h". This was fixed in the + case where you do not use hard deprecation. + +- Improved error handling in various places. + + v0.99 ----- diff --git a/include/git2/deprecated.h b/include/git2/deprecated.h index 61d0115fd..e5e56edae 100644 --- a/include/git2/deprecated.h +++ b/include/git2/deprecated.h @@ -41,6 +41,13 @@ */ #ifndef GIT_DEPRECATE_HARD +/* + * The credential structures are now opaque by default, and their + * definition has moved into the `sys/credential.h` header; include + * them here for backward compatibility. + */ +#include "sys/credential.h" + /** * @file git2/deprecated.h * @brief libgit2 deprecated functions and values diff --git a/include/git2/repository.h b/include/git2/repository.h index 45d7962b2..9ddcd3404 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -481,10 +481,11 @@ GIT_EXTERN(const char *) git_repository_path(const git_repository *repo); GIT_EXTERN(const char *) git_repository_workdir(const 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. + * Get the path of the shared common directory for this repository. + * + * If the repository is bare, it is the root directory for the repository. + * If the repository is a worktree, it is the parent repo's gitdir. + * Otherwise, it is the gitdir. * * @param repo A repository object * @return the path to the common dir diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 8e22c4f02..c31e26d95 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -64,6 +64,12 @@ struct git_refdb_backend { * Queries the refdb backend for the existence of a reference. * * A refdb implementation must provide this function. + * + * @arg exists The implementation shall set this to `0` if a ref does + * not exist, otherwise to `1`. + * @arg ref_name The reference's name that should be checked for + * existence. + * @return `0` on success, a negative error value code. */ int GIT_CALLBACK(exists)( int *exists, @@ -74,6 +80,13 @@ struct git_refdb_backend { * Queries the refdb backend for a given reference. * * A refdb implementation must provide this function. + * + * @arg out The implementation shall set this to the allocated + * reference, if it could be found, otherwise to `NULL`. + * @arg ref_name The reference's name that should be checked for + * existence. + * @return `0` on success, `GIT_ENOTFOUND` if the reference does + * exist, otherwise a negative error code. */ int GIT_CALLBACK(lookup)( git_reference **out, @@ -84,6 +97,16 @@ struct git_refdb_backend { * Allocate an iterator object for the backend. * * A refdb implementation must provide this function. + * + * @arg out The implementation shall set this to the allocated + * reference iterator. A custom structure may be used with an + * embedded `git_reference_iterator` structure. Both `next` + * and `next_name` functions of `git_reference_iterator` need + * to be populated. + * @arg glob A pattern to filter references by. If given, the iterator + * shall only return references that match the glob when + * passed to `wildmatch`. + * @return `0` on success, otherwise a negative error code. */ int GIT_CALLBACK(iterator)( git_reference_iterator **iter, @@ -94,6 +117,27 @@ struct git_refdb_backend { * Writes the given reference to the refdb. * * A refdb implementation must provide this function. + * + * @arg ref The reference to persist. May either be a symbolic or + * direct reference. + * @arg force Whether to write the reference if a reference with the + * same name already exists. + * @arg who The person updating the reference. Shall be used to create + * a reflog entry. + * @arg message The message detailing what kind of reference update is + * performed. Shall be used to create a reflog entry. + * @arg old If not `NULL` and `force` is not set, then the + * implementation needs to ensure that the reference is currently at + * the given OID before writing the new value. If both `old` + * and `old_target` are `NULL`, then the reference should not + * exist at the point of writing. + * @arg old_target If not `NULL` and `force` is not set, then the + * implementation needs to ensure that the symbolic + * reference is currently at the given target before + * writing the new value. If both `old` and + * `old_target` are `NULL`, then the reference should + * not exist at the point of writing. + * @return `0` on success, otherwise a negative error code. */ int GIT_CALLBACK(write)(git_refdb_backend *backend, const git_reference *ref, int force, @@ -104,6 +148,18 @@ struct git_refdb_backend { * Rename a reference in the refdb. * * A refdb implementation must provide this function. + * + * @arg out The implementation shall set this to the newly created + * reference or `NULL` on error. + * @arg old_name The current name of the reference that is to be renamed. + * @arg new_name The new name that the old reference shall be renamed to. + * @arg force Whether to write the reference if a reference with the + * target name already exists. + * @arg who The person updating the reference. Shall be used to create + * a reflog entry. + * @arg message The message detailing what kind of reference update is + * performed. Shall be used to create a reflog entry. + * @return `0` on success, otherwise a negative error code. */ int GIT_CALLBACK(rename)( git_reference **out, git_refdb_backend *backend, @@ -116,6 +172,16 @@ struct git_refdb_backend { * If it exists, its reflog should be deleted as well. * * A refdb implementation must provide this function. + * + * @arg ref_name The name of the reference name that shall be deleted. + * @arg old_id If not `NULL` and `force` is not set, then the + * implementation needs to ensure that the reference is currently at + * the given OID before writing the new value. + * @arg old_target If not `NULL` and `force` is not set, then the + * implementation needs to ensure that the symbolic + * reference is currently at the given target before + * writing the new value. + * @return `0` on success, otherwise a negative error code. */ int GIT_CALLBACK(del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target); @@ -127,13 +193,21 @@ struct git_refdb_backend { * * A refdb implementation may provide this function; if it is not * provided, nothing will be done. + * + * @return `0` on success a negative error code otherwise */ int GIT_CALLBACK(compress)(git_refdb_backend *backend); /** * Query whether a particular reference has a log (may be empty) * + * Shall return 1 if it has a reflog, 0 it it doesn't and negative in + * case an error occurred. + * * A refdb implementation must provide this function. + * + * @return `0` on success, `1` if the reflog for the given reference + * exists, a negative error code otherwise */ int GIT_CALLBACK(has_log)(git_refdb_backend *backend, const char *refname); @@ -142,6 +216,8 @@ struct git_refdb_backend { * will be appended to on writes. * * A refdb implementation must provide this function. + * + * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(ensure_log)(git_refdb_backend *backend, const char *refname); @@ -157,6 +233,8 @@ struct git_refdb_backend { * Read the reflog for the given reference name. * * A refdb implementation must provide this function. + * + * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(reflog_read)(git_reflog **out, git_refdb_backend *backend, const char *name); @@ -164,6 +242,11 @@ struct git_refdb_backend { * Write a reflog to disk. * * A refdb implementation must provide this function. + * + * @arg reflog The complete reference log for a given reference. Note + * that this may contain entries that have already been + * written to disk. + * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(reflog_write)(git_refdb_backend *backend, git_reflog *reflog); @@ -171,6 +254,10 @@ struct git_refdb_backend { * Rename a reflog. * * A refdb implementation must provide this function. + * + * @arg old_name The name of old reference whose reflog shall be renamed from. + * @arg new_name The name of new reference whose reflog shall be renamed to. + * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(reflog_rename)(git_refdb_backend *_backend, const char *old_name, const char *new_name); @@ -178,16 +265,22 @@ struct git_refdb_backend { * Remove a reflog. * * A refdb implementation must provide this function. + * + * @arg name The name of the reference whose reflog shall be deleted. + * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(reflog_delete)(git_refdb_backend *backend, const char *name); /** * Lock a reference. * - * The opaque parameter will be passed to the unlock function. - * * A refdb implementation may provide this function; if it is not * provided, the transaction API will fail to work. + * + * @arg payload_out Opaque parameter that will be passed verbosely to + * `unlock`. + * @arg refname Reference that shall be locked. + * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(lock)(void **payload_out, git_refdb_backend *backend, const char *refname); @@ -200,6 +293,20 @@ struct git_refdb_backend { * * A refdb implementation must provide this function if a `lock` * implementation is provided. + * + * @arg payload The payload returned by `lock`. + * @arg success `1` if a reference should be updated, `2` if + * a reference should be deleted, `0` if the lock must be + * discarded. + * @arg update_reflog `1` in case the reflog should be updated, `0` + * otherwise. + * @arg ref The reference which should be unlocked. + * @arg who The person updating the reference. Shall be used to create + * a reflog entry in case `update_reflog` is set. + * @arg message The message detailing what kind of reference update is + * performed. Shall be used to create a reflog entry in + * case `update_reflog` is set. + * @return `0` on success, a negative error code otherwise */ int GIT_CALLBACK(unlock)(git_refdb_backend *backend, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message); diff --git a/include/git2/version.h b/include/git2/version.h index 4a824ffea..b078e1ea2 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.99.0" -#define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 99 +#define LIBGIT2_VERSION "1.0.0" +#define LIBGIT2_VER_MAJOR 1 +#define LIBGIT2_VER_MINOR 0 #define LIBGIT2_VER_REVISION 0 #define LIBGIT2_VER_PATCH 0 -#define LIBGIT2_SOVERSION "0.99" +#define LIBGIT2_SOVERSION "1.0" #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcc6a0139..dff1d94e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,14 +21,8 @@ SET(LIBGIT2_INCLUDES SET(LIBGIT2_SYSTEM_INCLUDES "") SET(LIBGIT2_LIBS "") -# Installation paths -# -SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.") -SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.") -SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.") - # Enable tracing -IF (ENABLE_TRACE STREQUAL "ON") +IF(ENABLE_TRACE) SET(GIT_TRACE 1) ENDIF() ADD_FEATURE_INFO(tracing GIT_TRACE "tracing support") @@ -140,7 +134,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) @@ -300,6 +294,10 @@ FILE(GLOB SRC_GIT2 *.c *.h streams/*.c streams/*.h transports/*.c transports/*.h xdiff/*.c xdiff/*.h) +IF(APPLE) + # The old Secure Transport API has been deprecated in macOS 10.15. + SET_SOURCE_FILES_PROPERTIES(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated) +ENDIF() # the xdiff dependency is not (yet) warning-free, disable warnings as # errors for the xdiff sources until we've sorted them out @@ -387,9 +385,9 @@ ENDIF () # Install INSTALL(TARGETS git2 - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) -INSTALL(DIRECTORY ${libgit2_SOURCE_DIR}/include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} ) -INSTALL(FILES ${libgit2_SOURCE_DIR}/include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} ) +INSTALL(DIRECTORY ${libgit2_SOURCE_DIR}/include/git2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +INSTALL(FILES ${libgit2_SOURCE_DIR}/include/git2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/src/blame.c b/src/blame.c index 404f1f643..23c21027a 100644 --- a/src/blame.c +++ b/src/blame.c @@ -415,7 +415,7 @@ on_error: static bool hunk_is_bufferblame(git_blame_hunk *hunk) { - return git_oid_is_zero(&hunk->final_commit_id); + return hunk && git_oid_is_zero(&hunk->final_commit_id); } static int buffer_hunk_cb( 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/refdb_fs.c b/src/refdb_fs.c index a721f9841..1e53b3af5 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -2129,6 +2129,9 @@ int git_refdb_backend_fs( backend = git__calloc(1, sizeof(refdb_fs_backend)); GIT_ERROR_CHECK_ALLOC(backend); + if (git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION) < 0) + goto fail; + backend->repo = repository; if (repository->gitdir) { 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); + 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 - -#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 - -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 458810c91..5b66352ca 100644 --- a/src/streams/openssl.c +++ b/src/streams/openssl.c @@ -156,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]); } 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..bde67ca9f 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; @@ -585,7 +596,7 @@ static int apply_credentials( if (auth->connection_affinity) free_auth_context(server); } else if (!token.size) { - git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challange"); + git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challenge"); error = -1; goto done; } 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/win32/path_w32.c b/src/win32/path_w32.c index eda85abf4..18b43e728 100644 --- a/src/win32/path_w32.c +++ b/src/win32/path_w32.c @@ -25,6 +25,9 @@ #define path__is_unc(p) \ (((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/')) +#define path__startswith_slash(p) \ + ((p)[0] == '\\' || (p)[0] == '/') + GIT_INLINE(int) path__cwd(wchar_t *path, int size) { int len; @@ -221,7 +224,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src) goto on_error; } /* Absolute paths omitting the drive letter */ - else if (src[0] == '\\' || src[0] == '/') { + else if (path__startswith_slash(src)) { if (path__cwd(dest, MAX_PATH) < 0) goto on_error; @@ -257,6 +260,30 @@ on_error: return -1; } +int git_win32_path_relative_from_utf8(git_win32_path out, const char *src) +{ + wchar_t *dest = out, *p; + int len; + + /* Handle absolute paths */ + if (git_path_is_absolute(src) || + path__is_nt_namespace(src) || + path__is_unc(src) || + path__startswith_slash(src)) { + return git_win32_path_from_utf8(out, src); + } + + if ((len = git__utf8_to_16(dest, MAX_PATH, src)) < 0) + return -1; + + for (p = dest; p < (dest + len); p++) { + if (*p == L'/') + *p = L'\\'; + } + + return len; +} + int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src) { char *out = dest; diff --git a/src/win32/path_w32.h b/src/win32/path_w32.h index afd0aa155..dab8b96fa 100644 --- a/src/win32/path_w32.h +++ b/src/win32/path_w32.h @@ -11,7 +11,9 @@ #include "vector.h" /** - * Create a Win32 path (in UCS-2 format) from a UTF-8 string. + * Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given + * path is relative, then it will be turned into an absolute path by having + * the current working directory prepended. * * @param dest The buffer to receive the wide string. * @param src The UTF-8 string to convert. @@ -19,6 +21,16 @@ */ extern int git_win32_path_from_utf8(git_win32_path dest, const char *src); +/** + * Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given + * path is relative, then it will not be turned into an absolute path. + * + * @param dest The buffer to receive the wide string. + * @param src The UTF-8 string to convert. + * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure + */ +extern int git_win32_path_relative_from_utf8(git_win32_path dest, const char *src); + /** * Canonicalize a Win32 UCS-2 path so that it is suitable for delivery to the * Win32 APIs: remove multiple directory separators, squashing to a single one, @@ -26,6 +38,9 @@ extern int git_win32_path_from_utf8(git_win32_path dest, const char *src); * canonical (always backslashes, never forward slashes) and process any * directory entries of '.' or '..'. * + * Note that this is intended to be used on absolute Windows paths, those + * that start with `C:\`, `\\server\share`, `\\?\`, etc. + * * This processes the buffer in place. * * @param path The buffer to process diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 29641bdaf..cacf986e8 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -447,8 +447,7 @@ int p_symlink(const char *target, const char *path) * relative symlinks, this is not someting we want. */ if (git_win32_path_from_utf8(path_w, path) < 0 || - git__utf8_to_16(target_w, MAX_PATH, target) < 0 || - git_win32_path_canonicalize(target_w) < 0) + git_win32_path_relative_from_utf8(target_w, target) < 0) return -1; dwFlags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; 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/blame/buffer.c b/tests/blame/buffer.c index 340b1dced..06d5042dd 100644 --- a/tests/blame/buffer.c +++ b/tests/blame/buffer.c @@ -17,6 +17,32 @@ void test_blame_buffer__cleanup(void) git_repository_free(g_repo); } +void test_blame_buffer__index(void) +{ + const git_blame_hunk *hunk; + const char *buffer = "Hello\nWorld!"; + + /* + * We need to open a different file from the ones used in other tests. Close + * the one opened in test_blame_buffer__initialize() to avoid a leak. + */ + git_blame_free(g_fileblame); + g_fileblame = NULL; + cl_git_pass(git_blame_file(&g_fileblame, g_repo, "file.txt", NULL)); + + cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer))); + cl_assert_equal_i(2, git_blame_get_hunk_count(g_bufferblame)); + + check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 1, 0, "836bc00b", "file.txt"); + hunk = git_blame_get_hunk_byline(g_bufferblame, 1); + cl_assert(hunk); + cl_assert_equal_s("lhchavez", hunk->final_signature->name); + check_blame_hunk_index(g_repo, g_bufferblame, 1, 2, 1, 0, "00000000", "file.txt"); + hunk = git_blame_get_hunk_byline(g_bufferblame, 2); + cl_assert(hunk); + cl_assert(hunk->final_signature == NULL); +} + void test_blame_buffer__added_line(void) { const git_blame_hunk *hunk; 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/core/posix.c b/tests/core/posix.c index 764ca1942..1bb1e9c6b 100644 --- a/tests/core/posix.c +++ b/tests/core/posix.c @@ -190,6 +190,26 @@ void test_core_posix__symlink_resolves_to_correct_type(void) git_buf_dispose(&contents); } +void test_core_posix__relative_symlink(void) +{ + git_buf contents = GIT_BUF_INIT; + + if (!git_path_supports_symlinks(clar_sandbox_path())) + clar__skip(); + + cl_must_pass(git_futils_mkdir("dir", 0777, 0)); + cl_git_mkfile("file", "contents"); + cl_git_pass(p_symlink("../file", "dir/link")); + cl_git_pass(git_futils_readbuffer(&contents, "dir/link")); + cl_assert_equal_s(contents.ptr, "contents"); + + cl_must_pass(p_unlink("file")); + cl_must_pass(p_unlink("dir/link")); + cl_must_pass(p_rmdir("dir")); + + git_buf_dispose(&contents); +} + void test_core_posix__symlink_to_file_across_dirs(void) { git_buf contents = GIT_BUF_INIT; 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/path/win32.c b/tests/path/win32.c index 3ed7d7a6a..b416e9e60 100644 --- a/tests/path/win32.c +++ b/tests/path/win32.c @@ -21,6 +21,21 @@ void test_utf8_to_utf16(const char *utf8_in, const wchar_t *utf16_expected) #endif } +void test_utf8_to_utf16_relative(const char* utf8_in, const wchar_t* utf16_expected) +{ +#ifdef GIT_WIN32 + git_win32_path path_utf16; + int path_utf16len; + + cl_assert((path_utf16len = git_win32_path_relative_from_utf8(path_utf16, utf8_in)) >= 0); + cl_assert_equal_wcs(utf16_expected, path_utf16); + cl_assert_equal_i(wcslen(utf16_expected), path_utf16len); +#else + GIT_UNUSED(utf8_in); + GIT_UNUSED(utf16_expected); +#endif +} + void test_path_win32__utf8_to_utf16(void) { #ifdef GIT_WIN32 @@ -129,6 +144,31 @@ void test_path_win32__absolute_from_relative(void) #endif } +void test_path_win32__keeps_relative(void) +{ +#ifdef GIT_WIN32 + /* Relative paths stay relative */ + test_utf8_to_utf16_relative("Foo", L"Foo"); + test_utf8_to_utf16_relative("..\\..\\Foo", L"..\\..\\Foo"); + test_utf8_to_utf16_relative("Foo\\..", L"Foo\\.."); + test_utf8_to_utf16_relative("Foo\\..\\..", L"Foo\\..\\.."); + test_utf8_to_utf16_relative("Foo\\Bar", L"Foo\\Bar"); + test_utf8_to_utf16_relative("Foo\\..\\Bar", L"Foo\\..\\Bar"); + test_utf8_to_utf16_relative("../../Foo", L"..\\..\\Foo"); + test_utf8_to_utf16_relative("Foo/..", L"Foo\\.."); + test_utf8_to_utf16_relative("Foo/../..", L"Foo\\..\\.."); + test_utf8_to_utf16_relative("Foo/Bar", L"Foo\\Bar"); + test_utf8_to_utf16_relative("Foo/../Bar", L"Foo\\..\\Bar"); + test_utf8_to_utf16_relative("Foo/../Bar/", L"Foo\\..\\Bar\\"); + test_utf8_to_utf16_relative("", L""); + + /* Absolute paths are canonicalized */ + test_utf8_to_utf16_relative("\\Foo", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16_relative("/Foo/Bar/", L"\\\\?\\C:\\Foo\\Bar"); + test_utf8_to_utf16_relative("\\\\server\\c$\\unc\\path", L"\\\\?\\UNC\\server\\c$\\unc\\path"); +#endif +} + #ifdef GIT_WIN32 static void test_canonicalize(const wchar_t *in, const wchar_t *expected) { @@ -203,16 +243,6 @@ void test_path_win32__canonicalize(void) test_canonicalize(L"C:/Foo/Bar", L"C:\\Foo\\Bar"); test_canonicalize(L"C:/", L"C:\\"); - test_canonicalize(L"Foo\\\\Bar\\\\Asdf\\\\", L"Foo\\Bar\\Asdf"); - test_canonicalize(L"Foo\\\\Bar\\\\..\\\\Asdf\\", L"Foo\\Asdf"); - test_canonicalize(L"Foo\\\\Bar\\\\.\\\\Asdf\\", L"Foo\\Bar\\Asdf"); - test_canonicalize(L"Foo\\\\..\\Bar\\\\.\\\\Asdf\\", L"Bar\\Asdf"); - test_canonicalize(L"\\", L""); - test_canonicalize(L"", L""); - test_canonicalize(L"Foo\\..\\..\\..\\..", L""); - test_canonicalize(L"..\\..\\..\\..", L""); - test_canonicalize(L"\\..\\..\\..\\..", L""); - test_canonicalize(L"\\\\?\\C:\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar"); test_canonicalize(L"\\\\?\\C:\\Foo\\Bar\\", L"\\\\?\\C:\\Foo\\Bar"); test_canonicalize(L"\\\\?\\C:\\\\Foo\\.\\Bar\\\\..\\", L"\\\\?\\C:\\Foo"); diff --git a/tests/resources/blametest.git/objects/83/6bc00b06cb60eb0f629e237ad2b58adb2cfc7e b/tests/resources/blametest.git/objects/83/6bc00b06cb60eb0f629e237ad2b58adb2cfc7e new file mode 100644 index 000000000..71f9c980e Binary files /dev/null and b/tests/resources/blametest.git/objects/83/6bc00b06cb60eb0f629e237ad2b58adb2cfc7e differ diff --git a/tests/resources/blametest.git/objects/a8/ba8436b5d8ccbdfd5be597c194e7bb8e0a092f b/tests/resources/blametest.git/objects/a8/ba8436b5d8ccbdfd5be597c194e7bb8e0a092f new file mode 100644 index 000000000..0bab0ef53 Binary files /dev/null and b/tests/resources/blametest.git/objects/a8/ba8436b5d8ccbdfd5be597c194e7bb8e0a092f differ diff --git a/tests/resources/blametest.git/objects/f9/264f7fbd31ae7a18b7931ed8946fb0aebb0af3 b/tests/resources/blametest.git/objects/f9/264f7fbd31ae7a18b7931ed8946fb0aebb0af3 new file mode 100644 index 000000000..942a7eedc Binary files /dev/null and b/tests/resources/blametest.git/objects/f9/264f7fbd31ae7a18b7931ed8946fb0aebb0af3 differ diff --git a/tests/resources/blametest.git/refs/heads/master b/tests/resources/blametest.git/refs/heads/master index d1bc4ca6b..994877a20 100644 Binary files a/tests/resources/blametest.git/refs/heads/master and b/tests/resources/blametest.git/refs/heads/master differ