INCLUDE(FindPkgLibraries)
INCLUDE(FindThreads)
INCLUDE(FindStatNsec)
+INCLUDE(GNUInstallDirs)
INCLUDE(IdeSplitSources)
INCLUDE(FeatureSummary)
INCLUDE(EnableWarnings)
| 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
The following CMake variables are declared:
-- `BIN_INSTALL_DIR`: Where to install binaries to.
-- `LIB_INSTALL_DIR`: Where to install libraries to.
-- `INCLUDE_INSTALL_DIR`: Where to install headers to.
+- `CMAKE_INSTALL_BINDIR`: Where to install binaries to.
+- `CMAKE_INSTALL_LIBDIR`: Where to install libraries to.
+- `CMAKE_INSTALL_INCLUDEDIR`: Where to install headers to.
- `BUILD_SHARED_LIBS`: Build libgit2 as a Shared Library (defaults to ON)
- `BUILD_CLAR`: Build [Clar](https://github.com/vmg/clar)-based test suite (defaults to ON)
- `THREADSAFE`: Build libgit2 with threading support (defaults to ON)
* 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
- 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:
- 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:
- 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:
- 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:
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
- 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
- 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
- job: documentation
displayName: 'Generate Documentation'
pool:
- vmImage: 'Ubuntu 16.04'
+ vmImage: 'ubuntu-18.04'
steps:
- script: |
cd $(Build.SourcesDirectory)/azure-pipelines/docker
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
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 \
- job: coverity
displayName: 'Coverity'
pool:
- vmImage: 'Ubuntu 16.04'
+ vmImage: 'ubuntu-18.04'
steps:
- script: |
cd $(Build.SourcesDirectory)/azure-pipelines/docker
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 && \
-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
#!/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 "$@"
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 && \
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 . && \
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 && \
- 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:
- 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:
- 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:
- 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:
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
- 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
- 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
- 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:
- 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:
- 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:
- 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:
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)..."
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..."
# 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)
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"
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()
--- /dev/null
+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()
# 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")
# Select the backend to use
+INCLUDE(SanitizeBool)
+
# We try to find any packages our backends might use
FIND_PACKAGE(OpenSSL)
FIND_PACKAGE(mbedTLS)
ENDIF()
# Auto-select TLS backend
+SanitizeBool(USE_HTTPS)
IF (USE_HTTPS STREQUAL ON)
IF (SECURITY_FOUND)
IF (SECURITY_HAS_SSLCREATECONTEXT)
# 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")
+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
-----
*/
#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
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
* 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,
* 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,
* 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,
* 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,
* 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,
* 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);
*
* 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);
* 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);
* 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);
* 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);
* 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);
* 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);
*
* 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);
#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
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")
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)
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
# 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})
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(
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 */
}
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;
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;
}
#include "hash.h"
#include "odb.h"
#include "delta.h"
-#include "sha1_lookup.h"
#include "mwindow.h"
#include "pack.h"
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 ||
#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;
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,
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);
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) {
*
* 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);
}
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)
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);
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;
}
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);
}
/* 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. */
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);
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);
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);
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);
return 0;
git_error_set(GIT_ERROR_INVALID, "object is not a committish");
- return -1;
+ return error;
}
if (error < 0)
return error;
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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
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]);
}
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;
{ 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;
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;
}
# 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
#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;
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;
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;
#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.
*/
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,
* 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
* 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;
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;
}
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;
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;
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)
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)
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(
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;
}
}
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);
#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,
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;
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));
git_buf_dispose(&buf);
git_config_free(config);
- p_unlink(TEST_CONFIG);
+ cl_must_pass(p_unlink(TEST_CONFIG));
}
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;
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);
+}
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);
+}
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");
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");
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);
{
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));
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);
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);
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));
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);
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);
#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
#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)
{
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");
--- /dev/null
+x\ 1\9d\8fK
+\ 21\10D]ç\14ÙÍB\90$\9dO\aD\ 4Á{¤{:\8cà\18\19¢\vOï\80Ì\ 5¬Õ£àA\15·y¾uí\1cìú"¢\vRA\ f\91Â\88Ì4Ö1\90\84\9cØf/\89\bÅ\14\93]Uϲȣë\18\ 3Ôê\1dX\10
+\8c\ eM¬Ö\aÈ\96,\96\1csFc8©òêS[ô}⩼å£\8f\e\9d78p\9bOÚ\ 6\84\b.%§÷f\8dZÛua\97\7f\uùÝ\e®\83ú\ 2\92JF\80
\ No newline at end of file
--- /dev/null
+x\ 1+)JMU04¶`040031QHÔ+©(aàÙµåJü5¹Ð\85'3ëçÏÙP%ô0Ä\ 3ª ¬à½Ñ}_Õ\89Î\93ÍE\8f{\ 6þ4ºÙ©\9cr\11ª -3'\15¬æ§\9a\7fý^ÃuU\12Û'ËÝ\98\92¿aÝn®ÏP5\19¥é\105÷æ?\8e\9fÉöp¦\1f\87ÍÑ9íÊÚ_$*£\ 1Û\9\88
\ No newline at end of file
-6653ff42313eb5c82806f145391b18a9699800c7
+836bc00b06cb60eb0f629e237ad2b58adb2cfc7e