diff --git a/.github/actions/run-build/action.yml b/.github/actions/run-build/action.yml
index bd0f59c9a88..9afcfb11e72 100644
--- a/.github/actions/run-build/action.yml
+++ b/.github/actions/run-build/action.yml
@@ -40,6 +40,7 @@ runs:
-e PKG_CONFIG_PATH \
-e SKIP_NEGOTIATE_TESTS \
-e SKIP_SSH_TESTS \
+ -e SKIP_PUSHOPTIONS_TESTS \
-e TSAN_OPTIONS \
-e UBSAN_OPTIONS \
${{ inputs.container-version }} \
diff --git a/.github/release.yml b/.github/release.yml
index 099e3803fa6..4d4e31860c2 100644
--- a/.github/release.yml
+++ b/.github/release.yml
@@ -27,6 +27,9 @@ changelog:
- title: Git compatibility fixes
labels:
- git compatibility
+ - title: Dependency updates
+ labels:
+ - dependency
- title: Other changes
labels:
- '*'
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 831ffbb8235..6ee492ac443 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -54,7 +54,7 @@ jobs:
runs-on: ${{ matrix.platform.os }}
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
@@ -79,7 +79,7 @@ jobs:
../source/tests/benchmarks/benchmark.sh --baseline-cli "git" --cli "${GIT2_CLI}" --name libgit2 --json benchmarks.json --zip benchmarks.zip
shell: bash
- name: Upload results
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: benchmark-${{ matrix.platform.id }}
path: benchmark
@@ -89,18 +89,18 @@ jobs:
publish:
name: Publish results
needs: [ build ]
- if: always()
+ if: ${{ always() && github.repository == 'libgit2/libgit2' }}
runs-on: ubuntu-latest
steps:
- name: Check out benchmark repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
repository: libgit2/benchmarks
path: site
fetch-depth: 0
ssh-key: ${{ secrets.BENCHMARKS_PUBLISH_KEY }}
- name: Download test results
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
- name: Publish API
run: |
# Move today's benchmark run into the right place
diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml
index 84bc4da0e64..b52571c1811 100644
--- a/.github/workflows/build-containers.yml
+++ b/.github/workflows/build-containers.yml
@@ -45,7 +45,7 @@ jobs:
name: "Create container: ${{ matrix.container.name }}"
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
diff --git a/.github/workflows/experimental.yml b/.github/workflows/experimental.yml
new file mode 100644
index 00000000000..5bfea2c0028
--- /dev/null
+++ b/.github/workflows/experimental.yml
@@ -0,0 +1,118 @@
+# Validation builds for experimental features; these shouldn't be
+# required for pull request approval.
+name: Experimental Features
+
+on:
+ push:
+ branches: [ main, maint/* ]
+ pull_request:
+ branches: [ main, maint/* ]
+ workflow_dispatch:
+
+env:
+ docker-registry: ghcr.io
+ docker-config-path: ci/docker
+
+permissions:
+ contents: write
+ packages: write
+
+jobs:
+ # Run our CI/CD builds. We build a matrix with the various build targets
+ # and their details. Then we build either in a docker container (Linux)
+ # or on the actual hosts (macOS, Windows).
+ build:
+ strategy:
+ matrix:
+ platform:
+ # All builds: experimental SHA256 support
+ - name: "Linux (SHA256, Xenial, Clang, OpenSSL)"
+ id: linux-sha256
+ os: ubuntu-latest
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DEXPERIMENTAL_SHA256=ON
+ - name: "macOS (SHA256)"
+ id: macos-sha256
+ os: macos-12
+ setup-script: osx
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON -DEXPERIMENTAL_SHA256=ON
+ CMAKE_GENERATOR: Ninja
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (SHA256, amd64, Visual Studio)"
+ id: windows-sha256
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DEXPERIMENTAL_SHA256=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ fail-fast: false
+ env: ${{ matrix.platform.env }}
+ runs-on: ${{ matrix.platform.os }}
+ name: "Build: ${{ matrix.platform.name }}"
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Set up build environment
+ run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh
+ shell: bash
+ if: matrix.platform.setup-script != ''
+ - name: Setup QEMU
+ run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
+ if: matrix.platform.container.qemu == true
+ - name: Set up container
+ uses: ./source/.github/actions/download-or-build-container
+ with:
+ registry: ${{ env.docker-registry }}
+ config-path: ${{ env.docker-config-path }}
+ container: ${{ matrix.platform.container.name }}
+ github_token: ${{ secrets.github_token }}
+ dockerfile: ${{ matrix.platform.container.dockerfile }}
+ if: matrix.platform.container.name != ''
+ - name: Prepare build
+ run: mkdir build
+ - name: Build
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd ${BUILD_WORKSPACE:-.}/build && ../source/ci/build.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ shell: ${{ matrix.platform.shell }}
+ - name: Test
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd ${BUILD_WORKSPACE:-.}/build && ../source/ci/test.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ shell: ${{ matrix.platform.shell }}
+ - name: Upload test results
+ uses: actions/upload-artifact@v4
+ if: success() || failure()
+ with:
+ name: test-results-${{ matrix.platform.id }}
+ path: build/results_*.xml
+
+ test_results:
+ name: Test results
+ needs: [ build ]
+ if: always()
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download test results
+ uses: actions/download-artifact@v3
+ - name: Generate test summary
+ uses: test-summary/action@v2
+ with:
+ paths: 'test-results-*/*.xml'
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index accb042e463..87e834f10db 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -65,6 +65,7 @@ jobs:
- name: "macOS"
id: macos
os: macos-12
+ setup-script: osx
env:
CC: clang
CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
@@ -72,7 +73,6 @@ jobs:
PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- setup-script: osx
- name: "Windows (amd64, Visual Studio, Schannel)"
id: windows-amd64-vs
os: windows-2019
@@ -125,6 +125,8 @@ jobs:
# All builds: sanitizers
- name: "Sanitizer (Memory)"
id: sanitizer-memory
+ os: ubuntu-latest
+ setup-script: sanitizer
container:
name: noble
env:
@@ -136,9 +138,10 @@ jobs:
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
UBSAN_OPTIONS: print_stacktrace=1
- os: ubuntu-latest
- name: "Sanitizer (Address)"
id: sanitizer-address
+ os: ubuntu-latest
+ setup-script: sanitizer
container:
name: noble
env:
@@ -150,10 +153,10 @@ jobs:
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
UBSAN_OPTIONS: print_stacktrace=1
- os: ubuntu-latest
- name: "Sanitizer (UndefinedBehavior)"
id: sanitizer-ub
os: ubuntu-latest
+ setup-script: sanitizer
container:
name: noble
env:
@@ -168,6 +171,7 @@ jobs:
- name: "Sanitizer (Thread)"
id: sanitizer-thread
os: ubuntu-latest
+ setup-script: sanitizer
container:
name: noble
env:
@@ -180,44 +184,13 @@ jobs:
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
UBSAN_OPTIONS: print_stacktrace=1
TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
-
- # All builds: experimental SHA256 support
- - name: "Linux (SHA256, Xenial, Clang, OpenSSL)"
- id: xenial-clang-openssl
- os: ubuntu-latest
- container:
- name: xenial
- env:
- CC: clang
- CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DEXPERIMENTAL_SHA256=ON
- - name: "macOS (SHA256)"
- id: macos
- os: macos-12
- setup-script: osx
- env:
- CC: clang
- CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON -DEXPERIMENTAL_SHA256=ON
- CMAKE_GENERATOR: Ninja
- PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
- - name: "Windows (SHA256, amd64, Visual Studio)"
- id: windows-amd64-vs
- os: windows-2019
- env:
- ARCH: amd64
- CMAKE_GENERATOR: Visual Studio 16 2019
- CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DEXPERIMENTAL_SHA256=ON
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
fail-fast: false
env: ${{ matrix.platform.env }}
runs-on: ${{ matrix.platform.os }}
name: "Build: ${{ matrix.platform.name }}"
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
@@ -254,7 +227,7 @@ jobs:
container-version: ${{ env.docker-registry-container-sha }}
shell: ${{ matrix.platform.shell }}
- name: Upload test results
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: success() || failure()
with:
name: test-results-${{ matrix.platform.id }}
@@ -285,7 +258,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
@@ -312,7 +285,7 @@ jobs:
cm doc api.docurium
git checkout gh-pages
zip --exclude .git/\* --exclude .gitignore --exclude .gitattributes -r api-documentation.zip .
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload artifact
with:
name: api-documentation
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 593fd1c47ba..1369d73c6e8 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -66,6 +66,7 @@ jobs:
- name: "macOS"
id: macos
os: macos-12
+ setup-script: osx
env:
CC: clang
CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
@@ -73,7 +74,6 @@ jobs:
PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- setup-script: osx
- name: "Windows (amd64, Visual Studio, Schannel)"
id: windows-amd64-vs
os: windows-2019
@@ -126,6 +126,8 @@ jobs:
# All builds: sanitizers
- name: "Sanitizer (Memory)"
id: memorysanitizer
+ os: ubuntu-latest
+ setup-script: sanitizer
container:
name: noble
env:
@@ -137,10 +139,10 @@ jobs:
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
UBSAN_OPTIONS: print_stacktrace=1
- os: ubuntu-latest
- name: "Sanitizer (UndefinedBehavior)"
id: ubsanitizer
os: ubuntu-latest
+ setup-script: sanitizer
container:
name: noble
env:
@@ -155,6 +157,7 @@ jobs:
- name: "Sanitizer (Thread)"
id: threadsanitizer
os: ubuntu-latest
+ setup-script: sanitizer
container:
name: noble
env:
@@ -178,6 +181,7 @@ jobs:
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
SKIP_NEGOTIATE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
- name: "Linux (CentOS 7, dynamically-loaded OpenSSL)"
id: centos7-dynamicopenssl
os: ubuntu-latest
@@ -187,6 +191,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
SKIP_NEGOTIATE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
- name: "Linux (CentOS 8, OpenSSL)"
id: centos8-openssl
os: ubuntu-latest
@@ -218,6 +223,7 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=pcre2 -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_NTLMCLIENT=OFF -DUSE_SSH=libssh2 -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
- name: "Linux (Bionic, GCC, dynamically-loaded OpenSSL)"
+ id: bionic-gcc-dynamicopenssl
container:
name: bionic
dockerfile: bionic
@@ -226,8 +232,10 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (x86, Bionic, Clang, OpenSSL)"
+ id: bionic-x86-clang-openssl
container:
name: bionic-x86
dockerfile: bionic
@@ -237,8 +245,10 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (x86, Bionic, GCC, OpenSSL)"
+ id: bionic-x86-gcc-openssl
container:
name: bionic-x86
dockerfile: bionic
@@ -247,8 +257,10 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (arm32, Bionic, GCC, OpenSSL)"
+ id: bionic-arm32-gcc-openssl
container:
name: bionic-arm32
dockerfile: bionic
@@ -259,9 +271,11 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PROXY_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
GITTEST_FLAKY_STAT: true
os: ubuntu-latest
- name: "Linux (arm64, Bionic, GCC, OpenSSL)"
+ id: bionic-arm64-gcc-openssl
container:
name: bionic-arm64
dockerfile: bionic
@@ -272,6 +286,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PROXY_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
# Nightly builds: ensure we fallback when missing core functionality
@@ -284,6 +299,7 @@ jobs:
CC: gcc
CMAKE_OPTIONS: -DTHREADSAFE=OFF -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
+ SKIP_PUSHOPTIONS_TESTS: true
- name: "Linux (no mmap)"
id: noble-nommap
os: ubuntu-latest
@@ -297,6 +313,7 @@ jobs:
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (no mmap)"
+ id: windows-nommap
os: windows-2019
env:
ARCH: amd64
@@ -319,7 +336,7 @@ jobs:
# All builds: experimental SHA256 support
- name: "Linux (SHA256, Xenial, Clang, OpenSSL)"
- id: xenial-clang-openssl
+ id: linux-sha256
container:
name: xenial
env:
@@ -328,17 +345,17 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
os: ubuntu-latest
- name: "macOS (SHA256)"
- id: macos
+ id: macos-sha256
os: macos-12
+ setup-script: osx
env:
CC: clang
CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON -DEXPERIMENTAL_SHA256=ON
PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- setup-script: osx
- name: "Windows (SHA256, amd64, Visual Studio)"
- id: windows-amd64-vs
+ id: windows-sha256
os: windows-2019
env:
ARCH: amd64
@@ -352,7 +369,7 @@ jobs:
name: "Build ${{ matrix.platform.name }}"
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
@@ -389,7 +406,7 @@ jobs:
container-version: ${{ env.docker-registry-container-sha }}
shell: ${{ matrix.platform.shell }}
- name: Upload test results
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: success() || failure()
with:
name: test-results-${{ matrix.platform.id }}
@@ -398,7 +415,7 @@ jobs:
test_results:
name: Test results
needs: [ build ]
- if: always()
+ if: ${{ always() && github.repository == 'libgit2/libgit2' }}
runs-on: ubuntu-latest
steps:
- name: Download test results
@@ -417,7 +434,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
@@ -448,7 +465,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
diff --git a/COPYING b/COPYING
index 32c851f68da..701792e9acb 100644
--- a/COPYING
+++ b/COPYING
@@ -1217,6 +1217,175 @@ SUCH DAMAGE.
----------------------------------------------------------------------
+The bundled ntlmclient code is licensed under the MIT license:
+
+Copyright (c) Edward Thomson. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+----------------------------------------------------------------------
+
+Portions of this software derived from Team Explorer Everywhere:
+
+Copyright (c) Microsoft Corporation
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+---------------------------------------------------------------------------
+
+Portions of this software derived from the LLVM Compiler Infrastructure:
+
+Copyright (c) 2003-2016 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois at
+ Urbana-Champaign, nor the names of its contributors may be used to
+ endorse or promote products derived from this Software without specific
+ prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+---------------------------------------------------------------------------
+
+Portions of this software derived from Unicode, Inc:
+
+Copyright 2001-2004 Unicode, Inc.
+
+Disclaimer
+
+This source code is provided as is by Unicode, Inc. No claims are
+made as to fitness for any particular purpose. No warranties of any
+kind are expressed or implied. The recipient agrees to determine
+applicability of information provided. If this file has been
+purchased on magnetic or optical media from Unicode, Inc., the
+sole remedy for any claim will be exchange of defective media
+within 90 days of receipt.
+
+Limitations on Rights to Redistribute This Code
+
+Unicode, Inc. hereby grants the right to freely use the information
+supplied in this file in the creation of products supporting the
+Unicode Standard, and to make copies of this file in any form
+for internal or external distribution as long as this notice
+remains attached.
+
+---------------------------------------------------------------------------
+
+Portions of this software derived from sheredom/utf8.h:
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+
+---------------------------------------------------------------------------
+
+Portions of this software derived from RFC 1320:
+
+Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD4 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+----------------------------------------------------------------------
+
The bundled llhttp dependency is licensed under the MIT license:
Copyright Fedor Indutny, 2018.
diff --git a/README.md b/README.md
index 19fe7196976..77efdd4a688 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ libgit2 - the Git linkable library
| Build Status | |
| ------------ | - |
| **main** branch CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush) |
+| **v1.8 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.8&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.8) |
| **v1.7 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.7&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.7) |
-| **v1.6 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.6&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.6) |
| **Nightly** builds | [![Nightly Build](https://github.com/libgit2/libgit2/workflows/Nightly%20Build/badge.svg)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22Nightly+Build%22) [![Coverity Scan 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
@@ -113,7 +113,7 @@ Getting Help
**Getting Help**
If you have questions about the library, please be sure to check out the
-[API documentation](http://libgit2.github.com/libgit2/). If you still have
+[API documentation](https://libgit2.org/libgit2/). If you still have
questions, reach out to us on Slack or post a question on
[StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) (with the `libgit2` tag).
diff --git a/ci/setup-sanitizer-build.sh b/ci/setup-sanitizer-build.sh
new file mode 100755
index 00000000000..e4591f85bec
--- /dev/null
+++ b/ci/setup-sanitizer-build.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -ex
+
+# Linux updated its ASLR randomization in a way that is incompatible with
+# TSAN. See https://github.com/google/sanitizers/issues/1716
+sudo sysctl vm.mmap_rnd_bits=28
diff --git a/ci/test.sh b/ci/test.sh
index 4217568226a..98093c6ec5c 100755
--- a/ci/test.sh
+++ b/ci/test.sh
@@ -18,6 +18,11 @@ if [[ "$(uname -s)" == MINGW* ]]; then
SKIP_NTLM_TESTS=1
fi
+# older versions of git don't support push options
+if [ -z "$SKIP_PUSHOPTIONS_TESTS" ]; then
+ export GITTEST_PUSH_OPTIONS=true
+fi
+
SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd)
BUILD_PATH=${BUILD_PATH:=$PATH}
@@ -207,7 +212,6 @@ if should_run "SSH_TESTS"; then
echo "Starting SSH server..."
SSHD_DIR=`mktemp -d ${TMPDIR}/sshd.XXXXXXXX`
cp -R "${SOURCE_DIR}/tests/resources/pushoptions.git" "${SSHD_DIR}/test.git"
- ls -FlasR "${SSHD_DIR}"
cat >"${SSHD_DIR}/sshd_config" <<-EOF
Port 2222
@@ -325,10 +329,8 @@ if should_run "GITDAEMON_TESTS"; then
echo ""
export GITTEST_REMOTE_URL="git://localhost/test.git"
- export GITTEST_PUSH_OPTIONS=true
run_test gitdaemon
unset GITTEST_REMOTE_URL
- unset GITTEST_PUSH_OPTIONS
echo ""
echo "Running gitdaemon (namespace) tests"
@@ -383,12 +385,10 @@ if should_run "NTLM_TESTS"; then
export GITTEST_REMOTE_URL="http://localhost:9000/ntlm/test.git"
export GITTEST_REMOTE_USER="foo"
export GITTEST_REMOTE_PASS="baz"
- export GITTEST_PUSH_OPTIONS=true
run_test auth_clone_and_push
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_USER
unset GITTEST_REMOTE_PASS
- unset GITTEST_PUSH_OPTIONS
echo ""
echo "Running NTLM tests (Apache emulation)"
@@ -397,12 +397,10 @@ if should_run "NTLM_TESTS"; then
export GITTEST_REMOTE_URL="http://localhost:9000/broken-ntlm/test.git"
export GITTEST_REMOTE_USER="foo"
export GITTEST_REMOTE_PASS="baz"
- export GITTEST_PUSH_OPTIONS=true
run_test auth_clone_and_push
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_USER
unset GITTEST_REMOTE_PASS
- unset GITTEST_PUSH_OPTIONS
fi
if should_run "NEGOTIATE_TESTS" && -n "$GITTEST_NEGOTIATE_PASSWORD" ; then
@@ -452,20 +450,16 @@ if should_run "SSH_TESTS"; then
echo ""
export GITTEST_REMOTE_URL="ssh://localhost:2222/$SSHD_DIR/test.git"
- export GITTEST_PUSH_OPTIONS=true
run_test ssh
unset GITTEST_REMOTE_URL
- unset GITTEST_PUSH_OPTIONS
echo ""
echo "Running ssh tests (scp-style paths)"
echo ""
export GITTEST_REMOTE_URL="[localhost:2222]:$SSHD_DIR/test.git"
- export GITTEST_PUSH_OPTIONS=true
run_test ssh
unset GITTEST_REMOTE_URL
- unset GITTEST_PUSH_OPTIONS
unset GITTEST_SSH_CMD
diff --git a/cmake/SelectHTTPSBackend.cmake b/cmake/SelectHTTPSBackend.cmake
index 0b3d63a790c..d293001f567 100644
--- a/cmake/SelectHTTPSBackend.cmake
+++ b/cmake/SelectHTTPSBackend.cmake
@@ -55,6 +55,10 @@ if(USE_HTTPS)
set(GIT_OPENSSL 1)
list(APPEND LIBGIT2_SYSTEM_INCLUDES ${OPENSSL_INCLUDE_DIR})
list(APPEND LIBGIT2_SYSTEM_LIBS ${OPENSSL_LIBRARIES})
+ # Static OpenSSL (lib crypto.a) requires libdl, include it explicitly
+ if(LINK_WITH_STATIC_LIBRARIES STREQUAL ON)
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_DL_LIBS})
+ endif()
list(APPEND LIBGIT2_PC_LIBS ${OPENSSL_LDFLAGS})
list(APPEND LIBGIT2_PC_REQUIRES "openssl")
elseif(USE_HTTPS STREQUAL "mbedTLS")
diff --git a/deps/ntlmclient/CMakeLists.txt b/deps/ntlmclient/CMakeLists.txt
index 8356d472367..f1f5de162a0 100644
--- a/deps/ntlmclient/CMakeLists.txt
+++ b/deps/ntlmclient/CMakeLists.txt
@@ -31,7 +31,7 @@ elseif(USE_HTTPS STREQUAL "OpenSSL-Dynamic")
elseif(USE_HTTPS STREQUAL "mbedTLS")
add_definitions(-DCRYPT_MBEDTLS)
include_directories(${MBEDTLS_INCLUDE_DIR})
- set(SRC_NTLMCLIENT_CRYPTO "crypt_mbedtls.c" "crypt_mbedtls.h")
+ set(SRC_NTLMCLIENT_CRYPTO "crypt_mbedtls.c" "crypt_mbedtls.h" "crypt_builtin_md4.c")
else()
message(FATAL_ERROR "Unable to use libgit2's HTTPS backend (${USE_HTTPS}) for NTLM crypto")
endif()
diff --git a/deps/ntlmclient/crypt_builtin_md4.c b/deps/ntlmclient/crypt_builtin_md4.c
new file mode 100644
index 00000000000..de9a85cafaa
--- /dev/null
+++ b/deps/ntlmclient/crypt_builtin_md4.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) Edward Thomson. All rights reserved.
+ *
+ * This file is part of ntlmclient, distributed under the MIT license.
+ * For full terms and copyright information, and for third-party
+ * copyright information, see the included LICENSE.txt file.
+ */
+
+#include
+#include
+
+#include "ntlm.h"
+#include "crypt.h"
+
+/*
+ * Below is the MD4 code from RFC 1320, with minor modifications
+ * to make it compile on a modern compiler. It is included since
+ * many system crypto libraries lack MD4, sensibly.
+ */
+
+/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm
+ */
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD4 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef uint16_t UINT2;
+
+/* UINT4 defines a four byte word */
+typedef uint32_t UINT4;
+
+#define MD4_memcpy memcpy
+#define MD4_memset memset
+
+/* MD4 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD4_CTX;
+
+/* Constants for MD4Transform routine.
+ */
+#define S11 3
+#define S12 7
+#define S13 11
+#define S14 19
+#define S21 3
+#define S22 5
+#define S23 9
+#define S24 13
+#define S31 3
+#define S32 9
+#define S33 11
+#define S34 15
+
+static void MD4Transform(UINT4 [4], const unsigned char [64]);
+static void Encode (unsigned char *, UINT4 *, unsigned int);
+static void Decode (UINT4 *, const unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G and H are basic MD4 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s) { \
+ (a) += F ((b), (c), (d)) + (x); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define GG(a, b, c, d, x, s) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define HH(a, b, c, d, x, s) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+
+/* MD4 initialization. Begins an MD4 operation, writing a new context.
+ */
+static void MD4Init (MD4_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+
+ /* Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD4 block update operation. Continues an MD4 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+static void MD4Update (MD4_CTX *context, const unsigned char *input, unsigned int inputLen)
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ MD4_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD4Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD4Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD4_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen-i);
+}
+
+/* MD4 finalization. Ends an MD4 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+static void MD4Final (unsigned char digest[16], MD4_CTX *context)
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD4Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD4Update (context, bits, 8);
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+ */
+ MD4_memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD4 basic transformation. Transforms state based on block.
+ */
+static void MD4Transform (UINT4 state[4], const unsigned char block[64])
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11); /* 1 */
+ FF (d, a, b, c, x[ 1], S12); /* 2 */
+ FF (c, d, a, b, x[ 2], S13); /* 3 */
+ FF (b, c, d, a, x[ 3], S14); /* 4 */
+ FF (a, b, c, d, x[ 4], S11); /* 5 */
+ FF (d, a, b, c, x[ 5], S12); /* 6 */
+ FF (c, d, a, b, x[ 6], S13); /* 7 */
+ FF (b, c, d, a, x[ 7], S14); /* 8 */
+ FF (a, b, c, d, x[ 8], S11); /* 9 */
+ FF (d, a, b, c, x[ 9], S12); /* 10 */
+ FF (c, d, a, b, x[10], S13); /* 11 */
+ FF (b, c, d, a, x[11], S14); /* 12 */
+ FF (a, b, c, d, x[12], S11); /* 13 */
+ FF (d, a, b, c, x[13], S12); /* 14 */
+ FF (c, d, a, b, x[14], S13); /* 15 */
+ FF (b, c, d, a, x[15], S14); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 0], S21); /* 17 */
+ GG (d, a, b, c, x[ 4], S22); /* 18 */
+ GG (c, d, a, b, x[ 8], S23); /* 19 */
+ GG (b, c, d, a, x[12], S24); /* 20 */
+ GG (a, b, c, d, x[ 1], S21); /* 21 */
+ GG (d, a, b, c, x[ 5], S22); /* 22 */
+ GG (c, d, a, b, x[ 9], S23); /* 23 */
+ GG (b, c, d, a, x[13], S24); /* 24 */
+ GG (a, b, c, d, x[ 2], S21); /* 25 */
+ GG (d, a, b, c, x[ 6], S22); /* 26 */
+ GG (c, d, a, b, x[10], S23); /* 27 */
+ GG (b, c, d, a, x[14], S24); /* 28 */
+ GG (a, b, c, d, x[ 3], S21); /* 29 */
+ GG (d, a, b, c, x[ 7], S22); /* 30 */
+ GG (c, d, a, b, x[11], S23); /* 31 */
+ GG (b, c, d, a, x[15], S24); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 0], S31); /* 33 */
+ HH (d, a, b, c, x[ 8], S32); /* 34 */
+ HH (c, d, a, b, x[ 4], S33); /* 35 */
+ HH (b, c, d, a, x[12], S34); /* 36 */
+ HH (a, b, c, d, x[ 2], S31); /* 37 */
+ HH (d, a, b, c, x[10], S32); /* 38 */
+ HH (c, d, a, b, x[ 6], S33); /* 39 */
+ HH (b, c, d, a, x[14], S34); /* 40 */
+ HH (a, b, c, d, x[ 1], S31); /* 41 */
+ HH (d, a, b, c, x[ 9], S32); /* 42 */
+ HH (c, d, a, b, x[ 5], S33); /* 43 */
+ HH (b, c, d, a, x[13], S34); /* 44 */
+ HH (a, b, c, d, x[ 3], S31); /* 45 */
+ HH (d, a, b, c, x[11], S32); /* 46 */
+ HH (c, d, a, b, x[ 7], S33); /* 47 */
+ HH (b, c, d, a, x[15], S34); /* 48 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+ */
+ MD4_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode (UINT4 *output, const unsigned char *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+bool ntlm_md4_digest(
+ unsigned char out[CRYPT_MD4_DIGESTSIZE],
+ ntlm_client *ntlm,
+ const unsigned char *in,
+ size_t in_len)
+{
+ MD4_CTX ctx;
+
+ NTLM_UNUSED(ntlm);
+
+ if (in_len > UINT_MAX)
+ return false;
+
+ MD4Init(&ctx);
+ MD4Update(&ctx, in, (unsigned int)in_len);
+ MD4Final (out, &ctx);
+
+ return true;
+}
diff --git a/deps/ntlmclient/crypt_mbedtls.c b/deps/ntlmclient/crypt_mbedtls.c
index 6283c3eec08..4bbb878015d 100644
--- a/deps/ntlmclient/crypt_mbedtls.c
+++ b/deps/ntlmclient/crypt_mbedtls.c
@@ -12,7 +12,6 @@
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/des.h"
#include "mbedtls/entropy.h"
-#include "mbedtls/md4.h"
#include "ntlm.h"
#include "crypt.h"
@@ -88,25 +87,6 @@ bool ntlm_des_encrypt(
return success;
}
-bool ntlm_md4_digest(
- unsigned char out[CRYPT_MD4_DIGESTSIZE],
- ntlm_client *ntlm,
- const unsigned char *in,
- size_t in_len)
-{
- mbedtls_md4_context ctx;
-
- NTLM_UNUSED(ntlm);
-
- mbedtls_md4_init(&ctx);
- mbedtls_md4_starts(&ctx);
- mbedtls_md4_update(&ctx, in, in_len);
- mbedtls_md4_finish(&ctx, out);
- mbedtls_md4_free(&ctx);
-
- return true;
-}
-
bool ntlm_hmac_md5_init(
ntlm_client *ntlm,
const unsigned char *key,
diff --git a/deps/ntlmclient/ntlm.c b/deps/ntlmclient/ntlm.c
index ad4de5de56e..6094a4a3484 100644
--- a/deps/ntlmclient/ntlm.c
+++ b/deps/ntlmclient/ntlm.c
@@ -988,9 +988,9 @@ static inline bool generate_lm_hash(
keystr2_len = (password_len > 7) ? MIN(14, password_len) - 7 : 0;
for (i = 0; i < keystr1_len; i++)
- keystr1[i] = (unsigned char)toupper(password[i]);
+ keystr1[i] = (unsigned char)toupper((unsigned char)password[i]);
for (i = 0; i < keystr2_len; i++)
- keystr2[i] = (unsigned char)toupper(password[i+7]);
+ keystr2[i] = (unsigned char)toupper((unsigned char)password[i+7]);
/* DES encrypt the LM constant using the password as the key */
des_key_from_password(&key1, keystr1, keystr1_len);
diff --git a/docs/changelog.md b/docs/changelog.md
index f733102356a..7b118c798be 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,3 +1,320 @@
+v1.8
+----
+
+This is release v1.8.0, "Das Fliegende Klassenzimmer". This release
+includes optional, experimental support for invoking OpenSSH to fetch
+and push, an easier mechanism to perform the default behavior of
+`git commit`, and has many improvements for worktrees. This release
+also includes many other new features and bugfixes.
+
+## Major changes
+
+* **Executable SSH (OpenSSH) support**
+ libgit2 can now invoke the command-line OpenSSH to fetch from and push
+ to remotes over SSH. This support takes the place of libssh2 support.
+ To use it, configure libgit2 with `cmake -DUSE_SSH=exec`, and please
+ report any problems that you discover. By @ethomson in
+ https://github.com/libgit2/libgit2/pull/6617
+
+* **Simplified commit creation**
+ The `git_commit_create_from_stage` API was introduced to allow users to
+ better emulate the behavior of `git commit` without needing to provide
+ unnecessary information. The current state of the index is committed to
+ the current branch. By @ethomson in
+ https://github.com/libgit2/libgit2/pull/6716
+
+* **Worktree improvements**
+ A number of worktree improvements have been made for better
+ compatibility with core git. First, libgit2 now understands per-worktree
+ references, thanks to @csware in
+ https://github.com/libgit2/libgit2/pull/6387. Worktree-specific
+ configuration is now supported, thanks to @vermiculus in
+ https://github.com/libgit2/libgit2/pull/6202. And improved compatibility
+ with `git worktree add` is now supported, thanks to @herrerog in
+ https://github.com/libgit2/libgit2/pull/5319.
+
+## Breaking changes
+
+* **Adding `WORKTREE` configuration level** (ABI breaking change)
+ To support worktree configurations at the appropriate level (higher
+ priority than local configuration, but lower priority than app-specific
+ configuration), the `GIT_CONFIG_LEVEL_WORKTREE` level was introduced at
+ priority 6. `GIT_CONFIG_LEVEL_APP` now begins at priority 7.
+
+* **Changes to `git_config_entry`** (ABI breaking change)
+ The `git_config_entry` structure now contains information about the
+ `backend_type` and `origin_path`. The unused `payload` value has been
+ removed.
+
+* **`git_push_options` includes remote push options** (ABI breaking change)
+ The `git_push_options` structure now contains a value for remote push
+ options.
+
+## Other changes
+
+### New features
+
+* config: provide an "origin" for config entries by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6615
+* cli: add a `git config` command by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6616
+* Add OpenSSH support by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6617
+* remote: optionally report unchanged tips by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6645
+* Support setting oid type for in-memory repositories by @kcsaul in
+ https://github.com/libgit2/libgit2/pull/6671
+* cli: add `index-pack` command by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6681
+* Add `git_repository_commit_parents` to identify the parents of the next
+ commit given the repository state by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6707
+* commit: introduce `git_commit_create_from_stage` by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6716
+* set SSH timeout by @vafada in
+ https://github.com/libgit2/libgit2/pull/6721
+* Implement push options on push by @russell in
+ https://github.com/libgit2/libgit2/pull/6439
+* Support index.skipHash true config by @parnic in
+ https://github.com/libgit2/libgit2/pull/6738
+* worktree: mimic 'git worktree add' behavior. by @herrerog in
+ https://github.com/libgit2/libgit2/pull/5319
+* Support the extension for worktree-specific config by @vermiculus in
+ https://github.com/libgit2/libgit2/pull/6202
+* Separate config reader and writer backend priorities (for worktree
+ configs) by @ethomson in https://github.com/libgit2/libgit2/pull/6756
+* fetch: enable deepening/shortening shallow clones by @kempniu in
+ https://github.com/libgit2/libgit2/pull/6662
+
+### Bug fixes
+
+* repository: make cleanup safe for re-use with grafts by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6600
+* fix: Add missing include for `oidarray`. by @dvzrv in
+ https://github.com/libgit2/libgit2/pull/6608
+* ssh: fix `known_hosts` leak in `_git_ssh_setup_conn` by @steven9724 in
+ https://github.com/libgit2/libgit2/pull/6599
+* proxy: Return an error for invalid proxy URLs instead of crashing by
+ @lrm29 in https://github.com/libgit2/libgit2/pull/6597
+* errors: refactoring - never return `NULL` in `git_error_last()` by
+ @ethomson in https://github.com/libgit2/libgit2/pull/6625
+* Reject potential option injections over ssh by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6636
+* remote: fix memory leak in `git_remote_download()` by @7Ji in
+ https://github.com/libgit2/libgit2/pull/6651
+* git2: Fix crash when called w/o parameters by @csware in
+ https://github.com/libgit2/libgit2/pull/6673
+* Avoid macro redefinition of `ENABLE_INTSAFE_SIGNED_FUNCTIONS` by @csware
+ in https://github.com/libgit2/libgit2/pull/6666
+* util: suppress some uninitialized variable warnings by @boretrk in
+ https://github.com/libgit2/libgit2/pull/6659
+* push: set generic error in `push_negotiation` cb by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6675
+* process: test `/usr/bin/false` on BSDs by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6677
+* clone: don't mix up "http://url" with "http:/url" when figuring out if we
+ should do a local clone by @boretrk in
+ https://github.com/libgit2/libgit2/pull/6361
+* Several compatibility fixes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6678
+* Git blame buffer gives the wrong result in many cases where there are
+ by @thosey in https://github.com/libgit2/libgit2/pull/6572
+* Fix 'path cannot exist in repository' during diff for in-memory repository
+ by @kcsaul in https://github.com/libgit2/libgit2/pull/6683
+* process: don't try to close the status by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6693
+* Minor bug fixes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6695
+* Bypass shallow clone support for in-memory repositories by @kcsaul in
+ https://github.com/libgit2/libgit2/pull/6684
+* examples: use `unsigned` int for bitfields by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6699
+* Fix some bugs caught by UBscan by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6700
+* `git_diff_find_similar` doesn't always remove unmodified deltas by @yori
+ in https://github.com/libgit2/libgit2/pull/6642
+* httpclient: clear `client->parser.data` after use by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6705
+* Do not normalize `safe.directory` paths by @csware in
+ https://github.com/libgit2/libgit2/pull/6668
+* clone: don't swallow error in `should_checkout` by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6727
+* Correct index add directory/file conflict detection by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6729
+* Correct `git_revparse_single` and add revparse fuzzing by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6730
+* config: properly delete or rename section containing multivars by
+ @samueltardieu in https://github.com/libgit2/libgit2/pull/6723
+* revparse: ensure bare '@' is truly bare by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6742
+* repo: ensure we can initialize win32 paths by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6743
+* Swap `GIT_DIFF_LINE_(ADD|DEL)_EOFNL` to match other Diffs by @xphoniex in
+ https://github.com/libgit2/libgit2/pull/6240
+* diff: fix test for SHA256 support in `diff_from_buffer` by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6745
+* http: support empty http.proxy config setting by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6744
+* More `safe.directory` improvements by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6739
+* Ensure that completely ignored diff is empty by @ethomson in
+ https://github.com/libgit2/libgit2/pull/5893
+* Fix broken regexp that matches submodule names containing ".path" by
+ @csware in https://github.com/libgit2/libgit2/pull/6749
+* Fix memory leaks by @csware in
+ https://github.com/libgit2/libgit2/pull/6748
+* Make `refdb_fs` (hopefully) fully aware of per worktree refs by @csware in
+ https://github.com/libgit2/libgit2/pull/6387
+* fix log example by @albfan in https://github.com/libgit2/libgit2/pull/6359
+* fetch: fail on depth for local transport by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6757
+* Fix message trailer parsing by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6761
+* config: correct fetching the `HIGHEST_LEVEL` config by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6766
+* Avoid some API breaking changes in v1.8 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6768
+
+### Build and CI improvements
+
+* meta: update version numbers to v1.8 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6596
+* Revert "CMake: Search for ssh2 instead of libssh2." by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6619
+* cmake: fix openssl build on win32 by @lazka in
+ https://github.com/libgit2/libgit2/pull/6626
+* ci: retry flaky online tests by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6628
+* ci: update to macOS 12 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6629
+* Use `#!/bin/bash` for script with bash-specific commands by @roehling in
+ https://github.com/libgit2/libgit2/pull/6581
+* ci: overwrite nonsense in `/usr/local` during macOS setup by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6664
+* release: add a compatibility label by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6676
+* actions: set permissions by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6680
+* cmake: rename FindIconv to avoid collision with cmake by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6682
+* ci: allow workflows to read and write packages by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6687
+* ci: allow workflows to push changes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6688
+* tests: remove test for strcasecmp by @boretrk in
+ https://github.com/libgit2/libgit2/pull/6691
+* CI fixes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6694
+* ci: improvements to prepare for Cygwin support by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6696
+* Yet more CI improvements by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6697
+* Fix nightly builds by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6709
+* Benchmarks: add a site to view results by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6715
+* `GIT_RAND_GETENTROPY`: do not include `sys/random.h` by @semarie in
+ https://github.com/libgit2/libgit2/pull/6736
+* add dl to `LIBGIT2_SYSTEM_LIBS` by @christopherfujino in
+ https://github.com/libgit2/libgit2/pull/6631
+* meta: add dependency tag to release.yml by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6740
+* CI: fix our nightlies by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6751
+* trace: Re-enable tests as tracing is now enabled by default by @lrm29 in
+ https://github.com/libgit2/libgit2/pull/6752
+* tests: don't free an unininitialized repo by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6763
+* ci: reduce ASLR randomization for TSAN by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6764
+* packbuilder: adjust nondeterministic tests by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6762
+* Allow libgit2 to be compiled with mbedtls3. by @adamharrison in
+ https://github.com/libgit2/libgit2/pull/6759
+* build: update to latest actions versions by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6765
+* ctype: cast characters to unsigned when classifying characters by
+ @boretrk in https://github.com/libgit2/libgit2/pull/6679 and
+ @ethomson in https://github.com/libgit2/libgit2/pull/6770
+* valgrind: suppress OpenSSL warnings by @ethomson in https://github.com/libgit2/libgit2/pull/6769
+
+### Documentation improvements
+
+* README.md: Fix link to conan packages by @lrm29 in
+ https://github.com/libgit2/libgit2/pull/6621
+* README: replace gmaster with GitButler by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6692
+* blame example: Fix support for line range in CLI by @wetneb in
+ https://github.com/libgit2/libgit2/pull/6638
+* Support authentication in push example by @pluehne in
+ https://github.com/libgit2/libgit2/pull/5904
+* docs: fix mistake in attr.h by @DavHau in
+ https://github.com/libgit2/libgit2/pull/6714
+* Fix broken links by @csware in
+ https://github.com/libgit2/libgit2/pull/6747
+
+### Platform compatibility fixes
+
+* stransport: macOS: replace `errSSLNetworkTimeout`, with hard-coded
+ value by @mascguy in https://github.com/libgit2/libgit2/pull/6610
+
+### Git compatibility fixes
+
+* Do not trim dots from usernames by @georgthegreat in
+ https://github.com/libgit2/libgit2/pull/6657
+* merge: fix incorrect rename detection for empty files by @herrerog in
+ https://github.com/libgit2/libgit2/pull/6717
+
+### Dependency updates
+
+* zlib: upgrade bundled zlib to v1.3 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6698
+* ntlmclient: update to latest upstream ntlmclient by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6704
+
+## New Contributors
+
+* @dvzrv made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6608
+* @mascguy made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6610
+* @steven9724 made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6599
+* @lazka made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6626
+* @roehling made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6581
+* @7Ji made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6651
+* @kempniu made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6662
+* @thosey made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6572
+* @wetneb made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6638
+* @yori made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6642
+* @pluehne made their first contribution in
+ https://github.com/libgit2/libgit2/pull/5904
+* @DavHau made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6714
+* @vafada made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6721
+* @semarie made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6736
+* @christopherfujino made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6631
+* @parnic made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6738
+* @samueltardieu made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6723
+* @xphoniex made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6240
+* @adamharrison made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6759
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.7.0...v1.8.0
+
v1.7
----
diff --git a/examples/README.md b/examples/README.md
index 769c4b2678a..0f1f253877a 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -15,8 +15,8 @@ so there are no restrictions on their use.
For annotated HTML versions, see the "Examples" section of:
- http://libgit2.github.com/libgit2
+ https://libgit2.org/libgit2
such as:
- http://libgit2.github.com/libgit2/ex/HEAD/general.html
+ https://libgit2.org/libgit2/ex/HEAD/general.html
diff --git a/examples/general.c b/examples/general.c
index 7f44cd78680..0275f84a24e 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -31,8 +31,8 @@
* Git Internals that you will need to know to work with Git at this level,
* check out [Chapter 10][pg] of the Pro Git book.
*
- * [lg]: http://libgit2.github.com
- * [ap]: http://libgit2.github.com/libgit2
+ * [lg]: https://libgit2.org
+ * [ap]: https://libgit2.org/libgit2
* [pg]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain
*/
@@ -97,7 +97,7 @@ int lg2_general(git_repository *repo, int argc, char** argv)
*
* (Try running this program against tests/resources/testrepo.git.)
*
- * [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository
+ * [me]: https://libgit2.org/libgit2/#HEAD/group/repository
*/
repo_path = (argc > 1) ? argv[1] : "/opt/libgit2-test/.git";
@@ -173,7 +173,7 @@ static void oid_parsing(git_oid *oid)
* working with raw objects, we'll need to get this structure from the
* repository.
*
- * [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb
+ * [odb]: https://libgit2.org/libgit2/#HEAD/group/odb
*/
static void object_database(git_repository *repo, git_oid *oid)
{
@@ -262,7 +262,7 @@ static void object_database(git_repository *repo, git_oid *oid)
* of them here. You can read about the other ones in the [commit API
* docs][cd].
*
- * [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+ * [cd]: https://libgit2.org/libgit2/#HEAD/group/commit
*/
static void commit_writing(git_repository *repo)
{
@@ -347,7 +347,7 @@ static void commit_writing(git_repository *repo)
* data in the commit - the author (name, email, datetime), committer
* (same), tree, message, encoding and parent(s).
*
- * [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+ * [pco]: https://libgit2.org/libgit2/#HEAD/group/commit
*/
static void commit_parsing(git_repository *repo)
{
@@ -418,7 +418,7 @@ static void commit_parsing(git_repository *repo)
* functions very similarly to the commit lookup, parsing and creation
* methods, since the objects themselves are very similar.
*
- * [tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag
+ * [tm]: https://libgit2.org/libgit2/#HEAD/group/tag
*/
static void tag_parsing(git_repository *repo)
{
@@ -472,7 +472,7 @@ static void tag_parsing(git_repository *repo)
* object type in Git, but a useful structure for parsing and traversing
* tree entries.
*
- * [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree
+ * [tp]: https://libgit2.org/libgit2/#HEAD/group/tree
*/
static void tree_parsing(git_repository *repo)
{
@@ -536,7 +536,7 @@ static void tree_parsing(git_repository *repo)
* from disk and writing it to the db and getting the oid back so you
* don't have to do all those steps yourself.
*
- * [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob
+ * [ba]: https://libgit2.org/libgit2/#HEAD/group/blob
*/
static void blob_parsing(git_repository *repo)
{
@@ -578,7 +578,7 @@ static void blob_parsing(git_repository *repo)
* that were ancestors of (reachable from) a given starting point. This
* can allow you to create `git log` type functionality.
*
- * [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk
+ * [rw]: https://libgit2.org/libgit2/#HEAD/group/revwalk
*/
static void revwalking(git_repository *repo)
{
@@ -643,7 +643,7 @@ static void revwalking(git_repository *repo)
* The [index file API][gi] allows you to read, traverse, update and write
* the Git index file (sometimes thought of as the staging area).
*
- * [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index
+ * [gi]: https://libgit2.org/libgit2/#HEAD/group/index
*/
static void index_walking(git_repository *repo)
{
@@ -687,7 +687,7 @@ static void index_walking(git_repository *repo)
* references such as branches, tags and remote references (everything in
* the .git/refs directory).
*
- * [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference
+ * [ref]: https://libgit2.org/libgit2/#HEAD/group/reference
*/
static void reference_listing(git_repository *repo)
{
@@ -740,7 +740,7 @@ static void reference_listing(git_repository *repo)
* The [config API][config] allows you to list and update config values
* in any of the accessible config file locations (system, global, local).
*
- * [config]: http://libgit2.github.com/libgit2/#HEAD/group/config
+ * [config]: https://libgit2.org/libgit2/#HEAD/group/config
*/
static void config_files(const char *repo_path, git_repository* repo)
{
diff --git a/examples/log.c b/examples/log.c
index 4b0a95dcd0d..62a6eb5858f 100644
--- a/examples/log.c
+++ b/examples/log.c
@@ -50,6 +50,7 @@ static int add_revision(struct log_state *s, const char *revstr);
/** log_options holds other command line options that affect log output */
struct log_options {
int show_diff;
+ int show_oneline;
int show_log_size;
int skip, limit;
int min_parents, max_parents;
@@ -81,9 +82,11 @@ int lg2_log(git_repository *repo, int argc, char *argv[])
git_commit *commit = NULL;
git_pathspec *ps = NULL;
+ memset(&s, 0, sizeof(s));
+
/** Parse arguments and set up revwalker. */
- last_arg = parse_options(&s, &opt, argc, argv);
s.repo = repo;
+ last_arg = parse_options(&s, &opt, argc, argv);
diffopts.pathspec.strings = &argv[last_arg];
diffopts.pathspec.count = argc - last_arg;
@@ -335,34 +338,45 @@ static void print_commit(git_commit *commit, struct log_options *opts)
const char *scan, *eol;
git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
- printf("commit %s\n", buf);
- if (opts->show_log_size) {
- printf("log size %d\n", (int)strlen(git_commit_message(commit)));
- }
+ if (opts->show_oneline) {
+ printf("%s ", buf);
+ } else {
+ printf("commit %s\n", buf);
- if ((count = (int)git_commit_parentcount(commit)) > 1) {
- printf("Merge:");
- for (i = 0; i < count; ++i) {
- git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
- printf(" %s", buf);
+ if (opts->show_log_size) {
+ printf("log size %d\n", (int)strlen(git_commit_message(commit)));
+ }
+
+ if ((count = (int)git_commit_parentcount(commit)) > 1) {
+ printf("Merge:");
+ for (i = 0; i < count; ++i) {
+ git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
+ printf(" %s", buf);
+ }
+ printf("\n");
}
- printf("\n");
- }
- if ((sig = git_commit_author(commit)) != NULL) {
- printf("Author: %s <%s>\n", sig->name, sig->email);
- print_time(&sig->when, "Date: ");
+ if ((sig = git_commit_author(commit)) != NULL) {
+ printf("Author: %s <%s>\n", sig->name, sig->email);
+ print_time(&sig->when, "Date: ");
+ }
+ printf("\n");
}
- printf("\n");
for (scan = git_commit_message(commit); scan && *scan; ) {
for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
- printf(" %.*s\n", (int)(eol - scan), scan);
+ if (opts->show_oneline)
+ printf("%.*s\n", (int)(eol - scan), scan);
+ else
+ printf(" %.*s\n", (int)(eol - scan), scan);
scan = *eol ? eol + 1 : NULL;
+ if (opts->show_oneline)
+ break;
}
- printf("\n");
+ if (!opts->show_oneline)
+ printf("\n");
}
/** Helper to find how many files in a commit changed from its nth parent. */
@@ -407,8 +421,6 @@ static int parse_options(
struct log_state *s, struct log_options *opt, int argc, char **argv)
{
struct args_info args = ARGS_INFO_INIT;
-
- memset(s, 0, sizeof(*s));
s->sorting = GIT_SORT_TIME;
memset(opt, 0, sizeof(*opt));
@@ -424,7 +436,7 @@ static int parse_options(
else
/** Try failed revision parse as filename. */
break;
- } else if (!match_arg_separator(&args)) {
+ } else if (match_arg_separator(&args)) {
break;
}
else if (!strcmp(a, "--date-order"))
@@ -474,6 +486,8 @@ static int parse_options(
opt->show_diff = 1;
else if (!strcmp(a, "--log-size"))
opt->show_log_size = 1;
+ else if (!strcmp(a, "--oneline"))
+ opt->show_oneline = 1;
else
usage("Unsupported argument", a);
}
diff --git a/include/git2/common.h b/include/git2/common.h
index 0f42c34f683..b7cf20b31c9 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -228,7 +228,9 @@ typedef enum {
GIT_OPT_SET_SERVER_CONNECT_TIMEOUT,
GIT_OPT_GET_SERVER_CONNECT_TIMEOUT,
GIT_OPT_SET_SERVER_TIMEOUT,
- GIT_OPT_GET_SERVER_TIMEOUT
+ GIT_OPT_GET_SERVER_TIMEOUT,
+ GIT_OPT_SET_USER_AGENT_PRODUCT,
+ GIT_OPT_GET_USER_AGENT_PRODUCT
} git_libgit2_opt_t;
/**
@@ -337,11 +339,35 @@ typedef enum {
*
* * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent)
*
- * > Set the value of the User-Agent header. This value will be
- * > appended to "git/1.0", for compatibility with other git clients.
+ * > Set the value of the comment section of the User-Agent header.
+ * > This can be information about your product and its version.
+ * > By default this is "libgit2" followed by the libgit2 version.
* >
- * > - `user_agent` is the value that will be delivered as the
- * > User-Agent header on HTTP requests.
+ * > This value will be appended to User-Agent _product_, which
+ * > is typically set to "git/2.0".
+ * >
+ * > Set to the empty string ("") to not send any information in the
+ * > comment section, or set to NULL to restore the default.
+ *
+ * * opts(GIT_OPT_GET_USER_AGENT, git_buf *out)
+ *
+ * > Get the value of the User-Agent header.
+ * > The User-Agent is written to the `out` buffer.
+ *
+ * * opts(GIT_OPT_SET_USER_AGENT_PRODUCT, const char *user_agent_product)
+ *
+ * > Set the value of the product portion of the User-Agent header.
+ * > This defaults to "git/2.0", for compatibility with other git
+ * > clients. It is recommended to keep this as git/ for
+ * > compatibility with servers that do user-agent detection.
+ * >
+ * > Set to the empty string ("") to not send any user-agent string,
+ * > or set to NULL to restore the default.
+ *
+ * * opts(GIT_OPT_GET_USER_AGENT_PRODUCT, git_buf *out)
+ *
+ * > Get the value of the User-Agent product header.
+ * > The User-Agent product is written to the `out` buffer.
*
* * opts(GIT_OPT_SET_WINDOWS_SHAREMODE, unsigned long value)
*
@@ -377,11 +403,6 @@ typedef enum {
* >
* > - `ciphers` is the list of ciphers that are eanbled.
*
- * * opts(GIT_OPT_GET_USER_AGENT, git_buf *out)
- *
- * > Get the value of the User-Agent header.
- * > The User-Agent is written to the `out` buffer.
- *
* * opts(GIT_OPT_ENABLE_OFS_DELTA, int enabled)
*
* > Enable or disable the use of "offset deltas" when creating packfiles,
diff --git a/include/git2/config.h b/include/git2/config.h
index 332e62036d0..32361431326 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -22,8 +22,19 @@ GIT_BEGIN_DECL
/**
* Priority level of a config file.
+ *
* These priority levels correspond to the natural escalation logic
- * (from higher to lower) when searching for config entries in git.git.
+ * (from higher to lower) when reading or searching for config entries
+ * in git.git. Meaning that for the same key, the configuration in
+ * the local configuration is preferred over the configuration in
+ * the system configuration file.
+ *
+ * Callers can add their own custom configuration, beginning at the
+ * `GIT_CONFIG_LEVEL_APP` level.
+ *
+ * Writes, by default, occur in the highest priority level backend
+ * that is writable. This ordering can be overridden with
+ * `git_config_set_writeorder`.
*
* git_config_open_default() and git_repository_config() honor those
* priority levels as well.
@@ -48,9 +59,13 @@ typedef enum {
*/
GIT_CONFIG_LEVEL_LOCAL = 5,
+ /** Worktree specific configuration file; $GIT_DIR/config.worktree
+ */
+ GIT_CONFIG_LEVEL_WORKTREE = 6,
+
/** Application specific configuration file; freely defined by applications
*/
- GIT_CONFIG_LEVEL_APP = 6,
+ GIT_CONFIG_LEVEL_APP = 7,
/** Represents the highest level available config file (i.e. the most
* specific config file available that actually is loaded)
@@ -296,6 +311,11 @@ GIT_EXTERN(int) git_config_open_level(
*/
GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config);
+GIT_EXTERN(int) git_config_set_writeorder(
+ git_config *cfg,
+ git_config_level_t *levels,
+ size_t len);
+
/**
* Create a snapshot of the configuration
*
diff --git a/include/git2/email.h b/include/git2/email.h
index 20393653e9f..3389353e796 100644
--- a/include/git2/email.h
+++ b/include/git2/email.h
@@ -8,6 +8,7 @@
#define INCLUDE_git_email_h__
#include "common.h"
+#include "diff.h"
/**
* @file git2/email.h
diff --git a/include/git2/errors.h b/include/git2/errors.h
index bdace8c43dd..52fa5f0720d 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -19,20 +19,20 @@ GIT_BEGIN_DECL
/** Generic return codes */
typedef enum {
- GIT_OK = 0, /**< No error */
+ GIT_OK = 0, /**< No error */
- GIT_ERROR = -1, /**< Generic error */
- GIT_ENOTFOUND = -3, /**< Requested object could not be found */
- GIT_EEXISTS = -4, /**< Object exists preventing operation */
- GIT_EAMBIGUOUS = -5, /**< More than one object matches */
- GIT_EBUFS = -6, /**< Output buffer too short to hold data */
+ GIT_ERROR = -1, /**< Generic error */
+ GIT_ENOTFOUND = -3, /**< Requested object could not be found */
+ GIT_EEXISTS = -4, /**< Object exists preventing operation */
+ GIT_EAMBIGUOUS = -5, /**< More than one object matches */
+ GIT_EBUFS = -6, /**< Output buffer too short to hold data */
/**
* GIT_EUSER is a special error that is never generated by libgit2
* code. You can return it from a callback (e.g to stop an iteration)
* to know that it was generated by the callback and not by libgit2.
*/
- GIT_EUSER = -7,
+ GIT_EUSER = -7,
GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
@@ -60,7 +60,9 @@ typedef enum {
GIT_EAPPLYFAIL = -35, /**< Patch application failed */
GIT_EOWNER = -36, /**< The object is not owned by the current user */
GIT_TIMEOUT = -37, /**< The operation timed out */
- GIT_EUNCHANGED = -38 /**< There were no changes */
+ GIT_EUNCHANGED = -38, /**< There were no changes */
+ GIT_ENOTSUPPORTED = -39, /**< An option is not supported */
+ GIT_EREADONLY = -40 /**< The subject is read-only */
} git_error_code;
/**
diff --git a/include/git2/refspec.h b/include/git2/refspec.h
index eaf7747465c..e7087132b04 100644
--- a/include/git2/refspec.h
+++ b/include/git2/refspec.h
@@ -58,7 +58,7 @@ GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
* Get the refspec's string
*
* @param refspec the refspec
- * @returns the refspec's original string
+ * @return the refspec's original string
*/
GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec);
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 7ad820ad3c3..7067c88b0b1 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -744,10 +744,10 @@ typedef struct {
git_fetch_prune_t prune;
/**
- * How to handle reference updates; a combination of
- * `git_remote_update_flags`.
+ * How to handle reference updates; see `git_remote_update_flags`.
*/
- unsigned int update_flags;
+ unsigned int update_fetchhead : 1,
+ report_unchanged : 1;
/**
* Determines how to behave regarding tags on the remote, such
@@ -790,7 +790,8 @@ typedef struct {
GIT_FETCH_OPTIONS_VERSION, \
GIT_REMOTE_CALLBACKS_INIT, \
GIT_FETCH_PRUNE_UNSPECIFIED, \
- GIT_REMOTE_UPDATE_FETCHHEAD, \
+ 1, \
+ 0, \
GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, \
GIT_PROXY_OPTIONS_INIT }
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 0ff0856510f..0afda72d402 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -504,6 +504,7 @@ typedef enum {
GIT_REPOSITORY_ITEM_LOGS,
GIT_REPOSITORY_ITEM_MODULES,
GIT_REPOSITORY_ITEM_WORKTREES,
+ GIT_REPOSITORY_ITEM_WORKTREE_CONFIG,
GIT_REPOSITORY_ITEM__LAST
} git_repository_item_t;
diff --git a/include/git2/sys/email.h b/include/git2/sys/email.h
index 6f4a2866209..5029f9a532c 100644
--- a/include/git2/sys/email.h
+++ b/include/git2/sys/email.h
@@ -7,6 +7,11 @@
#ifndef INCLUDE_sys_git_email_h__
#define INCLUDE_sys_git_email_h__
+#include "git2/common.h"
+#include "git2/diff.h"
+#include "git2/email.h"
+#include "git2/types.h"
+
/**
* @file git2/sys/email.h
* @brief Advanced git email creation routines
diff --git a/include/git2/sys/remote.h b/include/git2/sys/remote.h
index 07309ab09b6..58950e1ec77 100644
--- a/include/git2/sys/remote.h
+++ b/include/git2/sys/remote.h
@@ -20,6 +20,9 @@
GIT_BEGIN_DECL
+/**
+ * A remote's capabilities.
+ */
typedef enum {
/** Remote supports fetching an advertised object by ID. */
GIT_REMOTE_CAPABILITY_TIP_OID = (1 << 0),
diff --git a/include/git2/version.h b/include/git2/version.h
index 76cb0026fc2..010d4a224d9 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -11,7 +11,7 @@
* The version string for libgit2. This string follows semantic
* versioning (v2) guidelines.
*/
-#define LIBGIT2_VERSION "1.8.0-alpha"
+#define LIBGIT2_VERSION "1.8.0"
/** The major version number for this version of libgit2. */
#define LIBGIT2_VER_MAJOR 1
@@ -31,13 +31,13 @@
* a prerelease name like "beta" or "rc1". For final releases, this will
* be `NULL`.
*/
-#define LIBGIT2_VER_PRERELEASE "alpha"
+#define LIBGIT2_VER_PRERELEASE NULL
/**
* The library ABI soversion for this version of libgit2. This should
* only be changed when the library has a breaking ABI change, and so
* may trail the library's version number.
*/
-#define LIBGIT2_SOVERSION "1.7"
+#define LIBGIT2_SOVERSION "1.8"
#endif
diff --git a/include/git2/worktree.h b/include/git2/worktree.h
index 9193eaf347b..a6e5d17c4b1 100644
--- a/include/git2/worktree.h
+++ b/include/git2/worktree.h
@@ -11,6 +11,7 @@
#include "buffer.h"
#include "types.h"
#include "strarray.h"
+#include "checkout.h"
/**
* @file git2/worktrees.h
@@ -85,8 +86,9 @@ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
typedef struct git_worktree_add_options {
unsigned int version;
- int lock; /**< lock newly created worktree */
- git_reference *ref; /**< reference to use for the new worktree HEAD */
+ int lock; /**< lock newly created worktree */
+ int checkout_existing; /**< allow checkout of existing branch matching worktree name */
+ git_reference *ref; /**< reference to use for the new worktree HEAD */
/**
* Options for the checkout.
@@ -95,7 +97,8 @@ typedef struct git_worktree_add_options {
} git_worktree_add_options;
#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1
-#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,NULL,GIT_CHECKOUT_OPTIONS_INIT}
+#define GIT_WORKTREE_ADD_OPTIONS_INIT { GIT_WORKTREE_ADD_OPTIONS_VERSION, \
+ 0, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT }
/**
* Initialize git_worktree_add_options structure
diff --git a/package.json b/package.json
index 203e99b9bc6..2306e721853 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "libgit2",
- "version": "1.8.0-alpha",
+ "version": "1.8.0",
"repo": "https://github.com/libgit2/libgit2",
"description": " A cross-platform, linkable library implementation of Git that you can use in your application.",
"install": "mkdir build && cd build && cmake .. && cmake --build ."
diff --git a/script/valgrind.supp b/script/valgrind.supp
index 8c4549f62be..12b6634d5fe 100644
--- a/script/valgrind.supp
+++ b/script/valgrind.supp
@@ -191,6 +191,16 @@
...
}
+{
+ ignore-openssl-undefined-in-connect
+ Memcheck:Cond
+ ...
+ obj:*libcrypto.so*
+ ...
+ fun:openssl_connect
+ ...
+}
+
{
ignore-libssh2-rsa-sha1-sign
Memcheck:Leak
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
index 84b6c190151..97797e33bd9 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -4,7 +4,8 @@ set(CLI_INCLUDES
"${libgit2_SOURCE_DIR}/src/util"
"${libgit2_SOURCE_DIR}/src/cli"
"${libgit2_SOURCE_DIR}/include"
- "${LIBGIT2_DEPENDENCY_INCLUDES}")
+ "${LIBGIT2_DEPENDENCY_INCLUDES}"
+ "${LIBGIT2_SYSTEM_INCLUDES}")
if(WIN32 AND NOT CYGWIN)
file(GLOB CLI_SRC_OS win32/*.c)
diff --git a/src/libgit2/CMakeLists.txt b/src/libgit2/CMakeLists.txt
index 876a703e857..bc7cb5b3597 100644
--- a/src/libgit2/CMakeLists.txt
+++ b/src/libgit2/CMakeLists.txt
@@ -65,12 +65,6 @@ set_target_properties(libgit2package PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJE
set_target_properties(libgit2package PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(libgit2package PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
-# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
-# Win64+MSVC+static libs = linker error
-if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
- set_target_properties(libgit2package PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
-endif()
-
ide_split_sources(libgit2package)
if(SONAME)
diff --git a/src/libgit2/clone.c b/src/libgit2/clone.c
index db9a898e34e..d62c77ac554 100644
--- a/src/libgit2/clone.c
+++ b/src/libgit2/clone.c
@@ -431,7 +431,7 @@ static int clone_into(
return error;
memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
- fetch_opts.update_flags = ~GIT_REMOTE_UPDATE_FETCHHEAD;
+ fetch_opts.update_fetchhead = 0;
if (!opts->depth)
fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
diff --git a/src/libgit2/commit.c b/src/libgit2/commit.c
index 5582a65aadf..47f6fed892f 100644
--- a/src/libgit2/commit.c
+++ b/src/libgit2/commit.c
@@ -1107,8 +1107,10 @@ int git_commit_create_from_stage(
if (given_opts)
memcpy(&opts, given_opts, sizeof(git_commit_create_options));
- if ((author = opts.author) == NULL ||
- (committer = opts.committer) == NULL) {
+ author = opts.author;
+ committer = opts.committer;
+
+ if (!author || !committer) {
if (git_signature_default(&default_signature, repo) < 0)
goto done;
diff --git a/src/libgit2/config.c b/src/libgit2/config.c
index 23a8f9ffad1..1e4e17597d6 100644
--- a/src/libgit2/config.c
+++ b/src/libgit2/config.c
@@ -22,6 +22,32 @@
#include
+/*
+ * A refcounted instance of a config_backend that can be shared across
+ * a configuration instance, any snapshots, and individual configuration
+ * levels (from `git_config_open_level`).
+ */
+typedef struct {
+ git_refcount rc;
+ git_config_backend *backend;
+} backend_instance;
+
+/*
+ * An entry in the readers or writers vector in the configuration.
+ * This is kept separate from the refcounted instance so that different
+ * views of the configuration can have different notions of levels or
+ * write orders.
+ *
+ * (eg, a standard configuration has a priority ordering of writers, a
+ * snapshot has *no* writers, and an individual level has a single
+ * writer.)
+ */
+typedef struct {
+ backend_instance *instance;
+ git_config_level_t level;
+ int write_order;
+} backend_entry;
+
void git_config_entry_free(git_config_entry *entry)
{
if (!entry)
@@ -30,75 +56,75 @@ void git_config_entry_free(git_config_entry *entry)
entry->free(entry);
}
-typedef struct {
- git_refcount rc;
-
- git_config_backend *backend;
- git_config_level_t level;
-} backend_internal;
-
-static void backend_internal_free(backend_internal *internal)
+static void backend_instance_free(backend_instance *instance)
{
git_config_backend *backend;
- backend = internal->backend;
+ backend = instance->backend;
backend->free(backend);
- git__free(internal);
+ git__free(instance);
}
-static void config_free(git_config *cfg)
+static void config_free(git_config *config)
{
size_t i;
- backend_internal *internal;
+ backend_entry *entry;
- for (i = 0; i < cfg->backends.length; ++i) {
- internal = git_vector_get(&cfg->backends, i);
- GIT_REFCOUNT_DEC(internal, backend_internal_free);
+ git_vector_foreach(&config->readers, i, entry) {
+ GIT_REFCOUNT_DEC(entry->instance, backend_instance_free);
+ git__free(entry);
}
- git_vector_free(&cfg->backends);
-
- git__memzero(cfg, sizeof(*cfg));
- git__free(cfg);
+ git_vector_free(&config->readers);
+ git_vector_free(&config->writers);
+ git__free(config);
}
-void git_config_free(git_config *cfg)
+void git_config_free(git_config *config)
{
- if (cfg == NULL)
+ if (config == NULL)
return;
- GIT_REFCOUNT_DEC(cfg, config_free);
+ GIT_REFCOUNT_DEC(config, config_free);
}
-static int config_backend_cmp(const void *a, const void *b)
+static int reader_cmp(const void *_a, const void *_b)
{
- const backend_internal *bk_a = (const backend_internal *)(a);
- const backend_internal *bk_b = (const backend_internal *)(b);
+ const backend_entry *a = _a;
+ const backend_entry *b = _b;
- return bk_b->level - bk_a->level;
+ return b->level - a->level;
}
-int git_config_new(git_config **out)
+static int writer_cmp(const void *_a, const void *_b)
{
- git_config *cfg;
+ const backend_entry *a = _a;
+ const backend_entry *b = _b;
- cfg = git__malloc(sizeof(git_config));
- GIT_ERROR_CHECK_ALLOC(cfg);
+ return b->write_order - a->write_order;
+}
- memset(cfg, 0x0, sizeof(git_config));
+int git_config_new(git_config **out)
+{
+ git_config *config;
+
+ config = git__calloc(1, sizeof(git_config));
+ GIT_ERROR_CHECK_ALLOC(config);
- if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) {
- git__free(cfg);
+ if (git_vector_init(&config->readers, 8, reader_cmp) < 0 ||
+ git_vector_init(&config->writers, 8, writer_cmp) < 0) {
+ config_free(config);
return -1;
}
- *out = cfg;
- GIT_REFCOUNT_INC(cfg);
+ GIT_REFCOUNT_INC(config);
+
+ *out = config;
return 0;
}
int git_config_add_file_ondisk(
- git_config *cfg,
+ git_config *config,
const char *path,
git_config_level_t level,
const git_repository *repo,
@@ -108,7 +134,7 @@ int git_config_add_file_ondisk(
struct stat st;
int res;
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config);
GIT_ASSERT_ARG(path);
res = p_stat(path, &st);
@@ -120,7 +146,7 @@ int git_config_add_file_ondisk(
if (git_config_backend_from_file(&file, path) < 0)
return -1;
- if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) {
+ if ((res = git_config_add_backend(config, file, level, repo, force)) < 0) {
/*
* free manually; the file is not owned by the config
* instance yet and will not be freed on cleanup
@@ -154,7 +180,7 @@ int git_config_snapshot(git_config **out, git_config *in)
{
int error = 0;
size_t i;
- backend_internal *internal;
+ backend_entry *entry;
git_config *config;
*out = NULL;
@@ -162,18 +188,20 @@ int git_config_snapshot(git_config **out, git_config *in)
if (git_config_new(&config) < 0)
return -1;
- git_vector_foreach(&in->backends, i, internal) {
+ git_vector_foreach(&in->readers, i, entry) {
git_config_backend *b;
- if ((error = internal->backend->snapshot(&b, internal->backend)) < 0)
+ if ((error = entry->instance->backend->snapshot(&b, entry->instance->backend)) < 0)
break;
- if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) {
+ if ((error = git_config_add_backend(config, b, entry->level, NULL, 0)) < 0) {
b->free(b);
break;
}
}
+ git_config_set_writeorder(config, NULL, 0);
+
if (error < 0)
git_config_free(config);
else
@@ -183,141 +211,162 @@ int git_config_snapshot(git_config **out, git_config *in)
}
static int find_backend_by_level(
- backend_internal **out,
- const git_config *cfg,
+ backend_instance **out,
+ const git_config *config,
git_config_level_t level)
{
- int pos = -1;
- backend_internal *internal;
+ backend_entry *entry, *found = NULL;
size_t i;
- /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config backend
- * which has the highest level. As config backends are stored in a vector
- * sorted by decreasing order of level, getting the backend at position 0
- * will do the job.
+ /*
+ * when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the
+ * config backend which has the highest level. As config backends
+ * are stored in a vector sorted by decreasing order of level,
+ * getting the backend at position 0 will do the job.
*/
if (level == GIT_CONFIG_HIGHEST_LEVEL) {
- pos = 0;
+ found = git_vector_get(&config->readers, 0);
} else {
- git_vector_foreach(&cfg->backends, i, internal) {
- if (internal->level == level)
- pos = (int)i;
+ git_vector_foreach(&config->readers, i, entry) {
+ if (entry->level == level) {
+ found = entry;
+ break;
+ }
}
}
- if (pos == -1) {
+ if (!found) {
git_error_set(GIT_ERROR_CONFIG,
- "no configuration exists for the given level '%i'", (int)level);
+ "no configuration exists for the given level '%d'", level);
return GIT_ENOTFOUND;
}
- *out = git_vector_get(&cfg->backends, pos);
-
+ *out = found->instance;
return 0;
}
-static int duplicate_level(void **old_raw, void *new_raw)
+static int duplicate_level(void **_old, void *_new)
{
- backend_internal **old = (backend_internal **)old_raw;
+ backend_entry **old = (backend_entry **)_old;
- GIT_UNUSED(new_raw);
+ GIT_UNUSED(_new);
- git_error_set(GIT_ERROR_CONFIG, "there already exists a configuration for the given level (%i)", (int)(*old)->level);
+ git_error_set(GIT_ERROR_CONFIG, "configuration at level %d already exists", (*old)->level);
return GIT_EEXISTS;
}
static void try_remove_existing_backend(
- git_config *cfg,
+ git_config *config,
git_config_level_t level)
{
- int pos = -1;
- backend_internal *internal;
+ backend_entry *entry, *found = NULL;
size_t i;
- git_vector_foreach(&cfg->backends, i, internal) {
- if (internal->level == level)
- pos = (int)i;
+ git_vector_foreach(&config->readers, i, entry) {
+ if (entry->level == level) {
+ git_vector_remove(&config->readers, i);
+ found = entry;
+ break;
+ }
}
- if (pos == -1)
+ if (!found)
return;
- internal = git_vector_get(&cfg->backends, pos);
-
- if (git_vector_remove(&cfg->backends, pos) < 0)
- return;
+ git_vector_foreach(&config->writers, i, entry) {
+ if (entry->level == level) {
+ git_vector_remove(&config->writers, i);
+ break;
+ }
+ }
- GIT_REFCOUNT_DEC(internal, backend_internal_free);
+ GIT_REFCOUNT_DEC(found->instance, backend_instance_free);
+ git__free(found);
}
-static int git_config__add_internal(
- git_config *cfg,
- backend_internal *internal,
+static int git_config__add_instance(
+ git_config *config,
+ backend_instance *instance,
git_config_level_t level,
int force)
{
+ backend_entry *entry;
int result;
/* delete existing config backend for level if it exists */
if (force)
- try_remove_existing_backend(cfg, level);
+ try_remove_existing_backend(config, level);
- if ((result = git_vector_insert_sorted(&cfg->backends,
- internal, &duplicate_level)) < 0)
- return result;
+ entry = git__malloc(sizeof(backend_entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
- git_vector_sort(&cfg->backends);
- internal->backend->cfg = cfg;
+ entry->instance = instance;
+ entry->level = level;
+ entry->write_order = level;
- GIT_REFCOUNT_INC(internal);
+ if ((result = git_vector_insert_sorted(&config->readers,
+ entry, &duplicate_level)) < 0 ||
+ (result = git_vector_insert_sorted(&config->writers,
+ entry, NULL)) < 0) {
+ git__free(entry);
+ return result;
+ }
+
+ GIT_REFCOUNT_INC(entry->instance);
return 0;
}
-int git_config_open_global(git_config **cfg_out, git_config *cfg)
+int git_config_open_global(git_config **out, git_config *config)
{
- if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG))
+ int error;
+
+ error = git_config_open_level(out, config, GIT_CONFIG_LEVEL_XDG);
+
+ if (error == 0)
return 0;
+ else if (error != GIT_ENOTFOUND)
+ return error;
- return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL);
+ return git_config_open_level(out, config, GIT_CONFIG_LEVEL_GLOBAL);
}
int git_config_open_level(
- git_config **cfg_out,
- const git_config *cfg_parent,
+ git_config **out,
+ const git_config *parent,
git_config_level_t level)
{
- git_config *cfg;
- backend_internal *internal;
+ git_config *config;
+ backend_instance *instance;
int res;
- if ((res = find_backend_by_level(&internal, cfg_parent, level)) < 0)
+ if ((res = find_backend_by_level(&instance, parent, level)) < 0)
return res;
- if ((res = git_config_new(&cfg)) < 0)
+ if ((res = git_config_new(&config)) < 0)
return res;
- if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) {
- git_config_free(cfg);
+ if ((res = git_config__add_instance(config, instance, level, true)) < 0) {
+ git_config_free(config);
return res;
}
- *cfg_out = cfg;
+ *out = config;
return 0;
}
int git_config_add_backend(
- git_config *cfg,
+ git_config *config,
git_config_backend *backend,
git_config_level_t level,
const git_repository *repo,
int force)
{
- backend_internal *internal;
+ backend_instance *instance;
int result;
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config);
GIT_ASSERT_ARG(backend);
GIT_ERROR_CHECK_VERSION(backend, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
@@ -325,22 +374,50 @@ int git_config_add_backend(
if ((result = backend->open(backend, level, repo)) < 0)
return result;
- internal = git__malloc(sizeof(backend_internal));
- GIT_ERROR_CHECK_ALLOC(internal);
+ instance = git__calloc(1, sizeof(backend_instance));
+ GIT_ERROR_CHECK_ALLOC(instance);
- memset(internal, 0x0, sizeof(backend_internal));
+ instance->backend = backend;
+ instance->backend->cfg = config;
- internal->backend = backend;
- internal->level = level;
-
- if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
- git__free(internal);
+ if ((result = git_config__add_instance(config, instance, level, force)) < 0) {
+ git__free(instance);
return result;
}
return 0;
}
+int git_config_set_writeorder(
+ git_config *config,
+ git_config_level_t *levels,
+ size_t len)
+{
+ backend_entry *entry;
+ size_t i, j;
+
+ GIT_ASSERT(len < INT_MAX);
+
+ git_vector_foreach(&config->readers, i, entry) {
+ bool found = false;
+
+ for (j = 0; j < len; j++) {
+ if (levels[j] == entry->level) {
+ entry->write_order = (int)j;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ entry->write_order = -1;
+ }
+
+ git_vector_sort(&config->writers);
+
+ return 0;
+}
+
/*
* Loop over all the variables
*/
@@ -348,37 +425,20 @@ int git_config_add_backend(
typedef struct {
git_config_iterator parent;
git_config_iterator *current;
- const git_config *cfg;
+ const git_config *config;
git_regexp regex;
size_t i;
} all_iter;
-static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
-{
- backend_internal *internal;
-
- for (; i > 0; --i) {
- internal = git_vector_get(&cfg->backends, i - 1);
- if (!internal || !internal->backend)
- continue;
-
- *out = i;
- return 0;
- }
-
- return -1;
-}
-
-static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+static int all_iter_next(git_config_entry **out, git_config_iterator *_iter)
{
all_iter *iter = (all_iter *) _iter;
- backend_internal *internal;
+ backend_entry *entry;
git_config_backend *backend;
- size_t i;
int error = 0;
if (iter->current != NULL &&
- (error = iter->current->next(entry, iter->current)) == 0) {
+ (error = iter->current->next(out, iter->current)) == 0) {
return 0;
}
@@ -386,12 +446,14 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
return error;
do {
- if (find_next_backend(&i, iter->cfg, iter->i) < 0)
+ if (iter->i == 0)
return GIT_ITEROVER;
- internal = git_vector_get(&iter->cfg->backends, i - 1);
- backend = internal->backend;
- iter->i = i - 1;
+ entry = git_vector_get(&iter->config->readers, iter->i - 1);
+ GIT_ASSERT(entry && entry->instance && entry->instance->backend);
+
+ backend = entry->instance->backend;
+ iter->i--;
if (iter->current)
iter->current->free(iter->current);
@@ -404,7 +466,7 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
if (error < 0)
return error;
- error = iter->current->next(entry, iter->current);
+ error = iter->current->next(out, iter->current);
/* If this backend is empty, then keep going */
if (error == GIT_ITEROVER)
continue;
@@ -423,7 +485,7 @@ static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_it
/*
* We use the "normal" function to grab the next one across
- * backends and then apply the regex
+ * readers and then apply the regex
*/
while ((error = all_iter_next(entry, _iter)) == 0) {
/* skip non-matching keys if regexp was provided */
@@ -455,7 +517,7 @@ static void all_iter_glob_free(git_config_iterator *_iter)
all_iter_free(_iter);
}
-int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
+int git_config_iterator_new(git_config_iterator **out, const git_config *config)
{
all_iter *iter;
@@ -465,21 +527,21 @@ int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
iter->parent.free = all_iter_free;
iter->parent.next = all_iter_next;
- iter->i = cfg->backends.length;
- iter->cfg = cfg;
+ iter->i = config->readers.length;
+ iter->config = config;
*out = (git_config_iterator *) iter;
return 0;
}
-int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
+int git_config_iterator_glob_new(git_config_iterator **out, const git_config *config, const char *regexp)
{
all_iter *iter;
int result;
if (regexp == NULL)
- return git_config_iterator_new(out, cfg);
+ return git_config_iterator_new(out, config);
iter = git__calloc(1, sizeof(all_iter));
GIT_ERROR_CHECK_ALLOC(iter);
@@ -491,8 +553,8 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf
iter->parent.next = all_iter_glob_next;
iter->parent.free = all_iter_glob_free;
- iter->i = cfg->backends.length;
- iter->cfg = cfg;
+ iter->i = config->readers.length;
+ iter->config = config;
*out = (git_config_iterator *) iter;
@@ -500,9 +562,9 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf
}
int git_config_foreach(
- const git_config *cfg, git_config_foreach_cb cb, void *payload)
+ const git_config *config, git_config_foreach_cb cb, void *payload)
{
- return git_config_foreach_match(cfg, NULL, cb, payload);
+ return git_config_foreach_match(config, NULL, cb, payload);
}
int git_config_backend_foreach_match(
@@ -548,7 +610,7 @@ int git_config_backend_foreach_match(
}
int git_config_foreach_match(
- const git_config *cfg,
+ const git_config *config,
const char *regexp,
git_config_foreach_cb cb,
void *payload)
@@ -557,7 +619,7 @@ int git_config_foreach_match(
git_config_iterator *iter;
git_config_entry *entry;
- if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
+ if ((error = git_config_iterator_glob_new(&iter, config, regexp)) < 0)
return error;
while (!(error = git_config_next(&entry, iter))) {
@@ -579,72 +641,59 @@ int git_config_foreach_match(
* Setters
**************/
-typedef enum {
- BACKEND_USE_SET,
- BACKEND_USE_DELETE
-} backend_use;
-
-static const char *uses[] = {
- "set",
- "delete"
-};
-
-static int get_backend_for_use(git_config_backend **out,
- git_config *cfg, const char *name, backend_use use)
-{
+ static backend_instance *get_writer_instance(git_config *config)
+ {
+ backend_entry *entry;
size_t i;
- backend_internal *backend;
- *out = NULL;
+ git_vector_foreach(&config->writers, i, entry) {
+ if (entry->instance->backend->readonly)
+ continue;
- if (git_vector_length(&cfg->backends) == 0) {
- git_error_set(GIT_ERROR_CONFIG,
- "cannot %s value for '%s' when no config backends exist",
- uses[use], name);
- return GIT_ENOTFOUND;
- }
+ if (entry->write_order < 0)
+ continue;
- git_vector_foreach(&cfg->backends, i, backend) {
- if (!backend->backend->readonly) {
- *out = backend->backend;
- return 0;
- }
+ return entry->instance;
}
- git_error_set(GIT_ERROR_CONFIG,
- "cannot %s value for '%s' when all config backends are readonly",
- uses[use], name);
- return GIT_ENOTFOUND;
+ return NULL;
+ }
+
+static git_config_backend *get_writer(git_config *config)
+{
+ backend_instance *instance = get_writer_instance(config);
+
+ return instance ? instance->backend : NULL;
}
-int git_config_delete_entry(git_config *cfg, const char *name)
+int git_config_delete_entry(git_config *config, const char *name)
{
git_config_backend *backend;
- if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
- return GIT_ENOTFOUND;
+ if ((backend = get_writer(config)) == NULL)
+ return GIT_EREADONLY;
return backend->del(backend, name);
}
-int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
+int git_config_set_int64(git_config *config, const char *name, int64_t value)
{
char str_value[32]; /* All numbers should fit in here */
p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
- return git_config_set_string(cfg, name, str_value);
+ return git_config_set_string(config, name, str_value);
}
-int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
+int git_config_set_int32(git_config *config, const char *name, int32_t value)
{
- return git_config_set_int64(cfg, name, (int64_t)value);
+ return git_config_set_int64(config, name, (int64_t)value);
}
-int git_config_set_bool(git_config *cfg, const char *name, int value)
+int git_config_set_bool(git_config *config, const char *name, int value)
{
- return git_config_set_string(cfg, name, value ? "true" : "false");
+ return git_config_set_string(config, name, value ? "true" : "false");
}
-int git_config_set_string(git_config *cfg, const char *name, const char *value)
+int git_config_set_string(git_config *config, const char *name, const char *value)
{
int error;
git_config_backend *backend;
@@ -654,13 +703,15 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return -1;
}
- if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_SET) < 0)
- return GIT_ENOTFOUND;
+ if ((backend = get_writer(config)) == NULL) {
+ git_error_set(GIT_ERROR_CONFIG, "cannot set '%s': the configuration is read-only", name);
+ return GIT_EREADONLY;
+ }
error = backend->set(backend, name, value);
- if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
- git_repository__configmap_lookup_cache_clear(GIT_REFCOUNT_OWNER(cfg));
+ if (!error && GIT_REFCOUNT_OWNER(config) != NULL)
+ git_repository__configmap_lookup_cache_clear(GIT_REFCOUNT_OWNER(config));
return error;
}
@@ -714,16 +765,17 @@ enum {
static int get_entry(
git_config_entry **out,
- const git_config *cfg,
+ const git_config *config,
const char *name,
bool normalize_name,
int want_errors)
{
+ backend_entry *entry;
+ git_config_backend *backend;
int res = GIT_ENOTFOUND;
const char *key = name;
char *normalized = NULL;
size_t i;
- backend_internal *internal;
*out = NULL;
@@ -734,11 +786,12 @@ static int get_entry(
}
res = GIT_ENOTFOUND;
- git_vector_foreach(&cfg->backends, i, internal) {
- if (!internal || !internal->backend)
- continue;
+ git_vector_foreach(&config->readers, i, entry) {
+ GIT_ASSERT(entry->instance && entry->instance->backend);
+
+ backend = entry->instance->backend;
+ res = backend->get(backend, key, out);
- res = internal->backend->get(internal->backend, key, out);
if (res != GIT_ENOTFOUND)
break;
}
@@ -746,9 +799,9 @@ static int get_entry(
git__free(normalized);
cleanup:
- if (res == GIT_ENOTFOUND)
+ if (res == GIT_ENOTFOUND) {
res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name);
- else if (res && (want_errors == GET_NO_ERRORS)) {
+ } else if (res && (want_errors == GET_NO_ERRORS)) {
git_error_clear();
res = 0;
}
@@ -757,24 +810,24 @@ static int get_entry(
}
int git_config_get_entry(
- git_config_entry **out, const git_config *cfg, const char *name)
+ git_config_entry **out, const git_config *config, const char *name)
{
- return get_entry(out, cfg, name, true, GET_ALL_ERRORS);
+ return get_entry(out, config, name, true, GET_ALL_ERRORS);
}
int git_config__lookup_entry(
git_config_entry **out,
- const git_config *cfg,
+ const git_config *config,
const char *key,
bool no_errors)
{
return get_entry(
- out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING);
+ out, config, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING);
}
int git_config_get_mapped(
int *out,
- const git_config *cfg,
+ const git_config *config,
const char *name,
const git_configmap *maps,
size_t map_n)
@@ -782,7 +835,7 @@ int git_config_get_mapped(
git_config_entry *entry;
int ret;
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return ret;
ret = git_config_lookup_map_value(out, maps, map_n, entry->value);
@@ -791,12 +844,12 @@ int git_config_get_mapped(
return ret;
}
-int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
+int git_config_get_int64(int64_t *out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return ret;
ret = git_config_parse_int64(out, entry->value);
@@ -805,12 +858,12 @@ int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
return ret;
}
-int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
+int git_config_get_int32(int32_t *out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return ret;
ret = git_config_parse_int32(out, entry->value);
@@ -819,12 +872,12 @@ int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
return ret;
}
-int git_config_get_bool(int *out, const git_config *cfg, const char *name)
+int git_config_get_bool(int *out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return ret;
ret = git_config_parse_bool(out, entry->value);
@@ -833,16 +886,15 @@ int git_config_get_bool(int *out, const git_config *cfg, const char *name)
return ret;
}
-static int is_readonly(const git_config *cfg)
+static int is_readonly(const git_config *config)
{
+ backend_entry *entry;
size_t i;
- backend_internal *internal;
- git_vector_foreach(&cfg->backends, i, internal) {
- if (!internal || !internal->backend)
- continue;
+ git_vector_foreach(&config->writers, i, entry) {
+ GIT_ASSERT(entry->instance && entry->instance->backend);
- if (!internal->backend->readonly)
+ if (!entry->instance->backend->readonly)
return 0;
}
@@ -873,21 +925,21 @@ int git_config_parse_path(git_buf *out, const char *value)
int git_config_get_path(
git_buf *out,
- const git_config *cfg,
+ const git_config *config,
const char *name)
{
- GIT_BUF_WRAP_PRIVATE(out, git_config__get_path, cfg, name);
+ GIT_BUF_WRAP_PRIVATE(out, git_config__get_path, config, name);
}
int git_config__get_path(
git_str *out,
- const git_config *cfg,
+ const git_config *config,
const char *name)
{
git_config_entry *entry;
int error;
- if ((error = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((error = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return error;
error = git_config__parse_path(out, entry->value);
@@ -897,17 +949,17 @@ int git_config__get_path(
}
int git_config_get_string(
- const char **out, const git_config *cfg, const char *name)
+ const char **out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
- if (!is_readonly(cfg)) {
+ if (!is_readonly(config)) {
git_error_set(GIT_ERROR_CONFIG, "get_string called on a live config object");
return -1;
}
- ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
+ ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS);
*out = !ret ? (entry->value ? entry->value : "") : NULL;
git_config_entry_free(entry);
@@ -916,22 +968,22 @@ int git_config_get_string(
}
int git_config_get_string_buf(
- git_buf *out, const git_config *cfg, const char *name)
+ git_buf *out, const git_config *config, const char *name)
{
- GIT_BUF_WRAP_PRIVATE(out, git_config__get_string_buf, cfg, name);
+ GIT_BUF_WRAP_PRIVATE(out, git_config__get_string_buf, config, name);
}
int git_config__get_string_buf(
- git_str *out, const git_config *cfg, const char *name)
+ git_str *out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
const char *str;
GIT_ASSERT_ARG(out);
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config);
- ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
+ ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS);
str = !ret ? (entry->value ? entry->value : "") : NULL;
if (str)
@@ -943,12 +995,12 @@ int git_config__get_string_buf(
}
char *git_config__get_string_force(
- const git_config *cfg, const char *key, const char *fallback_value)
+ const git_config *config, const char *key, const char *fallback_value)
{
git_config_entry *entry;
char *ret;
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+ get_entry(&entry, config, key, false, GET_NO_ERRORS);
ret = (entry && entry->value) ? git__strdup(entry->value) : fallback_value ? git__strdup(fallback_value) : NULL;
git_config_entry_free(entry);
@@ -956,12 +1008,12 @@ char *git_config__get_string_force(
}
int git_config__get_bool_force(
- const git_config *cfg, const char *key, int fallback_value)
+ const git_config *config, const char *key, int fallback_value)
{
int val = fallback_value;
git_config_entry *entry;
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+ get_entry(&entry, config, key, false, GET_NO_ERRORS);
if (entry && git_config_parse_bool(&val, entry->value) < 0)
git_error_clear();
@@ -971,12 +1023,12 @@ int git_config__get_bool_force(
}
int git_config__get_int_force(
- const git_config *cfg, const char *key, int fallback_value)
+ const git_config *config, const char *key, int fallback_value)
{
int32_t val = (int32_t)fallback_value;
git_config_entry *entry;
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+ get_entry(&entry, config, key, false, GET_NO_ERRORS);
if (entry && git_config_parse_int32(&val, entry->value) < 0)
git_error_clear();
@@ -986,14 +1038,14 @@ int git_config__get_int_force(
}
int git_config_get_multivar_foreach(
- const git_config *cfg, const char *name, const char *regexp,
+ const git_config *config, const char *name, const char *regexp,
git_config_foreach_cb cb, void *payload)
{
int err, found;
git_config_iterator *iter;
git_config_entry *entry;
- if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
+ if ((err = git_config_multivar_iterator_new(&iter, config, name, regexp)) < 0)
return err;
found = 0;
@@ -1055,13 +1107,13 @@ static void multivar_iter_free(git_config_iterator *_iter)
git__free(iter);
}
-int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
+int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *config, const char *name, const char *regexp)
{
multivar_iter *iter = NULL;
git_config_iterator *inner = NULL;
int error;
- if ((error = git_config_iterator_new(&inner, cfg)) < 0)
+ if ((error = git_config_iterator_new(&inner, config)) < 0)
return error;
iter = git__calloc(1, sizeof(multivar_iter));
@@ -1092,22 +1144,24 @@ int git_config_multivar_iterator_new(git_config_iterator **out, const git_config
return error;
}
-int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
+int git_config_set_multivar(git_config *config, const char *name, const char *regexp, const char *value)
{
git_config_backend *backend;
- if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
- return GIT_ENOTFOUND;
+ if ((backend = get_writer(config)) == NULL) {
+ git_error_set(GIT_ERROR_CONFIG, "cannot set '%s': the configuration is read-only", name);
+ return GIT_EREADONLY;
+ }
return backend->set_multivar(backend, name, regexp, value);
}
-int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
+int git_config_delete_multivar(git_config *config, const char *name, const char *regexp)
{
git_config_backend *backend;
- if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
- return GIT_ENOTFOUND;
+ if ((backend = get_writer(config)) == NULL)
+ return GIT_EREADONLY;
return backend->del_multivar(backend, name, regexp);
}
@@ -1218,79 +1272,77 @@ int git_config__global_location(git_str *buf)
int git_config_open_default(git_config **out)
{
int error;
- git_config *cfg = NULL;
+ git_config *config = NULL;
git_str buf = GIT_STR_INIT;
- if ((error = git_config_new(&cfg)) < 0)
+ if ((error = git_config_new(&config)) < 0)
return error;
if (!git_config__find_global(&buf) ||
!git_config__global_location(&buf)) {
- error = git_config_add_file_ondisk(cfg, buf.ptr,
+ error = git_config_add_file_ondisk(config, buf.ptr,
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
}
if (!error && !git_config__find_xdg(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
+ error = git_config_add_file_ondisk(config, buf.ptr,
GIT_CONFIG_LEVEL_XDG, NULL, 0);
if (!error && !git_config__find_system(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
+ error = git_config_add_file_ondisk(config, buf.ptr,
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0);
if (!error && !git_config__find_programdata(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
+ error = git_config_add_file_ondisk(config, buf.ptr,
GIT_CONFIG_LEVEL_PROGRAMDATA, NULL, 0);
git_str_dispose(&buf);
if (error) {
- git_config_free(cfg);
- cfg = NULL;
+ git_config_free(config);
+ config = NULL;
}
- *out = cfg;
+ *out = config;
return error;
}
-int git_config_lock(git_transaction **out, git_config *cfg)
+int git_config_lock(git_transaction **out, git_config *config)
{
+ backend_instance *instance;
int error;
- git_config_backend *backend;
- backend_internal *internal;
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config);
- internal = git_vector_get(&cfg->backends, 0);
- if (!internal || !internal->backend) {
- git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends");
- return -1;
+ if ((instance = get_writer_instance(config)) == NULL) {
+ git_error_set(GIT_ERROR_CONFIG, "cannot lock: the configuration is read-only");
+ return GIT_EREADONLY;
}
- backend = internal->backend;
- if ((error = backend->lock(backend)) < 0)
+ if ((error = instance->backend->lock(instance->backend)) < 0 ||
+ (error = git_transaction_config_new(out, config, instance)) < 0)
return error;
- return git_transaction_config_new(out, cfg);
+ GIT_REFCOUNT_INC(instance);
+ return 0;
}
-int git_config_unlock(git_config *cfg, int commit)
+int git_config_unlock(
+ git_config *config,
+ void *data,
+ int commit)
{
- git_config_backend *backend;
- backend_internal *internal;
-
- GIT_ASSERT_ARG(cfg);
+ backend_instance *instance = data;
+ int error;
- internal = git_vector_get(&cfg->backends, 0);
- if (!internal || !internal->backend) {
- git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends");
- return -1;
- }
+ GIT_ASSERT_ARG(config && data);
+ GIT_UNUSED(config);
- backend = internal->backend;
+ error = instance->backend->unlock(instance->backend, commit);
+ GIT_REFCOUNT_DEC(instance, backend_instance_free);
- return backend->unlock(backend, commit);
+ return error;
}
/***********
@@ -1447,7 +1499,7 @@ static int normalize_section(char *start, char *end)
for (scan = start; *scan; ++scan) {
if (end && scan >= end)
break;
- if (isalnum(*scan))
+ if (git__isalnum(*scan))
*scan = (char)git__tolower(*scan);
else if (*scan != '-' || scan == start)
return GIT_EINVALIDSPEC;
@@ -1509,19 +1561,32 @@ static int rename_config_entries_cb(
int error = 0;
struct rename_data *data = (struct rename_data *)payload;
size_t base_len = git_str_len(data->name);
+ git_str value = GIT_STR_INIT;
+
+ if (base_len > 0) {
+ if ((error = git_str_puts(data->name,
+ entry->name + data->old_len)) < 0 ||
+ (error = git_config_set_multivar(
+ data->config, git_str_cstr(data->name), "^$",
+ entry->value)) < 0)
+ goto cleanup;
+ }
- if (base_len > 0 &&
- !(error = git_str_puts(data->name, entry->name + data->old_len)))
- {
- error = git_config_set_string(
- data->config, git_str_cstr(data->name), entry->value);
+ git_str_putc(&value, '^');
+ git_str_puts_escape_regex(&value, entry->value);
+ git_str_putc(&value, '$');
- git_str_truncate(data->name, base_len);
+ if (git_str_oom(&value)) {
+ error = -1;
+ goto cleanup;
}
- if (!error)
- error = git_config_delete_entry(data->config, entry->name);
+ error = git_config_delete_multivar(
+ data->config, entry->name, git_str_cstr(&value));
+ cleanup:
+ git_str_truncate(data->name, base_len);
+ git_str_dispose(&value);
return error;
}
diff --git a/src/libgit2/config.h b/src/libgit2/config.h
index 01b84b157f7..5003cbf9c1f 100644
--- a/src/libgit2/config.h
+++ b/src/libgit2/config.h
@@ -24,7 +24,8 @@
struct git_config {
git_refcount rc;
- git_vector backends;
+ git_vector readers;
+ git_vector writers;
};
extern int git_config__global_location(git_str *buf);
@@ -94,17 +95,21 @@ int git_config_lookup_map_enum(git_configmap_t *type_out,
size_t map_n, int enum_val);
/**
- * Unlock the backend with the highest priority
+ * Unlock the given backend that was previously locked.
*
* Unlocking will allow other writers to update the configuration
* file. Optionally, any changes performed since the lock will be
* applied to the configuration.
*
- * @param cfg the configuration
+ * @param config the config instance
+ * @param data the config data passed to git_transaction_new
* @param commit boolean which indicates whether to commit any changes
* done since locking
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit);
+GIT_EXTERN(int) git_config_unlock(
+ git_config *config,
+ void *data,
+ int commit);
#endif
diff --git a/src/libgit2/config_parse.c b/src/libgit2/config_parse.c
index 1431bed36db..7f933db4885 100644
--- a/src/libgit2/config_parse.c
+++ b/src/libgit2/config_parse.c
@@ -25,9 +25,9 @@ static void set_parse_error(git_config_parser *reader, int col, const char *erro
}
-GIT_INLINE(int) config_keychar(int c)
+GIT_INLINE(int) config_keychar(char c)
{
- return isalnum(c) || c == '-';
+ return git__isalnum(c) || c == '-';
}
static int strip_comments(char *line, int in_quotes)
@@ -158,9 +158,10 @@ static int parse_subsection_header(git_config_parser *reader, const char *line,
static int parse_section_header(git_config_parser *reader, char **section_out)
{
char *name, *name_end;
- int name_length, c, pos;
+ int name_length, pos;
int result;
char *line;
+ char c;
size_t line_len;
git_parse_advance_ws(&reader->ctx);
@@ -382,7 +383,7 @@ static int parse_multiline_variable(git_config_parser *reader, git_str *value, i
GIT_INLINE(bool) is_namechar(char c)
{
- return isalnum(c) || c == '-';
+ return git__isalnum(c) || c == '-';
}
static int parse_name(
diff --git a/src/libgit2/diff_print.c b/src/libgit2/diff_print.c
index 32c93682679..daeefca50ca 100644
--- a/src/libgit2/diff_print.c
+++ b/src/libgit2/diff_print.c
@@ -29,6 +29,7 @@ typedef struct {
const char *new_prefix;
uint32_t flags;
int id_strlen;
+ unsigned int sent_file_header;
git_oid_t oid_type;
int (*strcomp)(const char *, const char *);
@@ -579,6 +580,30 @@ static int diff_print_patch_file_binary(
return error;
}
+GIT_INLINE(int) should_force_header(const git_diff_delta *delta)
+{
+ if (delta->old_file.mode != delta->new_file.mode)
+ return 1;
+
+ if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
+ return 1;
+
+ return 0;
+}
+
+GIT_INLINE(int) flush_file_header(const git_diff_delta *delta, diff_print_info *pi)
+{
+ if (pi->sent_file_header)
+ return 0;
+
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_str_cstr(pi->buf);
+ pi->line.content_len = git_str_len(pi->buf);
+ pi->sent_file_header = 1;
+
+ return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+}
+
static int diff_print_patch_file(
const git_diff_delta *delta, float progress, void *data)
{
@@ -609,15 +634,22 @@ static int diff_print_patch_file(
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0;
+ pi->sent_file_header = 0;
+
if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx,
id_strlen, print_index)) < 0)
return error;
- pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
- pi->line.content = git_str_cstr(pi->buf);
- pi->line.content_len = git_str_len(pi->buf);
+ /*
+ * pi->buf now contains the file header data. Go ahead and send it
+ * if there's useful data in there, like similarity. Otherwise, we
+ * should queue it to send when we see the first hunk. This prevents
+ * us from sending a header when all hunks were ignored.
+ */
+ if (should_force_header(delta) && (error = flush_file_header(delta, pi)) < 0)
+ return error;
- return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+ return 0;
}
static int diff_print_patch_binary(
@@ -632,6 +664,9 @@ static int diff_print_patch_binary(
pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
int error;
+ if ((error = flush_file_header(delta, pi)) < 0)
+ return error;
+
git_str_clear(pi->buf);
if ((error = diff_print_patch_file_binary(
@@ -651,10 +686,14 @@ static int diff_print_patch_hunk(
void *data)
{
diff_print_info *pi = data;
+ int error;
if (S_ISDIR(d->new_file.mode))
return 0;
+ if ((error = flush_file_header(d, pi)) < 0)
+ return error;
+
pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
pi->line.content = h->header;
pi->line.content_len = h->header_len;
@@ -669,10 +708,14 @@ static int diff_print_patch_line(
void *data)
{
diff_print_info *pi = data;
+ int error;
if (S_ISDIR(delta->new_file.mode))
return 0;
+ if ((error = flush_file_header(delta, pi)) < 0)
+ return error;
+
return pi->print_cb(delta, hunk, line, pi->payload);
}
diff --git a/src/libgit2/git2.rc b/src/libgit2/git2.rc
index d273afd7066..b94ecafd774 100644
--- a/src/libgit2/git2.rc
+++ b/src/libgit2/git2.rc
@@ -10,7 +10,7 @@
#endif
#ifndef LIBGIT2_COMMENTS
-# define LIBGIT2_COMMENTS "For more information visit http://libgit2.github.com/"
+# define LIBGIT2_COMMENTS "For more information visit https://libgit2.org/"
#endif
#ifdef __GNUC__
diff --git a/src/libgit2/index.c b/src/libgit2/index.c
index 5074e3e1fa4..670fbc5cf15 100644
--- a/src/libgit2/index.c
+++ b/src/libgit2/index.c
@@ -2760,6 +2760,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
unsigned int i;
struct index_header header = { 0 };
unsigned char checksum[GIT_HASH_MAX_SIZE];
+ unsigned char zero_checksum[GIT_HASH_MAX_SIZE] = { 0 };
size_t checksum_size = git_hash_size(git_oid_algorithm(index->oid_type));
const char *last = NULL;
const char *empty = "";
@@ -2849,8 +2850,11 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
/*
* SHA-1 or SHA-256 (depending on the repository's object format)
* over the content of the index file before this checksum.
+ * Note: checksum may be 0 if the index was written by a client
+ * where index.skipHash was set to true.
*/
- if (memcmp(checksum, buffer, checksum_size) != 0) {
+ if (memcmp(zero_checksum, buffer, checksum_size) != 0 &&
+ memcmp(checksum, buffer, checksum_size) != 0) {
error = index_error_invalid(
"calculated checksum does not match expected");
goto done;
diff --git a/src/libgit2/libgit2.c b/src/libgit2/libgit2.c
index 777dcbbb558..1b6f1a1f846 100644
--- a/src/libgit2/libgit2.c
+++ b/src/libgit2/libgit2.c
@@ -5,25 +5,19 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "libgit2.h"
-
#include
#include "alloc.h"
#include "buf.h"
-#include "cache.h"
#include "common.h"
#include "filter.h"
-#include "grafts.h"
#include "hash.h"
-#include "index.h"
#include "merge_driver.h"
#include "pool.h"
#include "mwindow.h"
-#include "object.h"
-#include "odb.h"
+#include "oid.h"
#include "rand.h"
-#include "refs.h"
#include "runtime.h"
+#include "settings.h"
#include "sysdir.h"
#include "thread.h"
#include "git2/global.h"
@@ -31,40 +25,12 @@
#include "streams/mbedtls.h"
#include "streams/openssl.h"
#include "streams/socket.h"
-#include "transports/smart.h"
-#include "transports/http.h"
#include "transports/ssh_libssh2.h"
#ifdef GIT_WIN32
# include "win32/w32_leakcheck.h"
#endif
-/* Declarations for tuneable settings */
-extern size_t git_mwindow__window_size;
-extern size_t git_mwindow__mapped_limit;
-extern size_t git_mwindow__file_limit;
-extern size_t git_indexer__max_objects;
-extern bool git_disable_pack_keep_file_checks;
-extern int git_odb__packed_priority;
-extern int git_odb__loose_priority;
-extern int git_socket_stream__connect_timeout;
-extern int git_socket_stream__timeout;
-
-char *git__user_agent;
-char *git__ssl_ciphers;
-
-static void libgit2_settings_global_shutdown(void)
-{
- git__free(git__user_agent);
- git__free(git__ssl_ciphers);
- git_repository__free_extensions();
-}
-
-static int git_libgit2_settings_global_init(void)
-{
- return git_runtime_shutdown_register(libgit2_settings_global_shutdown);
-}
-
int git_libgit2_init(void)
{
static git_runtime_init_fn init_fns[] = {
@@ -87,17 +53,12 @@ int git_libgit2_init(void)
git_mbedtls_stream_global_init,
git_mwindow_global_init,
git_pool_global_init,
- git_libgit2_settings_global_init
+ git_settings_global_init
};
return git_runtime_init(init_fns, ARRAY_SIZE(init_fns));
}
-int git_libgit2_init_count(void)
-{
- return git_runtime_init_count();
-}
-
int git_libgit2_shutdown(void)
{
return git_runtime_shutdown();
@@ -134,350 +95,3 @@ int git_libgit2_features(void)
#endif
;
}
-
-static int config_level_to_sysdir(int *out, int config_level)
-{
- switch (config_level) {
- case GIT_CONFIG_LEVEL_SYSTEM:
- *out = GIT_SYSDIR_SYSTEM;
- return 0;
- case GIT_CONFIG_LEVEL_XDG:
- *out = GIT_SYSDIR_XDG;
- return 0;
- case GIT_CONFIG_LEVEL_GLOBAL:
- *out = GIT_SYSDIR_GLOBAL;
- return 0;
- case GIT_CONFIG_LEVEL_PROGRAMDATA:
- *out = GIT_SYSDIR_PROGRAMDATA;
- return 0;
- default:
- break;
- }
-
- git_error_set(
- GIT_ERROR_INVALID, "invalid config path selector %d", config_level);
- return -1;
-}
-
-const char *git_libgit2__user_agent(void)
-{
- return git__user_agent;
-}
-
-const char *git_libgit2__ssl_ciphers(void)
-{
- return git__ssl_ciphers;
-}
-
-int git_libgit2_opts(int key, ...)
-{
- int error = 0;
- va_list ap;
-
- va_start(ap, key);
-
- switch (key) {
- case GIT_OPT_SET_MWINDOW_SIZE:
- git_mwindow__window_size = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_MWINDOW_SIZE:
- *(va_arg(ap, size_t *)) = git_mwindow__window_size;
- break;
-
- case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
- git_mwindow__mapped_limit = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
- *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit;
- break;
-
- case GIT_OPT_SET_MWINDOW_FILE_LIMIT:
- git_mwindow__file_limit = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_MWINDOW_FILE_LIMIT:
- *(va_arg(ap, size_t *)) = git_mwindow__file_limit;
- break;
-
- case GIT_OPT_GET_SEARCH_PATH:
- {
- int sysdir = va_arg(ap, int);
- git_buf *out = va_arg(ap, git_buf *);
- git_str str = GIT_STR_INIT;
- const git_str *tmp;
- int level;
-
- if ((error = git_buf_tostr(&str, out)) < 0 ||
- (error = config_level_to_sysdir(&level, sysdir)) < 0 ||
- (error = git_sysdir_get(&tmp, level)) < 0 ||
- (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
- break;
-
- error = git_buf_fromstr(out, &str);
- }
- break;
-
- case GIT_OPT_SET_SEARCH_PATH:
- {
- int level;
-
- if ((error = config_level_to_sysdir(&level, va_arg(ap, int))) >= 0)
- error = git_sysdir_set(level, va_arg(ap, const char *));
- }
- break;
-
- case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
- {
- git_object_t type = (git_object_t)va_arg(ap, int);
- size_t size = va_arg(ap, size_t);
- error = git_cache_set_max_object_size(type, size);
- break;
- }
-
- case GIT_OPT_SET_CACHE_MAX_SIZE:
- git_cache__max_storage = va_arg(ap, ssize_t);
- break;
-
- case GIT_OPT_ENABLE_CACHING:
- git_cache__enabled = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_GET_CACHED_MEMORY:
- *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
- *(va_arg(ap, ssize_t *)) = git_cache__max_storage;
- break;
-
- case GIT_OPT_GET_TEMPLATE_PATH:
- {
- git_buf *out = va_arg(ap, git_buf *);
- git_str str = GIT_STR_INIT;
- const git_str *tmp;
-
- if ((error = git_buf_tostr(&str, out)) < 0 ||
- (error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0 ||
- (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
- break;
-
- error = git_buf_fromstr(out, &str);
- }
- break;
-
- case GIT_OPT_SET_TEMPLATE_PATH:
- error = git_sysdir_set(GIT_SYSDIR_TEMPLATE, va_arg(ap, const char *));
- break;
-
- case GIT_OPT_SET_SSL_CERT_LOCATIONS:
-#ifdef GIT_OPENSSL
- {
- const char *file = va_arg(ap, const char *);
- const char *path = va_arg(ap, const char *);
- error = git_openssl__set_cert_location(file, path);
- }
-#elif defined(GIT_MBEDTLS)
- {
- const char *file = va_arg(ap, const char *);
- const char *path = va_arg(ap, const char *);
- error = git_mbedtls__set_cert_location(file, path);
- }
-#else
- git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support certificate locations");
- error = -1;
-#endif
- break;
- case GIT_OPT_SET_USER_AGENT:
- git__free(git__user_agent);
- git__user_agent = git__strdup(va_arg(ap, const char *));
- if (!git__user_agent) {
- git_error_set_oom();
- error = -1;
- }
-
- break;
-
- case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
- git_object__strict_input_validation = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION:
- git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_SSL_CIPHERS:
-#if (GIT_OPENSSL || GIT_MBEDTLS)
- {
- git__free(git__ssl_ciphers);
- git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
- if (!git__ssl_ciphers) {
- git_error_set_oom();
- error = -1;
- }
- }
-#else
- git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support custom ciphers");
- error = -1;
-#endif
- break;
-
- case GIT_OPT_GET_USER_AGENT:
- {
- git_buf *out = va_arg(ap, git_buf *);
- git_str str = GIT_STR_INIT;
-
- if ((error = git_buf_tostr(&str, out)) < 0 ||
- (error = git_str_puts(&str, git__user_agent)) < 0)
- break;
-
- error = git_buf_fromstr(out, &str);
- }
- break;
-
- case GIT_OPT_ENABLE_OFS_DELTA:
- git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_ENABLE_FSYNC_GITDIR:
- git_repository__fsync_gitdir = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_GET_WINDOWS_SHAREMODE:
-#ifdef GIT_WIN32
- *(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode;
-#endif
- break;
-
- case GIT_OPT_SET_WINDOWS_SHAREMODE:
-#ifdef GIT_WIN32
- git_win32__createfile_sharemode = va_arg(ap, unsigned long);
-#endif
- break;
-
- case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION:
- git_odb__strict_hash_verification = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_ALLOCATOR:
- error = git_allocator_setup(va_arg(ap, git_allocator *));
- break;
-
- case GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY:
- git_index__enforce_unsaved_safety = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_PACK_MAX_OBJECTS:
- git_indexer__max_objects = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_PACK_MAX_OBJECTS:
- *(va_arg(ap, size_t *)) = git_indexer__max_objects;
- break;
-
- case GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS:
- git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE:
- git_http__expect_continue = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_ODB_PACKED_PRIORITY:
- git_odb__packed_priority = va_arg(ap, int);
- break;
-
- case GIT_OPT_SET_ODB_LOOSE_PRIORITY:
- git_odb__loose_priority = va_arg(ap, int);
- break;
-
- case GIT_OPT_SET_EXTENSIONS:
- {
- const char **extensions = va_arg(ap, const char **);
- size_t len = va_arg(ap, size_t);
- error = git_repository__set_extensions(extensions, len);
- }
- break;
-
- case GIT_OPT_GET_EXTENSIONS:
- {
- git_strarray *out = va_arg(ap, git_strarray *);
- char **extensions;
- size_t len;
-
- if ((error = git_repository__extensions(&extensions, &len)) < 0)
- break;
-
- out->strings = extensions;
- out->count = len;
- }
- break;
-
- case GIT_OPT_GET_OWNER_VALIDATION:
- *(va_arg(ap, int *)) = git_repository__validate_ownership;
- break;
-
- case GIT_OPT_SET_OWNER_VALIDATION:
- git_repository__validate_ownership = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_GET_HOMEDIR:
- {
- git_buf *out = va_arg(ap, git_buf *);
- git_str str = GIT_STR_INIT;
- const git_str *tmp;
-
- if ((error = git_buf_tostr(&str, out)) < 0 ||
- (error = git_sysdir_get(&tmp, GIT_SYSDIR_HOME)) < 0 ||
- (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
- break;
-
- error = git_buf_fromstr(out, &str);
- }
- break;
-
- case GIT_OPT_SET_HOMEDIR:
- error = git_sysdir_set(GIT_SYSDIR_HOME, va_arg(ap, const char *));
- break;
-
- case GIT_OPT_GET_SERVER_CONNECT_TIMEOUT:
- *(va_arg(ap, int *)) = git_socket_stream__connect_timeout;
- break;
-
- case GIT_OPT_SET_SERVER_CONNECT_TIMEOUT:
- {
- int timeout = va_arg(ap, int);
-
- if (timeout < 0) {
- git_error_set(GIT_ERROR_INVALID, "invalid connect timeout");
- error = -1;
- } else {
- git_socket_stream__connect_timeout = timeout;
- }
- }
- break;
-
- case GIT_OPT_GET_SERVER_TIMEOUT:
- *(va_arg(ap, int *)) = git_socket_stream__timeout;
- break;
-
- case GIT_OPT_SET_SERVER_TIMEOUT:
- {
- int timeout = va_arg(ap, int);
-
- if (timeout < 0) {
- git_error_set(GIT_ERROR_INVALID, "invalid timeout");
- error = -1;
- } else {
- git_socket_stream__timeout = timeout;
- }
- }
- break;
-
- default:
- git_error_set(GIT_ERROR_INVALID, "invalid option key");
- error = -1;
- }
-
- va_end(ap);
-
- return error;
-}
diff --git a/src/libgit2/libgit2.h b/src/libgit2/libgit2.h
deleted file mode 100644
index a898367ae37..00000000000
--- a/src/libgit2/libgit2.h
+++ /dev/null
@@ -1,15 +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_libgit2_h__
-#define INCLUDE_libgit2_h__
-
-extern int git_libgit2_init_count(void);
-
-extern const char *git_libgit2__user_agent(void);
-extern const char *git_libgit2__ssl_ciphers(void);
-
-#endif
diff --git a/src/libgit2/merge.c b/src/libgit2/merge.c
index 0114e4b75a0..21e5ef6a8f9 100644
--- a/src/libgit2/merge.c
+++ b/src/libgit2/merge.c
@@ -1225,6 +1225,13 @@ static int merge_diff_mark_similarity_exact(
if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry))
continue;
+ /*
+ * Ignore empty files because it has always the same blob sha1
+ * and will lead to incorrect matches between all entries.
+ */
+ if (git_oid_equal(&conflict_src->ancestor_entry.id, &git_oid__empty_blob_sha1))
+ continue;
+
if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
error = deletes_by_oid_enqueue(ours_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i);
if (error < 0)
diff --git a/src/libgit2/patch_parse.c b/src/libgit2/patch_parse.c
index c06915537f2..04f2a582ab1 100644
--- a/src/libgit2/patch_parse.c
+++ b/src/libgit2/patch_parse.c
@@ -562,9 +562,9 @@ static int parse_hunk_header(
static int eof_for_origin(int origin) {
if (origin == GIT_DIFF_LINE_ADDITION)
- return GIT_DIFF_LINE_ADD_EOFNL;
- if (origin == GIT_DIFF_LINE_DELETION)
return GIT_DIFF_LINE_DEL_EOFNL;
+ if (origin == GIT_DIFF_LINE_DELETION)
+ return GIT_DIFF_LINE_ADD_EOFNL;
return GIT_DIFF_LINE_CONTEXT_EOFNL;
}
diff --git a/src/libgit2/path.c b/src/libgit2/path.c
index a19340efe6f..4b584fb8056 100644
--- a/src/libgit2/path.c
+++ b/src/libgit2/path.c
@@ -202,7 +202,7 @@ GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char *
{
size_t count = 0;
- while (len > 0 && tolower(*str) == tolower(*prefix)) {
+ while (len > 0 && git__tolower(*str) == git__tolower(*prefix)) {
count++;
str++;
prefix++;
diff --git a/src/libgit2/refdb_fs.c b/src/libgit2/refdb_fs.c
index 6952548046c..9a5c38ed63d 100644
--- a/src/libgit2/refdb_fs.c
+++ b/src/libgit2/refdb_fs.c
@@ -410,7 +410,9 @@ static const char *loose_parse_symbolic(git_str *file_content)
static bool is_per_worktree_ref(const char *ref_name)
{
return git__prefixcmp(ref_name, "refs/") != 0 ||
- git__prefixcmp(ref_name, "refs/bisect/") == 0;
+ git__prefixcmp(ref_name, "refs/bisect/") == 0 ||
+ git__prefixcmp(ref_name, "refs/worktree/") == 0 ||
+ git__prefixcmp(ref_name, "refs/rewritten/") == 0;
}
static int loose_lookup(
@@ -805,83 +807,149 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
git__free(iter);
}
-static int iter_load_loose_paths(
- refdb_fs_backend *backend,
- refdb_fs_iter *iter)
+struct iter_load_context {
+ refdb_fs_backend *backend;
+ refdb_fs_iter *iter;
+
+ /*
+ * If we have a glob with a prefix (eg `refs/heads/ *`) then we can
+ * optimize our prefix to avoid walking refs that we know won't
+ * match. This is that prefix.
+ */
+ const char *ref_prefix;
+ size_t ref_prefix_len;
+
+ /* Temporary variables to avoid unnecessary allocations */
+ git_str ref_name;
+ git_str path;
+};
+
+static void iter_load_optimize_prefix(struct iter_load_context *ctx)
{
- int error = 0;
- git_str path = GIT_STR_INIT;
- git_iterator *fsit = NULL;
- git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
- const git_index_entry *entry = NULL;
- const char *ref_prefix = GIT_REFS_DIR;
- size_t ref_prefix_len = strlen(ref_prefix);
+ const char *pos, *last_sep = NULL;
- if (!backend->commonpath) /* do nothing if no commonpath for loose refs */
- return 0;
+ if (!ctx->iter->glob)
+ return;
- fsit_opts.flags = backend->iterator_flags;
- fsit_opts.oid_type = backend->oid_type;
-
- if (iter->glob) {
- const char *last_sep = NULL;
- const char *pos;
- for (pos = iter->glob; *pos; ++pos) {
- switch (*pos) {
- case '?':
- case '*':
- case '[':
- case '\\':
- break;
- case '/':
- last_sep = pos;
- /* FALLTHROUGH */
- default:
- continue;
- }
+ for (pos = ctx->iter->glob; *pos; pos++) {
+ switch (*pos) {
+ case '?':
+ case '*':
+ case '[':
+ case '\\':
break;
+ case '/':
+ last_sep = pos;
+ /* FALLTHROUGH */
+ default:
+ continue;
}
- if (last_sep) {
- ref_prefix = iter->glob;
- ref_prefix_len = (last_sep - ref_prefix) + 1;
- }
+ break;
}
- if ((error = git_str_puts(&path, backend->commonpath)) < 0 ||
- (error = git_str_put(&path, ref_prefix, ref_prefix_len)) < 0) {
- git_str_dispose(&path);
- return error;
+ if (last_sep) {
+ ctx->ref_prefix = ctx->iter->glob;
+ ctx->ref_prefix_len = (last_sep - ctx->ref_prefix) + 1;
}
+}
+
+static int iter_load_paths(
+ struct iter_load_context *ctx,
+ const char *root_path,
+ bool worktree)
+{
+ git_iterator *fsit = NULL;
+ git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry;
+ int error = 0;
+
+ fsit_opts.flags = ctx->backend->iterator_flags;
+
+ git_str_clear(&ctx->path);
+ git_str_puts(&ctx->path, root_path);
+ git_str_put(&ctx->path, ctx->ref_prefix, ctx->ref_prefix_len);
+
+ fsit_opts.flags = ctx->backend->iterator_flags;
+ fsit_opts.oid_type = ctx->backend->oid_type;
+
+ if ((error = git_iterator_for_filesystem(&fsit, ctx->path.ptr, &fsit_opts)) < 0) {
+ /*
+ * Subdirectories - either glob provided or per-worktree refs - need
+ * not exist.
+ */
+ if ((worktree || ctx->iter->glob) && error == GIT_ENOTFOUND)
+ error = 0;
- if ((error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) {
- git_str_dispose(&path);
- return (iter->glob && error == GIT_ENOTFOUND)? 0 : error;
+ goto done;
}
- error = git_str_sets(&path, ref_prefix);
+ git_str_clear(&ctx->ref_name);
+ git_str_put(&ctx->ref_name, ctx->ref_prefix, ctx->ref_prefix_len);
- while (!error && !git_iterator_advance(&entry, fsit)) {
- const char *ref_name;
+ while (git_iterator_advance(&entry, fsit) == 0) {
char *ref_dup;
- git_str_truncate(&path, ref_prefix_len);
- git_str_puts(&path, entry->path);
- ref_name = git_str_cstr(&path);
+ git_str_truncate(&ctx->ref_name, ctx->ref_prefix_len);
+ git_str_puts(&ctx->ref_name, entry->path);
- if (git__suffixcmp(ref_name, ".lock") == 0 ||
- (iter->glob && wildmatch(iter->glob, ref_name, 0) != 0))
+ if (worktree) {
+ if (!is_per_worktree_ref(ctx->ref_name.ptr))
+ continue;
+ } else {
+ if (git_repository_is_worktree(ctx->backend->repo) &&
+ is_per_worktree_ref(ctx->ref_name.ptr))
+ continue;
+ }
+
+ if (git__suffixcmp(ctx->ref_name.ptr, ".lock") == 0)
continue;
- ref_dup = git_pool_strdup(&iter->pool, ref_name);
- if (!ref_dup)
- error = -1;
- else
- error = git_vector_insert(&iter->loose, ref_dup);
+ if (ctx->iter->glob && wildmatch(ctx->iter->glob, ctx->ref_name.ptr, 0))
+ continue;
+
+ ref_dup = git_pool_strdup(&ctx->iter->pool, ctx->ref_name.ptr);
+ GIT_ERROR_CHECK_ALLOC(ref_dup);
+
+ if ((error = git_vector_insert(&ctx->iter->loose, ref_dup)) < 0)
+ goto done;
}
+done:
git_iterator_free(fsit);
- git_str_dispose(&path);
+ return error;
+}
+#define iter_load_context_init(b, i) { b, i, GIT_REFS_DIR, CONST_STRLEN(GIT_REFS_DIR) }
+#define iter_load_context_dispose(ctx) do { \
+ git_str_dispose(&((ctx)->path)); \
+ git_str_dispose(&((ctx)->ref_name)); \
+} while(0)
+
+static int iter_load_loose_paths(
+ refdb_fs_backend *backend,
+ refdb_fs_iter *iter)
+{
+ struct iter_load_context ctx = iter_load_context_init(backend, iter);
+
+ int error = 0;
+
+ if (!backend->commonpath)
+ return 0;
+
+ iter_load_optimize_prefix(&ctx);
+
+ if ((error = iter_load_paths(&ctx,
+ backend->commonpath, false)) < 0)
+ goto done;
+
+ if (git_repository_is_worktree(backend->repo)) {
+ if ((error = iter_load_paths(&ctx,
+ backend->gitpath, true)) < 0)
+ goto done;
+ }
+
+done:
+ iter_load_context_dispose(&ctx);
return error;
}
diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c
index 9eb4bac8b2f..8b07c83184a 100644
--- a/src/libgit2/remote.c
+++ b/src/libgit2/remote.c
@@ -1376,7 +1376,12 @@ int git_remote_fetch(
return error;
if (opts) {
- update_flags = opts->update_flags;
+ if (opts->update_fetchhead)
+ update_flags |= GIT_REMOTE_UPDATE_FETCHHEAD;
+
+ if (opts->report_unchanged)
+ update_flags |= GIT_REMOTE_UPDATE_REPORT_UNCHANGED;
+
tagopt = opts->download_tags;
}
diff --git a/src/libgit2/repository.c b/src/libgit2/repository.c
index b2afc4f59d2..8e449a6df09 100644
--- a/src/libgit2/repository.c
+++ b/src/libgit2/repository.c
@@ -62,7 +62,8 @@ static const struct {
{ GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "hooks", true },
{ GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "logs", true },
{ GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM__LAST, "modules", true },
- { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "worktrees", true }
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "worktrees", true },
+ { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM_GITDIR, "config.worktree", false }
};
static int check_repositoryformatversion(int *version, git_config *config);
@@ -558,37 +559,39 @@ typedef struct {
static int validate_ownership_cb(const git_config_entry *entry, void *payload)
{
validate_ownership_data *data = payload;
+ const char *test_path;
if (strcmp(entry->value, "") == 0) {
*data->is_safe = false;
} else if (strcmp(entry->value, "*") == 0) {
*data->is_safe = true;
} else {
- const char *test_path = entry->value;
-
- if (git_str_sets(&data->tmp, test_path) < 0 ||
- git_fs_path_to_dir(&data->tmp) < 0)
+ if (git_str_sets(&data->tmp, entry->value) < 0)
return -1;
- /*
- * Ensure that `git_fs_path_to_dir` mutated the
- * input path by adding a trailing backslash.
- * A trailing backslash on the input is not allowed.
- */
- if (strcmp(data->tmp.ptr, test_path) == 0)
- return 0;
+ if (!git_fs_path_is_root(data->tmp.ptr)) {
+ /* Input must not have trailing backslash. */
+ if (!data->tmp.size ||
+ data->tmp.ptr[data->tmp.size - 1] == '/')
+ return 0;
+
+ if (git_fs_path_to_dir(&data->tmp) < 0)
+ return -1;
+ }
+
+ test_path = data->tmp.ptr;
-#ifdef GIT_WIN32
/*
- * Git for Windows does some truly bizarre things with
- * paths that start with a forward slash; and expects you
- * to escape that with `%(prefix)`. This syntax generally
- * means to add the prefix that Git was installed to -- eg
- * `/usr/local` -- unless it's an absolute path, in which
- * case the leading `%(prefix)/` is just removed. And Git
- * for Windows expects you to use this syntax for absolute
- * Unix-style paths (in "Git Bash" or Windows Subsystem for
- * Linux).
+ * Git - and especially, Git for Windows - does some
+ * truly bizarre things with paths that start with a
+ * forward slash; and expects you to escape that with
+ * `%(prefix)`. This syntax generally means to add the
+ * prefix that Git was installed to (eg `/usr/local`)
+ * unless it's an absolute path, in which case the
+ * leading `%(prefix)/` is just removed. And Git for
+ * Windows expects you to use this syntax for absolute
+ * Unix-style paths (in "Git Bash" or Windows Subsystem
+ * for Linux).
*
* Worse, the behavior used to be that a leading `/` was
* not absolute. It would indicate that Git for Windows
@@ -603,12 +606,8 @@ static int validate_ownership_cb(const git_config_entry *entry, void *payload)
*/
if (strncmp(test_path, "%(prefix)//", strlen("%(prefix)//")) == 0)
test_path += strlen("%(prefix)/");
- else if (strncmp(test_path, "//", 2) == 0 &&
- strncmp(test_path, "//wsl.localhost/", strlen("//wsl.localhost/")) != 0)
- test_path++;
-#endif
- if (strcmp(data->tmp.ptr, data->repo_path) == 0)
+ if (strcmp(test_path, data->repo_path) == 0)
*data->is_safe = true;
}
@@ -705,9 +704,12 @@ static int validate_ownership(git_repository *repo)
goto done;
if (!is_safe) {
+ size_t path_len = git_fs_path_is_root(path) ?
+ strlen(path) : git_fs_path_dirlen(path);
+
git_error_set(GIT_ERROR_CONFIG,
- "repository path '%s' is not owned by current user",
- path);
+ "repository path '%.*s' is not owned by current user",
+ (int)min(path_len, INT_MAX), path);
error = GIT_EOWNER;
}
@@ -1272,6 +1274,24 @@ int git_repository_discover(
return error;
}
+static int has_config_worktree(bool *out, git_config *cfg)
+{
+ int worktreeconfig = 0, error;
+
+ *out = false;
+
+ error = git_config_get_bool(&worktreeconfig, cfg, "extensions.worktreeconfig");
+
+ if (error == 0)
+ *out = worktreeconfig;
+ else if (error == GIT_ENOTFOUND)
+ *out = false;
+ else
+ return error;
+
+ return 0;
+}
+
static int load_config(
git_config **out,
git_repository *repo,
@@ -1280,9 +1300,11 @@ static int load_config(
const char *system_config_path,
const char *programdata_path)
{
- int error;
git_str config_path = GIT_STR_INIT;
git_config *cfg = NULL;
+ git_config_level_t write_order;
+ bool has_worktree;
+ int error;
GIT_ASSERT_ARG(out);
@@ -1296,6 +1318,14 @@ static int load_config(
if (error && error != GIT_ENOTFOUND)
goto on_error;
+ if ((error = has_config_worktree(&has_worktree, cfg)) == 0 &&
+ has_worktree &&
+ (error = git_repository__item_path(&config_path, repo, GIT_REPOSITORY_ITEM_WORKTREE_CONFIG)) == 0)
+ error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_WORKTREE, repo, 0);
+
+ if (error && error != GIT_ENOTFOUND)
+ goto on_error;
+
git_str_dispose(&config_path);
}
@@ -1325,6 +1355,11 @@ static int load_config(
git_error_clear(); /* clear any lingering ENOTFOUND errors */
+ write_order = GIT_CONFIG_LEVEL_LOCAL;
+
+ if ((error = git_config_set_writeorder(cfg, &write_order, 1)) < 0)
+ goto on_error;
+
*out = cfg;
return 0;
@@ -1844,7 +1879,8 @@ static int check_repositoryformatversion(int *version, git_config *config)
static const char *builtin_extensions[] = {
"noop",
- "objectformat"
+ "objectformat",
+ "worktreeconfig",
};
static git_vector user_extensions = { 0, git__strcmp_cb };
@@ -2676,6 +2712,8 @@ static int repo_init_directories(
if (git_str_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
return -1;
+ git_fs_path_mkposix(repo_path->ptr);
+
has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
if (has_dotgit)
opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;
@@ -3243,14 +3281,18 @@ int git_repository_set_workdir(
if (git_fs_path_prettify_dir(&path, workdir, NULL) < 0)
return -1;
- if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
+ if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0) {
+ git_str_dispose(&path);
return 0;
+ }
if (update_gitlink) {
git_config *config;
- if (git_repository_config__weakptr(&config, repo) < 0)
+ if (git_repository_config__weakptr(&config, repo) < 0) {
+ git_str_dispose(&path);
return -1;
+ }
error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
@@ -3272,6 +3314,7 @@ int git_repository_set_workdir(
git__free(old_workdir);
}
+ git_str_dispose(&path);
return error;
}
diff --git a/src/libgit2/revparse.c b/src/libgit2/revparse.c
index 06d92f82bf2..08237628793 100644
--- a/src/libgit2/revparse.c
+++ b/src/libgit2/revparse.c
@@ -817,6 +817,12 @@ static int revparse(
base_rev = temp_object;
break;
} else if (spec[pos+1] == '\0') {
+ if (pos) {
+ git_error_set(GIT_ERROR_REFERENCE, "invalid revspec");
+ error = GIT_EINVALIDSPEC;
+ goto cleanup;
+ }
+
spec = "HEAD";
identifier_len = 4;
parsed = true;
@@ -935,7 +941,7 @@ int git_revparse(
* allowed.
*/
if (!git__strcmp(spec, "..")) {
- git_error_set(GIT_ERROR_INVALID, "Invalid pattern '..'");
+ git_error_set(GIT_ERROR_INVALID, "invalid pattern '..'");
return GIT_EINVALIDSPEC;
}
diff --git a/src/libgit2/settings.c b/src/libgit2/settings.c
new file mode 100644
index 00000000000..4a41830b8fd
--- /dev/null
+++ b/src/libgit2/settings.c
@@ -0,0 +1,456 @@
+/*
+ * 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 "settings.h"
+
+#include
+#include "alloc.h"
+#include "buf.h"
+#include "cache.h"
+#include "common.h"
+#include "filter.h"
+#include "grafts.h"
+#include "hash.h"
+#include "index.h"
+#include "merge_driver.h"
+#include "pool.h"
+#include "mwindow.h"
+#include "object.h"
+#include "odb.h"
+#include "rand.h"
+#include "refs.h"
+#include "runtime.h"
+#include "sysdir.h"
+#include "thread.h"
+#include "git2/global.h"
+#include "streams/registry.h"
+#include "streams/mbedtls.h"
+#include "streams/openssl.h"
+#include "streams/socket.h"
+#include "transports/smart.h"
+#include "transports/http.h"
+#include "transports/ssh_libssh2.h"
+
+#ifdef GIT_WIN32
+# include "win32/w32_leakcheck.h"
+#endif
+
+/* Declarations for tuneable settings */
+extern size_t git_mwindow__window_size;
+extern size_t git_mwindow__mapped_limit;
+extern size_t git_mwindow__file_limit;
+extern size_t git_indexer__max_objects;
+extern bool git_disable_pack_keep_file_checks;
+extern int git_odb__packed_priority;
+extern int git_odb__loose_priority;
+extern int git_socket_stream__connect_timeout;
+extern int git_socket_stream__timeout;
+
+char *git__user_agent;
+char *git__user_agent_product;
+char *git__ssl_ciphers;
+
+static void settings_global_shutdown(void)
+{
+ git__free(git__user_agent);
+ git__free(git__user_agent_product);
+
+ git__free(git__ssl_ciphers);
+ git_repository__free_extensions();
+}
+
+int git_settings_global_init(void)
+{
+ return git_runtime_shutdown_register(settings_global_shutdown);
+}
+
+static int config_level_to_sysdir(int *out, int config_level)
+{
+ switch (config_level) {
+ case GIT_CONFIG_LEVEL_SYSTEM:
+ *out = GIT_SYSDIR_SYSTEM;
+ return 0;
+ case GIT_CONFIG_LEVEL_XDG:
+ *out = GIT_SYSDIR_XDG;
+ return 0;
+ case GIT_CONFIG_LEVEL_GLOBAL:
+ *out = GIT_SYSDIR_GLOBAL;
+ return 0;
+ case GIT_CONFIG_LEVEL_PROGRAMDATA:
+ *out = GIT_SYSDIR_PROGRAMDATA;
+ return 0;
+ default:
+ break;
+ }
+
+ git_error_set(
+ GIT_ERROR_INVALID, "invalid config path selector %d", config_level);
+ return -1;
+}
+
+const char *git_settings__user_agent_product(void)
+{
+ return git__user_agent_product ? git__user_agent_product :
+ "git/2.0";
+}
+
+const char *git_settings__user_agent(void)
+{
+ return git__user_agent ? git__user_agent :
+ "libgit2 " LIBGIT2_VERSION;
+}
+
+int git_libgit2_opts(int key, ...)
+{
+ int error = 0;
+ va_list ap;
+
+ va_start(ap, key);
+
+ switch (key) {
+ case GIT_OPT_SET_MWINDOW_SIZE:
+ git_mwindow__window_size = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_SIZE:
+ *(va_arg(ap, size_t *)) = git_mwindow__window_size;
+ break;
+
+ case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
+ git_mwindow__mapped_limit = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
+ *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit;
+ break;
+
+ case GIT_OPT_SET_MWINDOW_FILE_LIMIT:
+ git_mwindow__file_limit = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_FILE_LIMIT:
+ *(va_arg(ap, size_t *)) = git_mwindow__file_limit;
+ break;
+
+ case GIT_OPT_GET_SEARCH_PATH:
+ {
+ int sysdir = va_arg(ap, int);
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+ int level;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = config_level_to_sysdir(&level, sysdir)) < 0 ||
+ (error = git_sysdir_get(&tmp, level)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_SEARCH_PATH:
+ {
+ int level;
+
+ if ((error = config_level_to_sysdir(&level, va_arg(ap, int))) >= 0)
+ error = git_sysdir_set(level, va_arg(ap, const char *));
+ }
+ break;
+
+ case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
+ {
+ git_object_t type = (git_object_t)va_arg(ap, int);
+ size_t size = va_arg(ap, size_t);
+ error = git_cache_set_max_object_size(type, size);
+ break;
+ }
+
+ case GIT_OPT_SET_CACHE_MAX_SIZE:
+ git_cache__max_storage = va_arg(ap, ssize_t);
+ break;
+
+ case GIT_OPT_ENABLE_CACHING:
+ git_cache__enabled = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_CACHED_MEMORY:
+ *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
+ *(va_arg(ap, ssize_t *)) = git_cache__max_storage;
+ break;
+
+ case GIT_OPT_GET_TEMPLATE_PATH:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_TEMPLATE_PATH:
+ error = git_sysdir_set(GIT_SYSDIR_TEMPLATE, va_arg(ap, const char *));
+ break;
+
+ case GIT_OPT_SET_SSL_CERT_LOCATIONS:
+#ifdef GIT_OPENSSL
+ {
+ const char *file = va_arg(ap, const char *);
+ const char *path = va_arg(ap, const char *);
+ error = git_openssl__set_cert_location(file, path);
+ }
+#elif defined(GIT_MBEDTLS)
+ {
+ const char *file = va_arg(ap, const char *);
+ const char *path = va_arg(ap, const char *);
+ error = git_mbedtls__set_cert_location(file, path);
+ }
+#else
+ git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support certificate locations");
+ error = -1;
+#endif
+ break;
+
+ case GIT_OPT_SET_USER_AGENT:
+ {
+ const char *new_agent = va_arg(ap, const char *);
+
+ git__free(git__user_agent);
+
+ if (new_agent) {
+ git__user_agent= git__strdup(new_agent);
+
+ if (!git__user_agent)
+ error = -1;
+ } else {
+ git__user_agent = NULL;
+ }
+ }
+ break;
+
+ case GIT_OPT_GET_USER_AGENT:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_str_puts(&str, git_settings__user_agent())) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_USER_AGENT_PRODUCT:
+ {
+ const char *new_agent = va_arg(ap, const char *);
+
+ git__free(git__user_agent_product);
+
+ if (new_agent) {
+ git__user_agent_product = git__strdup(new_agent);
+
+ if (!git__user_agent_product)
+ error = -1;
+ } else {
+ git__user_agent_product = NULL;
+ }
+ }
+ break;
+
+ case GIT_OPT_GET_USER_AGENT_PRODUCT:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_str_puts(&str, git_settings__user_agent_product())) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
+ git_object__strict_input_validation = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION:
+ git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_SSL_CIPHERS:
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ {
+ git__free(git__ssl_ciphers);
+ git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
+ if (!git__ssl_ciphers) {
+ git_error_set_oom();
+ error = -1;
+ }
+ }
+#else
+ git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support custom ciphers");
+ error = -1;
+#endif
+ break;
+
+ case GIT_OPT_ENABLE_OFS_DELTA:
+ git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_FSYNC_GITDIR:
+ git_repository__fsync_gitdir = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+ *(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode;
+#endif
+ break;
+
+ case GIT_OPT_SET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+ git_win32__createfile_sharemode = va_arg(ap, unsigned long);
+#endif
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION:
+ git_odb__strict_hash_verification = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_ALLOCATOR:
+ error = git_allocator_setup(va_arg(ap, git_allocator *));
+ break;
+
+ case GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY:
+ git_index__enforce_unsaved_safety = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_PACK_MAX_OBJECTS:
+ git_indexer__max_objects = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_PACK_MAX_OBJECTS:
+ *(va_arg(ap, size_t *)) = git_indexer__max_objects;
+ break;
+
+ case GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS:
+ git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE:
+ git_http__expect_continue = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_ODB_PACKED_PRIORITY:
+ git_odb__packed_priority = va_arg(ap, int);
+ break;
+
+ case GIT_OPT_SET_ODB_LOOSE_PRIORITY:
+ git_odb__loose_priority = va_arg(ap, int);
+ break;
+
+ case GIT_OPT_SET_EXTENSIONS:
+ {
+ const char **extensions = va_arg(ap, const char **);
+ size_t len = va_arg(ap, size_t);
+ error = git_repository__set_extensions(extensions, len);
+ }
+ break;
+
+ case GIT_OPT_GET_EXTENSIONS:
+ {
+ git_strarray *out = va_arg(ap, git_strarray *);
+ char **extensions;
+ size_t len;
+
+ if ((error = git_repository__extensions(&extensions, &len)) < 0)
+ break;
+
+ out->strings = extensions;
+ out->count = len;
+ }
+ break;
+
+ case GIT_OPT_GET_OWNER_VALIDATION:
+ *(va_arg(ap, int *)) = git_repository__validate_ownership;
+ break;
+
+ case GIT_OPT_SET_OWNER_VALIDATION:
+ git_repository__validate_ownership = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_HOMEDIR:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_sysdir_get(&tmp, GIT_SYSDIR_HOME)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_HOMEDIR:
+ error = git_sysdir_set(GIT_SYSDIR_HOME, va_arg(ap, const char *));
+ break;
+
+ case GIT_OPT_GET_SERVER_CONNECT_TIMEOUT:
+ *(va_arg(ap, int *)) = git_socket_stream__connect_timeout;
+ break;
+
+ case GIT_OPT_SET_SERVER_CONNECT_TIMEOUT:
+ {
+ int timeout = va_arg(ap, int);
+
+ if (timeout < 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid connect timeout");
+ error = -1;
+ } else {
+ git_socket_stream__connect_timeout = timeout;
+ }
+ }
+ break;
+
+ case GIT_OPT_GET_SERVER_TIMEOUT:
+ *(va_arg(ap, int *)) = git_socket_stream__timeout;
+ break;
+
+ case GIT_OPT_SET_SERVER_TIMEOUT:
+ {
+ int timeout = va_arg(ap, int);
+
+ if (timeout < 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid timeout");
+ error = -1;
+ } else {
+ git_socket_stream__timeout = timeout;
+ }
+ }
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid option key");
+ error = -1;
+ }
+
+ va_end(ap);
+
+ return error;
+}
diff --git a/src/libgit2/settings.h b/src/libgit2/settings.h
index dc42ce93952..292936676aa 100644
--- a/src/libgit2/settings.h
+++ b/src/libgit2/settings.h
@@ -4,8 +4,12 @@
* 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_settings_h__
+#define INCLUDE_settings_h__
extern int git_settings_global_init(void);
-extern const char *git_libgit2__user_agent(void);
-extern const char *git_libgit2__ssl_ciphers(void);
+extern const char *git_settings__user_agent(void);
+extern const char *git_settings__user_agent_product(void);
+
+#endif
diff --git a/src/libgit2/streams/mbedtls.c b/src/libgit2/streams/mbedtls.c
index 49aa76c3ed8..1b2780706c6 100644
--- a/src/libgit2/streams/mbedtls.c
+++ b/src/libgit2/streams/mbedtls.c
@@ -32,7 +32,6 @@
# endif
#endif
-#include
#include
#include
#include
@@ -43,9 +42,15 @@
#define GIT_SSL_DEFAULT_CIPHERS "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-DSS-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-DSS-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-128-CBC-SHA256:TLS-DHE-DSS-WITH-AES-256-CBC-SHA256:TLS-DHE-DSS-WITH-AES-128-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-128-GCM-SHA256:TLS-RSA-WITH-AES-256-GCM-SHA384:TLS-RSA-WITH-AES-128-CBC-SHA256:TLS-RSA-WITH-AES-256-CBC-SHA256:TLS-RSA-WITH-AES-128-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA"
#define GIT_SSL_DEFAULT_CIPHERS_COUNT 30
-static mbedtls_ssl_config *git__ssl_conf;
static int ciphers_list[GIT_SSL_DEFAULT_CIPHERS_COUNT];
-static mbedtls_entropy_context *mbedtls_entropy;
+
+static bool initialized = false;
+static mbedtls_ssl_config mbedtls_config;
+static mbedtls_ctr_drbg_context mbedtls_rng;
+static mbedtls_entropy_context mbedtls_entropy;
+
+static bool has_ca_chain = false;
+static mbedtls_x509_crt mbedtls_ca_chain;
/**
* This function aims to clean-up the SSL context which
@@ -53,19 +58,16 @@ static mbedtls_entropy_context *mbedtls_entropy;
*/
static void shutdown_ssl(void)
{
- if (git__ssl_conf) {
- mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
- git__free(git__ssl_conf->ca_chain);
- mbedtls_ctr_drbg_free(git__ssl_conf->p_rng);
- git__free(git__ssl_conf->p_rng);
- mbedtls_ssl_config_free(git__ssl_conf);
- git__free(git__ssl_conf);
- git__ssl_conf = NULL;
+ if (has_ca_chain) {
+ mbedtls_x509_crt_free(&mbedtls_ca_chain);
+ has_ca_chain = false;
}
- if (mbedtls_entropy) {
- mbedtls_entropy_free(mbedtls_entropy);
- git__free(mbedtls_entropy);
- mbedtls_entropy = NULL;
+
+ if (initialized) {
+ mbedtls_ctr_drbg_free(&mbedtls_rng);
+ mbedtls_ssl_config_free(&mbedtls_config);
+ mbedtls_entropy_free(&mbedtls_entropy);
+ initialized = false;
}
}
@@ -74,32 +76,33 @@ int git_mbedtls_stream_global_init(void)
int loaded = 0;
char *crtpath = GIT_DEFAULT_CERT_LOCATION;
struct stat statbuf;
- mbedtls_ctr_drbg_context *ctr_drbg = NULL;
size_t ciphers_known = 0;
char *cipher_name = NULL;
char *cipher_string = NULL;
char *cipher_string_tmp = NULL;
- git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config));
- GIT_ERROR_CHECK_ALLOC(git__ssl_conf);
+ mbedtls_ssl_config_init(&mbedtls_config);
+ mbedtls_entropy_init(&mbedtls_entropy);
+ mbedtls_ctr_drbg_init(&mbedtls_rng);
- mbedtls_ssl_config_init(git__ssl_conf);
- if (mbedtls_ssl_config_defaults(git__ssl_conf,
- MBEDTLS_SSL_IS_CLIENT,
- MBEDTLS_SSL_TRANSPORT_STREAM,
- MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
+ if (mbedtls_ssl_config_defaults(&mbedtls_config,
+ MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM,
+ MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
git_error_set(GIT_ERROR_SSL, "failed to initialize mbedTLS");
goto cleanup;
}
- /* configure TLSv1 */
- mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0);
+ /* configure TLSv1.1 */
+#ifdef MBEDTLS_SSL_MINOR_VERSION_2
+ mbedtls_ssl_conf_min_version(&mbedtls_config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_2);
+#endif
/* verify_server_cert is responsible for making the check.
* OPTIONAL because REQUIRED drops the certificate as soon as the check
* is made, so we can never see the certificate and override it. */
- mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+ mbedtls_ssl_conf_authmode(&mbedtls_config, MBEDTLS_SSL_VERIFY_OPTIONAL);
/* set the list of allowed ciphersuites */
ciphers_known = 0;
@@ -123,42 +126,33 @@ int git_mbedtls_stream_global_init(void)
git_error_set(GIT_ERROR_SSL, "no cipher could be enabled");
goto cleanup;
}
- mbedtls_ssl_conf_ciphersuites(git__ssl_conf, ciphers_list);
+ mbedtls_ssl_conf_ciphersuites(&mbedtls_config, ciphers_list);
/* Seeding the random number generator */
- mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context));
- GIT_ERROR_CHECK_ALLOC(mbedtls_entropy);
-
- mbedtls_entropy_init(mbedtls_entropy);
-
- ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context));
- GIT_ERROR_CHECK_ALLOC(ctr_drbg);
- mbedtls_ctr_drbg_init(ctr_drbg);
-
- if (mbedtls_ctr_drbg_seed(ctr_drbg,
- mbedtls_entropy_func,
- mbedtls_entropy, NULL, 0) != 0) {
+ if (mbedtls_ctr_drbg_seed(&mbedtls_rng, mbedtls_entropy_func,
+ &mbedtls_entropy, NULL, 0) != 0) {
git_error_set(GIT_ERROR_SSL, "failed to initialize mbedTLS entropy pool");
goto cleanup;
}
- mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg);
+ mbedtls_ssl_conf_rng(&mbedtls_config, mbedtls_ctr_drbg_random, &mbedtls_rng);
/* load default certificates */
if (crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
loaded = (git_mbedtls__set_cert_location(crtpath, NULL) == 0);
+
if (!loaded && crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
loaded = (git_mbedtls__set_cert_location(NULL, crtpath) == 0);
+ initialized = true;
+
return git_runtime_shutdown_register(shutdown_ssl);
cleanup:
- mbedtls_ctr_drbg_free(ctr_drbg);
- git__free(ctr_drbg);
- mbedtls_ssl_config_free(git__ssl_conf);
- git__free(git__ssl_conf);
- git__ssl_conf = NULL;
+ mbedtls_ctr_drbg_free(&mbedtls_rng);
+ mbedtls_ssl_config_free(&mbedtls_config);
+ mbedtls_entropy_free(&mbedtls_entropy);
return -1;
}
@@ -192,7 +186,7 @@ static int ssl_set_error(mbedtls_ssl_context *ssl, int error)
break;
case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
- git_error_set(GIT_ERROR_SSL, "SSL error: %#04x [%x] - %s", error, ssl->session_negotiate->verify_result, errbuf);
+ git_error_set(GIT_ERROR_SSL, "SSL error: %#04x [%x] - %s", error, mbedtls_ssl_get_verify_result(ssl), errbuf);
ret = GIT_ECERTIFICATE;
break;
@@ -374,7 +368,7 @@ static int mbedtls_stream_wrap(
st->ssl = git__malloc(sizeof(mbedtls_ssl_context));
GIT_ERROR_CHECK_ALLOC(st->ssl);
mbedtls_ssl_init(st->ssl);
- if (mbedtls_ssl_setup(st->ssl, git__ssl_conf)) {
+ if (mbedtls_ssl_setup(st->ssl, &mbedtls_config)) {
git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
error = -1;
goto out_err;
@@ -441,30 +435,30 @@ int git_mbedtls__set_cert_location(const char *file, const char *path)
{
int ret = 0;
char errbuf[512];
- mbedtls_x509_crt *cacert;
GIT_ASSERT_ARG(file || path);
- cacert = git__malloc(sizeof(mbedtls_x509_crt));
- GIT_ERROR_CHECK_ALLOC(cacert);
+ if (has_ca_chain)
+ mbedtls_x509_crt_free(&mbedtls_ca_chain);
+
+ mbedtls_x509_crt_init(&mbedtls_ca_chain);
- mbedtls_x509_crt_init(cacert);
if (file)
- ret = mbedtls_x509_crt_parse_file(cacert, file);
+ ret = mbedtls_x509_crt_parse_file(&mbedtls_ca_chain, file);
+
if (ret >= 0 && path)
- ret = mbedtls_x509_crt_parse_path(cacert, path);
+ ret = mbedtls_x509_crt_parse_path(&mbedtls_ca_chain, path);
+
/* mbedtls_x509_crt_parse_path returns the number of invalid certs on success */
if (ret < 0) {
- mbedtls_x509_crt_free(cacert);
- git__free(cacert);
+ mbedtls_x509_crt_free(&mbedtls_ca_chain);
mbedtls_strerror( ret, errbuf, 512 );
git_error_set(GIT_ERROR_SSL, "failed to load CA certificates: %#04x - %s", ret, errbuf);
return -1;
}
- mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
- git__free(git__ssl_conf->ca_chain);
- mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL);
+ mbedtls_ssl_conf_ca_chain(&mbedtls_config, &mbedtls_ca_chain, NULL);
+ has_ca_chain = true;
return 0;
}
diff --git a/src/libgit2/streams/openssl.c b/src/libgit2/streams/openssl.c
index 9db911e39b3..7cb8f7f927c 100644
--- a/src/libgit2/streams/openssl.c
+++ b/src/libgit2/streams/openssl.c
@@ -36,6 +36,8 @@
# include
#endif
+extern char *git__ssl_ciphers;
+
SSL_CTX *git__ssl_ctx;
#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
@@ -105,7 +107,7 @@ static void git_openssl_free(void *mem)
static int openssl_init(void)
{
long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
- const char *ciphers = git_libgit2__ssl_ciphers();
+ const char *ciphers = git__ssl_ciphers;
#ifdef VALGRIND
static bool allocators_initialized = false;
#endif
diff --git a/src/libgit2/submodule.c b/src/libgit2/submodule.c
index 95ea84fc233..830d41c7d22 100644
--- a/src/libgit2/submodule.c
+++ b/src/libgit2/submodule.c
@@ -196,7 +196,7 @@ static void free_submodule_names(git_strmap *names)
*/
static int load_submodule_names(git_strmap **out, git_repository *repo, git_config *cfg)
{
- const char *key = "submodule\\..*\\.path";
+ const char *key = "^submodule\\..*\\.path$";
git_config_iterator *iter = NULL;
git_config_entry *entry;
git_str buf = GIT_STR_INIT;
@@ -332,7 +332,7 @@ int git_submodule__lookup_with_cache(
/* If it's not configured or we're looking by path */
if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
git_config_backend *mods;
- const char *pattern = "submodule\\..*\\.path";
+ const char *pattern = "^submodule\\..*\\.path$";
git_str path = GIT_STR_INIT;
fbp_data data = { NULL, NULL };
diff --git a/src/libgit2/trailer.c b/src/libgit2/trailer.c
index 4761c9922f2..c7579fb3b97 100644
--- a/src/libgit2/trailer.c
+++ b/src/libgit2/trailer.c
@@ -24,7 +24,7 @@ static const char *const git_generated_prefixes[] = {
static int is_blank_line(const char *str)
{
const char *s = str;
- while (*s && *s != '\n' && isspace(*s))
+ while (*s && *s != '\n' && git__isspace(*s))
s++;
return !*s || *s == '\n';
}
@@ -93,7 +93,7 @@ static bool find_separator(size_t *out, const char *line, const char *separators
return true;
}
- if (!whitespace_found && (isalnum(*c) || *c == '-'))
+ if (!whitespace_found && (git__isalnum(*c) || *c == '-'))
continue;
if (c != line && (*c == ' ' || *c == '\t')) {
whitespace_found = 1;
@@ -158,7 +158,7 @@ static size_t find_patch_start(const char *str)
const char *s;
for (s = str; *s; s = next_line(s)) {
- if (git__prefixcmp(s, "---") == 0)
+ if (git__prefixcmp(s, "---") == 0 && git__isspace(s[3]))
return s - str;
}
@@ -233,12 +233,12 @@ static size_t find_trailer_start(const char *buf, size_t len)
}
find_separator(&separator_pos, bol, TRAILER_SEPARATORS);
- if (separator_pos >= 1 && !isspace(bol[0])) {
+ if (separator_pos >= 1 && !git__isspace(bol[0])) {
trailer_lines++;
possible_continuation_lines = 0;
if (recognized_prefix)
continue;
- } else if (isspace(bol[0]))
+ } else if (git__isspace(bol[0]))
possible_continuation_lines++;
else {
non_trailer_lines++;
@@ -323,7 +323,7 @@ int git_message_trailers(git_message_trailer_array *trailer_arr, const char *mes
goto ret;
}
- if (isalnum(*ptr) || *ptr == '-') {
+ if (git__isalnum(*ptr) || *ptr == '-') {
/* legal key character */
NEXT(S_KEY);
}
diff --git a/src/libgit2/transaction.c b/src/libgit2/transaction.c
index ccffa9984cc..963416196b4 100644
--- a/src/libgit2/transaction.c
+++ b/src/libgit2/transaction.c
@@ -49,12 +49,16 @@ struct git_transaction {
git_repository *repo;
git_refdb *db;
git_config *cfg;
+ void *cfg_data;
git_strmap *locks;
git_pool pool;
};
-int git_transaction_config_new(git_transaction **out, git_config *cfg)
+int git_transaction_config_new(
+ git_transaction **out,
+ git_config *cfg,
+ void *data)
{
git_transaction *tx;
@@ -66,6 +70,8 @@ int git_transaction_config_new(git_transaction **out, git_config *cfg)
tx->type = TRANSACTION_CONFIG;
tx->cfg = cfg;
+ tx->cfg_data = data;
+
*out = tx;
return 0;
}
@@ -333,8 +339,9 @@ int git_transaction_commit(git_transaction *tx)
GIT_ASSERT_ARG(tx);
if (tx->type == TRANSACTION_CONFIG) {
- error = git_config_unlock(tx->cfg, true);
+ error = git_config_unlock(tx->cfg, tx->cfg_data, true);
tx->cfg = NULL;
+ tx->cfg_data = NULL;
return error;
}
@@ -369,10 +376,8 @@ void git_transaction_free(git_transaction *tx)
return;
if (tx->type == TRANSACTION_CONFIG) {
- if (tx->cfg) {
- git_config_unlock(tx->cfg, false);
- git_config_free(tx->cfg);
- }
+ if (tx->cfg)
+ git_config_unlock(tx->cfg, tx->cfg_data, false);
git__free(tx);
return;
diff --git a/src/libgit2/transaction.h b/src/libgit2/transaction.h
index 780c068303e..cb26017ae9f 100644
--- a/src/libgit2/transaction.h
+++ b/src/libgit2/transaction.h
@@ -9,6 +9,9 @@
#include "common.h"
-int git_transaction_config_new(git_transaction **out, git_config *cfg);
+int git_transaction_config_new(
+ git_transaction **out,
+ git_config *cfg,
+ void *data);
#endif
diff --git a/src/libgit2/transports/http.c b/src/libgit2/transports/http.c
index fc2161b6076..ea819952018 100644
--- a/src/libgit2/transports/http.c
+++ b/src/libgit2/transports/http.c
@@ -333,7 +333,7 @@ static int lookup_proxy(
return 0;
}
- if (!proxy ||
+ if (!proxy || !*proxy ||
(error = git_net_url_parse_http(&transport->proxy.url, proxy)) < 0)
goto done;
diff --git a/src/libgit2/transports/http.h b/src/libgit2/transports/http.h
index 8e8e7226ed2..7410202a820 100644
--- a/src/libgit2/transports/http.h
+++ b/src/libgit2/transports/http.h
@@ -15,14 +15,4 @@
extern bool git_http__expect_continue;
-GIT_INLINE(int) git_http__user_agent(git_str *buf)
-{
- const char *ua = git_libgit2__user_agent();
-
- if (!ua)
- ua = "libgit2 " LIBGIT2_VERSION;
-
- return git_str_printf(buf, "git/2.0 (%s)", ua);
-}
-
#endif
diff --git a/src/libgit2/transports/httpclient.c b/src/libgit2/transports/httpclient.c
index 9937e9484b1..a0c4002e806 100644
--- a/src/libgit2/transports/httpclient.c
+++ b/src/libgit2/transports/httpclient.c
@@ -649,6 +649,30 @@ static int puts_host_and_port(git_str *buf, git_net_url *url, bool force_port)
return git_str_oom(buf) ? -1 : 0;
}
+static int append_user_agent(git_str *buf)
+{
+ const char *product = git_settings__user_agent_product();
+ const char *comment = git_settings__user_agent();
+
+ GIT_ASSERT(product && comment);
+
+ if (!*product)
+ return 0;
+
+ git_str_puts(buf, "User-Agent: ");
+ git_str_puts(buf, product);
+
+ if (*comment) {
+ git_str_puts(buf, " (");
+ git_str_puts(buf, comment);
+ git_str_puts(buf, ")");
+ }
+
+ git_str_puts(buf, "\r\n");
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
static int generate_connect_request(
git_http_client *client,
git_http_request *request)
@@ -663,9 +687,7 @@ static int generate_connect_request(
puts_host_and_port(buf, &client->server.url, true);
git_str_puts(buf, " HTTP/1.1\r\n");
- git_str_puts(buf, "User-Agent: ");
- git_http__user_agent(buf);
- git_str_puts(buf, "\r\n");
+ append_user_agent(buf);
git_str_puts(buf, "Host: ");
puts_host_and_port(buf, &client->server.url, true);
@@ -709,9 +731,7 @@ static int generate_request(
git_str_puts(buf, " HTTP/1.1\r\n");
- git_str_puts(buf, "User-Agent: ");
- git_http__user_agent(buf);
- git_str_puts(buf, "\r\n");
+ append_user_agent(buf);
git_str_puts(buf, "Host: ");
puts_host_and_port(buf, request->url, false);
diff --git a/src/libgit2/transports/local.c b/src/libgit2/transports/local.c
index 64c21afbd0d..fe59bcab0c1 100644
--- a/src/libgit2/transports/local.c
+++ b/src/libgit2/transports/local.c
@@ -303,6 +303,11 @@ static int local_negotiate_fetch(
GIT_UNUSED(wants);
+ if (wants->depth) {
+ git_error_set(GIT_ERROR_NET, "shallow fetch is not supported by the local transport");
+ return GIT_ENOTSUPPORTED;
+ }
+
/* Fill in the loids */
git_vector_foreach(&t->refs, i, rhead) {
git_object *obj;
diff --git a/src/libgit2/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c
index 3307acfa08e..7ea8676e966 100644
--- a/src/libgit2/transports/smart_pkt.c
+++ b/src/libgit2/transports/smart_pkt.c
@@ -536,10 +536,10 @@ static int parse_len(size_t *out, const char *line, size_t linelen)
num[PKT_LEN_SIZE] = '\0';
for (i = 0; i < PKT_LEN_SIZE; ++i) {
- if (!isxdigit(num[i])) {
+ if (!git__isxdigit(num[i])) {
/* Make sure there are no special characters before passing to error message */
for (k = 0; k < PKT_LEN_SIZE; ++k) {
- if(!isprint(num[k])) {
+ if(!git__isprint(num[k])) {
num[k] = '.';
}
}
diff --git a/src/libgit2/transports/winhttp.c b/src/libgit2/transports/winhttp.c
index ae572c56d8e..7eca4b7443b 100644
--- a/src/libgit2/transports/winhttp.c
+++ b/src/libgit2/transports/winhttp.c
@@ -436,7 +436,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
GIT_ERROR_CHECK_ALLOC(proxy_url);
}
- if (proxy_url) {
+ if (proxy_url && *proxy_url) {
git_str processed_url = GIT_STR_INIT;
WINHTTP_PROXY_INFO proxy_info;
wchar_t *proxy_wide;
@@ -746,6 +746,33 @@ static void CALLBACK winhttp_status(
}
}
+static int user_agent(bool *exists, git_str *out)
+{
+ const char *product = git_settings__user_agent_product();
+ const char *comment = git_settings__user_agent();
+
+ GIT_ASSERT(product && comment);
+
+ if (!*product) {
+ *exists = false;
+ return 0;
+ }
+
+ git_str_puts(out, product);
+
+ if (*comment) {
+ git_str_puts(out, " (");
+ git_str_puts(out, comment);
+ git_str_puts(out, ")");
+ }
+
+ if (git_str_oom(out))
+ return -1;
+
+ *exists = true;
+ return 0;
+}
+
static int winhttp_connect(
winhttp_subtransport *t)
{
@@ -757,6 +784,7 @@ static int winhttp_connect(
int error = -1;
int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+ bool has_ua = true;
DWORD protocols =
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
@@ -787,11 +815,11 @@ static int winhttp_connect(
goto on_error;
}
-
- if (git_http__user_agent(&ua) < 0)
+ if (user_agent(&has_ua, &ua) < 0)
goto on_error;
- if (git_utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
+ if (has_ua &&
+ git_utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
goto on_error;
}
diff --git a/src/libgit2/worktree.c b/src/libgit2/worktree.c
index a878634ca84..00ff9e7da6c 100644
--- a/src/libgit2/worktree.c
+++ b/src/libgit2/worktree.c
@@ -335,11 +335,21 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
goto out;
}
- if (git_branch_is_checked_out(wtopts.ref)) {
- git_error_set(GIT_ERROR_WORKTREE, "reference is already checked out");
- err = -1;
+ if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
goto out;
- }
+ } else if (wtopts.checkout_existing && git_branch_lookup(&ref, repo, name, GIT_BRANCH_LOCAL) == 0) {
+ /* Do nothing */
+ } else if ((err = git_repository_head(&head, repo)) < 0 ||
+ (err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0 ||
+ (err = git_branch_create(&ref, repo, name, commit, false)) < 0) {
+ goto out;
+ }
+
+ if (git_branch_is_checked_out(ref)) {
+ git_error_set(GIT_ERROR_WORKTREE, "reference %s is already checked out",
+ git_reference_name(ref));
+ err = -1;
+ goto out;
}
/* Create gitdir directory ".git/worktrees/" */
@@ -392,19 +402,6 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
|| (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
goto out;
- /* Set up worktree reference */
- if (wtopts.ref) {
- if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
- goto out;
- } else {
- if ((err = git_repository_head(&head, repo)) < 0)
- goto out;
- if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
- goto out;
- if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
- goto out;
- }
-
/* Set worktree's HEAD */
if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
goto out;
diff --git a/src/util/ctype_compat.h b/src/util/ctype_compat.h
new file mode 100644
index 00000000000..462c8a17f17
--- /dev/null
+++ b/src/util/ctype_compat.h
@@ -0,0 +1,70 @@
+/*
+ * 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_ctype_compat_h__
+#define INCLUDE_ctype_compat_h__
+
+/*
+ * The Microsoft C runtime (MSVCRT) may take a heavy lock on the
+ * locale in order to figure out how the `ctype` functions work.
+ * This is deeply slow. Provide our own to avoid that.
+ */
+
+#ifdef GIT_WIN32
+
+GIT_INLINE(int) git__tolower(int c)
+{
+ return (c >= 'A' && c <= 'Z') ? (c + 32) : c;
+}
+
+GIT_INLINE(int) git__toupper(int c)
+{
+ return (c >= 'a' && c <= 'z') ? (c - 32) : c;
+}
+
+GIT_INLINE(bool) git__isalpha(int c)
+{
+ return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
+}
+
+GIT_INLINE(bool) git__isdigit(int c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+GIT_INLINE(bool) git__isalnum(int c)
+{
+ return git__isalpha(c) || git__isdigit(c);
+}
+
+GIT_INLINE(bool) git__isspace(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
+}
+
+GIT_INLINE(bool) git__isxdigit(int c)
+{
+ return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+GIT_INLINE(bool) git__isprint(int c)
+{
+ return (c >= ' ' && c <= '~');
+}
+
+#else
+# define git__tolower(a) tolower((unsigned char)(a))
+# define git__toupper(a) toupper((unsigned char)(a))
+
+# define git__isalpha(a) (!!isalpha((unsigned char)(a)))
+# define git__isdigit(a) (!!isdigit((unsigned char)(a)))
+# define git__isalnum(a) (!!isalnum((unsigned char)(a)))
+# define git__isspace(a) (!!isspace((unsigned char)(a)))
+# define git__isxdigit(a) (!!isxdigit((unsigned char)(a)))
+# define git__isprint(a) (!!isprint((unsigned char)(a)))
+#endif
+
+#endif
diff --git a/src/util/date.c b/src/util/date.c
index 4d757e21a00..872cb81f33c 100644
--- a/src/util/date.c
+++ b/src/util/date.c
@@ -129,9 +129,9 @@ static size_t match_string(const char *date, const char *str)
for (i = 0; *date; date++, str++, i++) {
if (*date == *str)
continue;
- if (toupper(*date) == toupper(*str))
+ if (git__toupper(*date) == git__toupper(*str))
continue;
- if (!isalnum(*date))
+ if (!git__isalnum(*date))
break;
return 0;
}
@@ -143,7 +143,7 @@ static int skip_alpha(const char *date)
int i = 0;
do {
i++;
- } while (isalpha(date[i]));
+ } while (git__isalpha(date[i]));
return i;
}
@@ -251,7 +251,7 @@ static size_t match_multi_number(unsigned long num, char c, const char *date, ch
num2 = strtol(end+1, &end, 10);
num3 = -1;
- if (*end == c && isdigit(end[1]))
+ if (*end == c && git__isdigit(end[1]))
num3 = strtol(end+1, &end, 10);
/* Time? Date? */
@@ -349,7 +349,7 @@ static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_
case '.':
case '/':
case '-':
- if (isdigit(end[1])) {
+ if (git__isdigit(end[1])) {
size_t match = match_multi_number(num, *end, date, end, tm);
if (match)
return match;
@@ -364,7 +364,7 @@ static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_
n = 0;
do {
n++;
- } while (isdigit(date[n]));
+ } while (git__isdigit(date[n]));
/* Four-digit year or a timezone? */
if (n == 4) {
@@ -514,11 +514,11 @@ static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset
if (!c || c == '\n')
break;
- if (isalpha(c))
+ if (git__isalpha(c))
match = match_alpha(date, &tm, offset);
- else if (isdigit(c))
+ else if (git__isdigit(c))
match = match_digit(date, &tm, offset, &tm_gmt);
- else if ((c == '-' || c == '+') && isdigit(date[1]))
+ else if ((c == '-' || c == '+') && git__isdigit(date[1]))
match = match_tz(date, offset);
if (!match) {
@@ -682,7 +682,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
const char *end = date;
int i;
- while (isalpha(*++end))
+ while (git__isalpha(*++end))
/* scan to non-alpha */;
for (i = 0; i < 12; i++) {
@@ -783,7 +783,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
case '.':
case '/':
case '-':
- if (isdigit(end[1])) {
+ if (git__isdigit(end[1])) {
size_t match = match_multi_number(number, *end, date, end, tm);
if (match)
return date + match;
@@ -843,13 +843,13 @@ static git_time_t approxidate_str(const char *date,
if (!c)
break;
date++;
- if (isdigit(c)) {
+ if (git__isdigit(c)) {
pending_number(&tm, &number);
date = approxidate_digit(date-1, &tm, &number);
touched = 1;
continue;
}
- if (isalpha(c))
+ if (git__isalpha(c))
date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
}
pending_number(&tm, &number);
diff --git a/src/util/fs_path.c b/src/util/fs_path.c
index ab64778c23e..9d5c99eab81 100644
--- a/src/util/fs_path.c
+++ b/src/util/fs_path.c
@@ -419,6 +419,16 @@ int git_fs_path_to_dir(git_str *path)
return git_str_oom(path) ? -1 : 0;
}
+size_t git_fs_path_dirlen(const char *path)
+{
+ size_t len = strlen(path);
+
+ while (len > 1 && path[len - 1] == '/')
+ len--;
+
+ return len;
+}
+
void git_fs_path_string_to_dir(char *path, size_t size)
{
size_t end = strlen(path);
diff --git a/src/util/fs_path.h b/src/util/fs_path.h
index e5ca6737818..43f7951adde 100644
--- a/src/util/fs_path.h
+++ b/src/util/fs_path.h
@@ -86,6 +86,29 @@ extern int git_fs_path_to_dir(git_str *path);
*/
extern void git_fs_path_string_to_dir(char *path, size_t size);
+/**
+ * Provides the length of the given path string with no trailing
+ * slashes.
+ */
+size_t git_fs_path_dirlen(const char *path);
+
+/**
+ * Returns nonzero if the given path is a filesystem root; on Windows, this
+ * means a drive letter (eg `A:/`, `C:\`). On POSIX this is `/`.
+ */
+GIT_INLINE(int) git_fs_path_is_root(const char *name)
+{
+#ifdef GIT_WIN32
+ if (((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z')) &&
+ name[1] == ':' &&
+ (name[2] == '/' || name[2] == '\\') &&
+ name[3] == '\0')
+ return 1;
+#endif
+
+ return (name[0] == '/' && name[1] == '\0');
+}
+
/**
* Taken from git.git; returns nonzero if the given path is "." or "..".
*/
diff --git a/src/util/git2_util.h b/src/util/git2_util.h
index 5ec9429344a..5bf09819956 100644
--- a/src/util/git2_util.h
+++ b/src/util/git2_util.h
@@ -165,5 +165,6 @@ typedef struct git_str git_str;
if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }
#include "util.h"
+#include "ctype_compat.h"
#endif
diff --git a/src/util/rand.c b/src/util/rand.c
index 2ed0605738b..a02853519b2 100644
--- a/src/util/rand.c
+++ b/src/util/rand.c
@@ -10,10 +10,6 @@ See . */
#include "rand.h"
#include "runtime.h"
-#if defined(GIT_RAND_GETENTROPY)
-# include
-#endif
-
#if defined(GIT_WIN32)
# include
#endif
diff --git a/src/util/str.c b/src/util/str.c
index 0d405bfda50..0b07c814702 100644
--- a/src/util/str.c
+++ b/src/util/str.c
@@ -485,8 +485,8 @@ int git_str_decode_percent(
for (str_pos = 0; str_pos < str_len; buf->size++, str_pos++) {
if (str[str_pos] == '%' &&
str_len > str_pos + 2 &&
- isxdigit(str[str_pos + 1]) &&
- isxdigit(str[str_pos + 2])) {
+ git__isxdigit(str[str_pos + 1]) &&
+ git__isxdigit(str[str_pos + 2])) {
buf->ptr[buf->size] = (HEX_DECODE(str[str_pos + 1]) << 4) +
HEX_DECODE(str[str_pos + 2]);
str_pos += 2;
diff --git a/src/util/unix/process.c b/src/util/unix/process.c
index 15092cb217f..68c0384a4c4 100644
--- a/src/util/unix/process.c
+++ b/src/util/unix/process.c
@@ -15,7 +15,12 @@
#include "process.h"
#include "strlist.h"
-extern char **environ;
+#ifdef __APPLE__
+ #include
+ #define environ (*_NSGetEnviron())
+#else
+ extern char **environ;
+#endif
struct git_process {
char **args;
diff --git a/src/util/util.h b/src/util/util.h
index 7f178b169fe..2ed005110ef 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -83,15 +83,6 @@ extern char *git__strsep(char **end, const char *sep);
extern void git__strntolower(char *str, size_t len);
extern void git__strtolower(char *str);
-#ifdef GIT_WIN32
-GIT_INLINE(int) git__tolower(int c)
-{
- return (c >= 'A' && c <= 'Z') ? (c + 32) : c;
-}
-#else
-# define git__tolower(a) tolower(a)
-#endif
-
extern size_t git__linenlen(const char *buffer, size_t buffer_len);
GIT_INLINE(const char *) git__next_line(const char *s)
@@ -249,26 +240,6 @@ GIT_INLINE(size_t) git__size_t_powerof2(size_t v)
return git__size_t_bitmask(v) + 1;
}
-GIT_INLINE(bool) git__isupper(int c)
-{
- return (c >= 'A' && c <= 'Z');
-}
-
-GIT_INLINE(bool) git__isalpha(int c)
-{
- return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
-}
-
-GIT_INLINE(bool) git__isdigit(int c)
-{
- return (c >= '0' && c <= '9');
-}
-
-GIT_INLINE(bool) git__isspace(int c)
-{
- return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
-}
-
GIT_INLINE(bool) git__isspace_nonlf(int c)
{
return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v');
@@ -279,11 +250,6 @@ GIT_INLINE(bool) git__iswildcard(int c)
return (c == '*' || c == '?' || c == '[');
}
-GIT_INLINE(bool) git__isxdigit(int c)
-{
- return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
/*
* Parse a string value as a boolean, just like Core Git does.
*
diff --git a/src/util/win32/posix_w32.c b/src/util/win32/posix_w32.c
index 3fec469a648..ace23200f59 100644
--- a/src/util/win32/posix_w32.c
+++ b/src/util/win32/posix_w32.c
@@ -787,13 +787,19 @@ int p_rmdir(const char *path)
char *p_realpath(const char *orig_path, char *buffer)
{
git_win32_path orig_path_w, buffer_w;
+ DWORD long_len;
if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
return NULL;
- /* Note that if the path provided is a relative path, then the current directory
+ /*
+ * POSIX realpath performs two functions: first, it turns relative
+ * paths into absolute paths. For this, we need GetFullPathName.
+ *
+ * Note that if the path provided is a relative path, then the current directory
* is used to resolve the path -- which is a concurrency issue because the current
- * directory is a process-wide variable. */
+ * directory is a process-wide variable.
+ */
if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
errno = ENAMETOOLONG;
@@ -803,9 +809,26 @@ char *p_realpath(const char *orig_path, char *buffer)
return NULL;
}
- /* The path must exist. */
- if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
- errno = ENOENT;
+ /*
+ * Then, the path is canonicalized. eg, on macOS,
+ * "/TMP" -> "/private/tmp". For this, we need GetLongPathName.
+ */
+ if ((long_len = GetLongPathNameW(buffer_w, buffer_w, GIT_WIN_PATH_UTF16)) == 0) {
+ DWORD error = GetLastError();
+
+ if (error == ERROR_FILE_NOT_FOUND ||
+ error == ERROR_PATH_NOT_FOUND)
+ errno = ENOENT;
+ else if (error == ERROR_ACCESS_DENIED)
+ errno = EPERM;
+ else
+ errno = EINVAL;
+
+ return NULL;
+ }
+
+ if (long_len > GIT_WIN_PATH_UTF16) {
+ errno = ENAMETOOLONG;
return NULL;
}
@@ -821,7 +844,6 @@ char *p_realpath(const char *orig_path, char *buffer)
return NULL;
git_fs_path_mkposix(buffer);
-
return buffer;
}
diff --git a/tests/clar/clar/sandbox.h b/tests/clar/clar/sandbox.h
index 0ba1479620a..0688374f8d6 100644
--- a/tests/clar/clar/sandbox.h
+++ b/tests/clar/clar/sandbox.h
@@ -2,7 +2,8 @@
#include
#endif
-static char _clar_path[4096 + 1];
+#define CLAR_PATH_MAX 4096
+static char _clar_path[CLAR_PATH_MAX];
static int
is_valid_tmp_path(const char *path)
@@ -35,10 +36,9 @@ find_tmp_path(char *buffer, size_t length)
continue;
if (is_valid_tmp_path(env)) {
-#ifdef __APPLE__
- if (length >= PATH_MAX && realpath(env, buffer) != NULL)
- return 0;
-#endif
+ if (strlen(env) + 1 > CLAR_PATH_MAX)
+ return -1;
+
strncpy(buffer, env, length - 1);
buffer[length - 1] = '\0';
return 0;
@@ -47,10 +47,6 @@ find_tmp_path(char *buffer, size_t length)
/* If the environment doesn't say anything, try to use /tmp */
if (is_valid_tmp_path("/tmp")) {
-#ifdef __APPLE__
- if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
- return 0;
-#endif
strncpy(buffer, "/tmp", length - 1);
buffer[length - 1] = '\0';
return 0;
@@ -75,6 +71,34 @@ find_tmp_path(char *buffer, size_t length)
return -1;
}
+static int canonicalize_tmp_path(char *buffer)
+{
+#ifdef _WIN32
+ char tmp[CLAR_PATH_MAX];
+ DWORD ret;
+
+ ret = GetFullPathName(buffer, CLAR_PATH_MAX, tmp, NULL);
+
+ if (ret == 0 || ret > CLAR_PATH_MAX)
+ return -1;
+
+ ret = GetLongPathName(tmp, buffer, CLAR_PATH_MAX);
+
+ if (ret == 0 || ret > CLAR_PATH_MAX)
+ return -1;
+
+ return 0;
+#else
+ char tmp[CLAR_PATH_MAX];
+
+ if (realpath(buffer, tmp) == NULL)
+ return -1;
+
+ strcpy(buffer, tmp);
+ return 0;
+#endif
+}
+
static void clar_unsandbox(void)
{
if (_clar_path[0] == '\0')
@@ -95,7 +119,8 @@ static int build_sandbox_path(void)
size_t len;
- if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
+ if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0 ||
+ canonicalize_tmp_path(_clar_path) < 0)
return -1;
len = strlen(_clar_path);
diff --git a/tests/libgit2/clone/local.c b/tests/libgit2/clone/local.c
index e0bd74df78a..d35fe86b27e 100644
--- a/tests/libgit2/clone/local.c
+++ b/tests/libgit2/clone/local.c
@@ -210,3 +210,13 @@ void test_clone_local__git_style_unc_paths(void)
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
#endif
}
+
+void test_clone_local__shallow_fails(void)
+{
+ git_repository *repo;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+
+ opts.fetch_opts.depth = 4;
+
+ cl_git_fail_with(GIT_ENOTSUPPORTED, git_clone(&repo, cl_fixture("testrepo.git"), "./clone.git", &opts));
+}
diff --git a/tests/libgit2/config/configlevel.c b/tests/libgit2/config/configlevel.c
index 8422d32c944..0e31268b0c6 100644
--- a/tests/libgit2/config/configlevel.c
+++ b/tests/libgit2/config/configlevel.c
@@ -71,3 +71,44 @@ void test_config_configlevel__fetching_a_level_from_an_empty_compound_config_ret
git_config_free(cfg);
}
+
+void test_config_configlevel__can_fetch_highest_level(void)
+{
+ git_config *cfg;
+ git_config *single_level_cfg;
+ git_buf buf = {0};
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+
+ cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_HIGHEST_LEVEL));
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_get_string_buf(&buf, single_level_cfg, "core.stringglobal"));
+ cl_assert_equal_s("don't find me!", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(single_level_cfg);
+}
+
+void test_config_configlevel__can_override_local_with_worktree(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_WORKTREE, NULL, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
+ cl_assert_equal_s("don't find me!", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/config/multivar.c b/tests/libgit2/config/multivar.c
index 244e3755965..3ed846012fa 100644
--- a/tests/libgit2/config/multivar.c
+++ b/tests/libgit2/config/multivar.c
@@ -1,4 +1,6 @@
#include "clar_libgit2.h"
+#include "config.h"
+#include "config/config_helpers.h"
static const char *_name = "remote.ab.url";
@@ -286,3 +288,32 @@ void test_config_multivar__delete_notfound(void)
git_config_free(cfg);
}
+
+void test_config_multivar__rename_section(void)
+{
+ git_repository *repo;
+ git_config *cfg;
+ int n;
+
+ repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_config(&cfg, repo));
+
+ cl_git_pass(git_config_set_multivar(cfg, "branch.foo.name", "^$", "bar"));
+ cl_git_pass(git_config_set_multivar(cfg, "branch.foo.name", "^$", "xyzzy"));
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(
+ cfg, "branch.foo.name", NULL, cb, &n));
+ cl_assert(n == 2);
+
+ cl_git_pass(
+ git_config_rename_section(repo, "branch.foo", "branch.foobar"));
+
+ assert_config_entry_existence(repo, "branch.foo.name", false);
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(
+ cfg, "branch.foobar.name", NULL, cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/libgit2/config/readonly.c b/tests/libgit2/config/readonly.c
index a8901e394c0..483f83a85fd 100644
--- a/tests/libgit2/config/readonly.c
+++ b/tests/libgit2/config/readonly.c
@@ -24,7 +24,7 @@ void test_config_readonly__writing_to_readonly_fails(void)
backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
- cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
+ cl_git_fail_with(GIT_EREADONLY, git_config_set_string(cfg, "foo.bar", "baz"));
cl_assert(!git_fs_path_exists("global"));
}
diff --git a/tests/libgit2/config/write.c b/tests/libgit2/config/write.c
index 9d8c3fe9495..c71d4f6dc86 100644
--- a/tests/libgit2/config/write.c
+++ b/tests/libgit2/config/write.c
@@ -696,6 +696,36 @@ void test_config_write__locking(void)
git_config_free(cfg);
}
+void test_config_write__abort_lock(void)
+{
+ git_config *cfg;
+ git_config_entry *entry;
+ git_transaction *tx;
+ const char *filename = "locked-file";
+
+ /* Open the config and lock it */
+ cl_git_mkfile(filename, "[section]\n\tname = value\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_pass(git_config_lock(&tx, cfg));
+
+ /* Change entries in the locked backend */
+ cl_git_pass(git_config_set_string(cfg, "section.name", "other value"));
+ cl_git_pass(git_config_set_string(cfg, "section2.name3", "more value"));
+
+ git_transaction_free(tx);
+
+ /* Now that we've unlocked it, we should see no changes */
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3"));
+
+ git_config_free(cfg);
+}
+
void test_config_write__repeated(void)
{
const char *filename = "config-repeated";
diff --git a/tests/libgit2/core/opts.c b/tests/libgit2/core/opts.c
index 1aa095adf4c..cbef29f991d 100644
--- a/tests/libgit2/core/opts.c
+++ b/tests/libgit2/core/opts.c
@@ -34,9 +34,10 @@ void test_core_opts__extensions_query(void)
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
- cl_assert_equal_sz(out.count, 2);
+ cl_assert_equal_sz(out.count, 3);
cl_assert_equal_s("noop", out.strings[0]);
cl_assert_equal_s("objectformat", out.strings[1]);
+ cl_assert_equal_s("worktreeconfig", out.strings[2]);
git_strarray_dispose(&out);
}
@@ -49,10 +50,11 @@ void test_core_opts__extensions_add(void)
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
- cl_assert_equal_sz(out.count, 3);
+ cl_assert_equal_sz(out.count, 4);
cl_assert_equal_s("foo", out.strings[0]);
cl_assert_equal_s("noop", out.strings[1]);
cl_assert_equal_s("objectformat", out.strings[2]);
+ cl_assert_equal_s("worktreeconfig", out.strings[3]);
git_strarray_dispose(&out);
}
@@ -65,10 +67,11 @@ void test_core_opts__extensions_remove(void)
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
- cl_assert_equal_sz(out.count, 3);
+ cl_assert_equal_sz(out.count, 4);
cl_assert_equal_s("bar", out.strings[0]);
cl_assert_equal_s("baz", out.strings[1]);
cl_assert_equal_s("objectformat", out.strings[2]);
+ cl_assert_equal_s("worktreeconfig", out.strings[3]);
git_strarray_dispose(&out);
}
@@ -81,11 +84,12 @@ void test_core_opts__extensions_uniq(void)
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
- cl_assert_equal_sz(out.count, 4);
+ cl_assert_equal_sz(out.count, 5);
cl_assert_equal_s("bar", out.strings[0]);
cl_assert_equal_s("foo", out.strings[1]);
cl_assert_equal_s("noop", out.strings[2]);
cl_assert_equal_s("objectformat", out.strings[3]);
+ cl_assert_equal_s("worktreeconfig", out.strings[4]);
git_strarray_dispose(&out);
}
diff --git a/tests/libgit2/core/useragent.c b/tests/libgit2/core/useragent.c
index a4ece902fd9..2e119de4490 100644
--- a/tests/libgit2/core/useragent.c
+++ b/tests/libgit2/core/useragent.c
@@ -1,17 +1,52 @@
#include "clar_libgit2.h"
#include "settings.h"
-void test_core_useragent__get(void)
+static git_buf default_ua = GIT_BUF_INIT;
+static git_buf default_product = GIT_BUF_INIT;
+
+void test_core_useragent__initialize(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &default_ua));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT_PRODUCT, &default_product));
+}
+
+void test_core_useragent__cleanup(void)
+{
+ git_libgit2_opts(GIT_OPT_SET_USER_AGENT, NULL);
+ git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, NULL);
+
+ git_buf_dispose(&default_ua);
+ git_buf_dispose(&default_product);
+}
+
+void test_core_useragent__get_default(void)
+{
+ cl_assert(default_ua.size);
+ cl_assert(default_ua.ptr);
+ cl_assert(git__prefixcmp(default_ua.ptr, "libgit2 ") == 0);
+
+ cl_assert(default_product.size);
+ cl_assert(default_product.ptr);
+ cl_assert(git__prefixcmp(default_product.ptr, "git/") == 0);
+}
+
+void test_core_useragent__set(void)
{
- const char *custom_name = "super duper git";
- git_str buf = GIT_STR_INIT;
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, "foo bar 4.24"));
+ cl_assert_equal_s("foo bar 4.24", git_settings__user_agent());
+ cl_assert_equal_s(default_product.ptr, git_settings__user_agent_product());
- cl_assert_equal_p(NULL, git_libgit2__user_agent());
- cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name));
- cl_assert_equal_s(custom_name, git_libgit2__user_agent());
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, "baz/2.2.3"));
+ cl_assert_equal_s("foo bar 4.24", git_settings__user_agent());
+ cl_assert_equal_s("baz/2.2.3", git_settings__user_agent_product());
- cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &buf));
- cl_assert_equal_s(custom_name, buf.ptr);
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, ""));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, ""));
+ cl_assert_equal_s("", git_settings__user_agent());
+ cl_assert_equal_s("", git_settings__user_agent_product());
- git_str_dispose(&buf);
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, NULL));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, NULL));
+ cl_assert_equal_s(default_ua.ptr, git_settings__user_agent());
+ cl_assert_equal_s(default_product.ptr, git_settings__user_agent_product());
}
diff --git a/tests/libgit2/diff/parse.c b/tests/libgit2/diff/parse.c
index 79745b99503..59fc0280ed6 100644
--- a/tests/libgit2/diff/parse.c
+++ b/tests/libgit2/diff/parse.c
@@ -279,6 +279,31 @@ static int file_cb(const git_diff_delta *delta, float progress, void *payload)
return 0;
}
+void test_diff_parse__eof_nl_missing(void)
+{
+ const char patch[] =
+ "diff --git a/.env b/.env\n"
+ "index f89e4c0..7c56eb7 100644\n"
+ "--- a/.env\n"
+ "+++ b/.env\n"
+ "@@ -1 +1 @@\n"
+ "-hello=12345\n"
+ "+hello=123456\n"
+ "\\ No newline at end of file\n";
+ git_diff *diff;
+ git_patch *ret_patch;
+ git_diff_line *line;
+
+ cl_git_pass(diff_from_buffer(&diff, patch, strlen(patch)));
+ cl_git_pass(git_patch_from_diff(&ret_patch, diff, 0));
+
+ cl_assert((line = git_array_get(ret_patch->lines, 2)) != NULL);
+ cl_assert(line->origin == GIT_DIFF_LINE_DEL_EOFNL);
+
+ git_diff_free(diff);
+ git_patch_free(ret_patch);
+}
+
void test_diff_parse__foreach_works_with_parsed_patch(void)
{
const char patch[] =
diff --git a/tests/libgit2/diff/workdir.c b/tests/libgit2/diff/workdir.c
index c433b75cedc..504ece6fc91 100644
--- a/tests/libgit2/diff/workdir.c
+++ b/tests/libgit2/diff/workdir.c
@@ -2286,42 +2286,81 @@ void test_diff_workdir__to_index_reversed_content_loads(void)
diff_expects exp;
int use_iterator;
char *pathspec = "new_file";
-
+
g_repo = cl_git_sandbox_init("status");
-
+
opts.context_lines = 3;
opts.interhunk_lines = 1;
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_SHOW_UNTRACKED_CONTENT | GIT_DIFF_REVERSE;
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1;
-
+
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
+
for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
memset(&exp, 0, sizeof(exp));
-
+
if (use_iterator)
cl_git_pass(diff_foreach_via_iterator(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
else
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
-
+
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
-
+
cl_assert_equal_i(1, exp.hunks);
-
+
cl_assert_equal_i(1, exp.lines);
cl_assert_equal_i(0, exp.line_ctxt);
cl_assert_equal_i(0, exp.line_adds);
cl_assert_equal_i(1, exp.line_dels);
}
-
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__completely_ignored_shows_empty_diff(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+ char *pathspec = "subdir.txt";
+
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ g_repo = cl_git_sandbox_init("status");
+ cl_git_rewritefile("status/subdir.txt", "Is it a bird?\n\nIs it a plane?\n");
+
+ /* Perform the diff normally */
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s("diff --git a/subdir.txt b/subdir.txt\nindex e8ee89e..53c8db5 100644\n--- a/subdir.txt\n+++ b/subdir.txt\n@@ -1,2 +1,3 @@\n Is it a bird?\n+\n Is it a plane?\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* Perform the diff ignoring blank lines */
+ opts.flags |= GIT_DIFF_IGNORE_BLANK_LINES;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s("", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
git_diff_free(diff);
}
diff --git a/tests/libgit2/merge/trees/renames.c b/tests/libgit2/merge/trees/renames.c
index a27945ee071..9507b51bc9e 100644
--- a/tests/libgit2/merge/trees/renames.c
+++ b/tests/libgit2/merge/trees/renames.c
@@ -350,3 +350,22 @@ void test_merge_trees_renames__cache_recomputation(void)
git_tree_free(our_tree);
git__free(data);
}
+
+void test_merge_trees_renames__emptyfile_renames(void)
+{
+ git_index *index;
+ git_merge_options *opts = NULL;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 1, "bar" },
+ { 0100644, "60b12be2d2f57977ce83d8dfd32e2394ac1ba1a2", 3, "bar" },
+ { 0100644, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 0, "boo" },
+ { 0100644, "e50a49f9558d09d4d3bfc108363bb24c127ed263", 0, "foo" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo,
+ "emptyfile_renames", "emptyfile_renames-branch",
+ opts));
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+ git_index_free(index);
+}
diff --git a/tests/libgit2/message/trailer.c b/tests/libgit2/message/trailer.c
index 919e10a499c..09e8f6115a7 100644
--- a/tests/libgit2/message/trailer.c
+++ b/tests/libgit2/message/trailer.c
@@ -3,19 +3,22 @@
static void assert_trailers(const char *message, git_message_trailer *trailers)
{
git_message_trailer_array arr;
- size_t i;
+ size_t i, count;
int rc = git_message_trailers(&arr, message);
cl_assert_equal_i(0, rc);
- for(i=0; i 256);
git_str_dispose(&path);
git_str_dispose(&buf);
-
- git_hash_fmt(hex, hash, GIT_HASH_SHA1_SIZE);
- cl_assert_equal_s(hex, "5d410bdf97cf896f9007681b92868471d636954b");
}
void test_pack_packbuilder__get_name(void)
@@ -148,22 +125,49 @@ void test_pack_packbuilder__get_name(void)
seed_packbuilder();
cl_git_pass(git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL));
- cl_assert_equal_s("7f5fa362c664d68ba7221259be1cbd187434b2f0", git_packbuilder_name(_packbuilder));
+ cl_assert(git_packbuilder_name(_packbuilder) != NULL);
+}
+
+static void get_packfile_path(git_str *out, git_packbuilder *pb)
+{
+ git_str_puts(out, "pack-");
+ git_str_puts(out, git_packbuilder_name(pb));
+ git_str_puts(out, ".pack");
+}
+
+static void get_index_path(git_str *out, git_packbuilder *pb)
+{
+ git_str_puts(out, "pack-");
+ git_str_puts(out, git_packbuilder_name(pb));
+ git_str_puts(out, ".idx");
}
void test_pack_packbuilder__write_default_path(void)
{
+ git_str idx = GIT_STR_INIT, pack = GIT_STR_INIT;
+
seed_packbuilder();
cl_git_pass(git_packbuilder_write(_packbuilder, NULL, 0, NULL, NULL));
- cl_assert(git_fs_path_exists("objects/pack/pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.idx"));
- cl_assert(git_fs_path_exists("objects/pack/pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack"));
+
+ git_str_puts(&idx, "objects/pack/");
+ get_index_path(&idx, _packbuilder);
+
+ git_str_puts(&pack, "objects/pack/");
+ get_packfile_path(&pack, _packbuilder);
+
+ cl_assert(git_fs_path_exists(idx.ptr));
+ cl_assert(git_fs_path_exists(pack.ptr));
+
+ git_str_dispose(&idx);
+ git_str_dispose(&pack);
}
static void test_write_pack_permission(mode_t given, mode_t expected)
{
struct stat statbuf;
mode_t mask, os_mask;
+ git_str idx = GIT_STR_INIT, pack = GIT_STR_INIT;
seed_packbuilder();
@@ -181,11 +185,17 @@ static void test_write_pack_permission(mode_t given, mode_t expected)
mask = p_umask(0);
p_umask(mask);
- cl_git_pass(p_stat("pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.idx", &statbuf));
+ get_index_path(&idx, _packbuilder);
+ get_packfile_path(&pack, _packbuilder);
+
+ cl_git_pass(p_stat(idx.ptr, &statbuf));
cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
- cl_git_pass(p_stat("pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack", &statbuf));
+ cl_git_pass(p_stat(pack.ptr, &statbuf));
cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+
+ git_str_dispose(&idx);
+ git_str_dispose(&pack);
}
void test_pack_packbuilder__permissions_standard(void)
diff --git a/tests/libgit2/refs/branches/delete.c b/tests/libgit2/refs/branches/delete.c
index 6b3d507a869..63f8c5d95c7 100644
--- a/tests/libgit2/refs/branches/delete.c
+++ b/tests/libgit2/refs/branches/delete.c
@@ -92,6 +92,21 @@ void test_refs_branches_delete__can_delete_a_local_branch(void)
git_reference_free(branch);
}
+void test_refs_branches_delete__can_delete_a_local_branch_with_multivar(void)
+{
+ git_reference *branch;
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_multivar(
+ cfg, "branch.br2.gitpublishto", "^$", "example1@example.com"));
+ cl_git_pass(git_config_set_multivar(
+ cfg, "branch.br2.gitpublishto", "^$", "example2@example.com"));
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
void test_refs_branches_delete__can_delete_a_remote_branch(void)
{
git_reference *branch;
diff --git a/tests/libgit2/refs/revparse.c b/tests/libgit2/refs/revparse.c
index d2f464840a0..3fe07811796 100644
--- a/tests/libgit2/refs/revparse.c
+++ b/tests/libgit2/refs/revparse.c
@@ -889,3 +889,15 @@ void test_refs_revparse__parses_at_head(void)
test_id("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE);
test_id("@", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE);
}
+
+void test_refs_revparse__rejects_bogus_at(void)
+{
+ git_repository *repo;
+ git_object *target;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_revparse_single(&target, repo, "foo@"));
+
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/libgit2/repo/init.c b/tests/libgit2/repo/init.c
index d78ec063cd2..446ab735e4e 100644
--- a/tests/libgit2/repo/init.c
+++ b/tests/libgit2/repo/init.c
@@ -755,3 +755,28 @@ void test_repo_init__longpath(void)
git_str_dispose(&path);
#endif
}
+
+void test_repo_init__absolute_path_with_backslashes(void)
+{
+#ifdef GIT_WIN32
+ git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_str path = GIT_STR_INIT;
+ char *c;
+
+ cl_set_cleanup(&cleanup_repository, "path");
+
+ cl_git_pass(git_str_joinpath(&path, clar_sandbox_path(), "path/to/newrepo"));
+
+ for (c = path.ptr; *c; c++) {
+ if (*c == '/')
+ *c = '\\';
+ }
+
+ initopts.flags |= GIT_REPOSITORY_INIT_MKDIR | GIT_REPOSITORY_INIT_MKPATH;
+
+ cl_git_pass(git_repository_init_ext(&g_repo, path.ptr, &initopts));
+ git_str_dispose(&path);
+#else
+ clar__skip();
+#endif
+}
diff --git a/tests/libgit2/repo/open.c b/tests/libgit2/repo/open.c
index 9c0bfde7b57..d58551343e1 100644
--- a/tests/libgit2/repo/open.c
+++ b/tests/libgit2/repo/open.c
@@ -316,7 +316,7 @@ static void unposix_path(git_str *path)
src = tgt = path->ptr;
/* convert "/d/..." to "d:\..." */
- if (src[0] == '/' && isalpha(src[1]) && src[2] == '/') {
+ if (src[0] == '/' && git__isalpha(src[1]) && src[2] == '/') {
*tgt++ = src[1];
*tgt++ = ':';
*tgt++ = '\\';
@@ -533,15 +533,21 @@ void test_repo_open__validates_bare_repo_ownership(void)
cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
}
-void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
+static int test_safe_path(const char *path)
{
git_repository *repo;
git_str config_path = GIT_STR_INIT,
config_filename = GIT_STR_INIT,
config_data = GIT_STR_INIT;
+ int error;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+ /*
+ * Sandbox the fixture, and ensure that when we fake an owner
+ * of "other" that the repository cannot be opened (and fails
+ * with `GIT_EOWNER`).
+ */
cl_fixture_sandbox("empty_standard_repo");
cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
@@ -555,25 +561,50 @@ void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
- /* Test with incorrect exception (slash at the end) */
+ git_str_clear(&config_data);
git_str_printf(&config_data,
"[foo]\n" \
"\tbar = Foobar\n" \
"\tbaz = Baz!\n" \
"[safe]\n" \
- "\tdirectory = /non/existent/path\n" \
- "\tdirectory = /\n" \
- "\tdirectory = c:\\\\temp\n" \
- "\tdirectory = %s/%s/\n" \
- "\tdirectory = /tmp\n" \
+ "\tdirectory = %s\n" \
"[bar]\n" \
"\tfoo = barfoo\n",
- clar_sandbox_path(), "empty_standard_repo");
+ path);
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
- cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
- /* Test with correct exception */
- git_str_clear(&config_data);
+ error = git_repository_open(&repo, "empty_standard_repo");
+ git_repository_free(repo);
+
+ git_str_dispose(&config_path);
+ git_str_dispose(&config_filename);
+ git_str_dispose(&config_data);
+
+ return error;
+}
+
+static int test_bare_safe_path(const char *path)
+{
+ git_repository *repo;
+ git_str config_path = GIT_STR_INIT,
+ config_filename = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
+ int error;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+
+ cl_fixture_sandbox("testrepo.git");
+
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+
+ /* Add safe.directory options to the global configuration */
+ git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
+ cl_must_pass(p_mkdir(config_path.ptr, 0777));
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
+
+ git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+
git_str_printf(&config_data,
"[foo]\n" \
"\tbar = Foobar\n" \
@@ -582,73 +613,127 @@ void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
"\tdirectory = /non/existent/path\n" \
"\tdirectory = /\n" \
"\tdirectory = c:\\\\temp\n" \
- "\tdirectory = %s/%s\n" \
+ "\tdirectory = %s\n" \
"\tdirectory = /tmp\n" \
"[bar]\n" \
"\tfoo = barfoo\n",
- clar_sandbox_path(), "empty_standard_repo");
+ path);
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ error = git_repository_open(&repo, "testrepo.git");
git_repository_free(repo);
git_str_dispose(&config_path);
git_str_dispose(&config_filename);
git_str_dispose(&config_data);
+
+ return error;
}
-void test_repo_open__can_wildcard_allowlist_with_problematic_ownership(void)
+void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
{
- git_repository *repo;
- git_str config_path = GIT_STR_INIT, config_filename = GIT_STR_INIT;
+ git_str path = GIT_STR_INIT;
- cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+ cl_git_pass(git_str_printf(&path, "%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+ cl_git_pass(test_safe_path(path.ptr));
+ git_str_dispose(&path);
+}
- cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(cl_rename(
- "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+void test_repo_open__safe_directory_fails_with_trailing_slash(void)
+{
+ git_str path = GIT_STR_INIT;
- git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
- cl_git_fail_with(
- GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
+ /*
+ * "/tmp/foo/" is not permitted; safe path must be specified
+ * as "/tmp/foo"
+ */
+ cl_git_pass(git_str_printf(&path, "%s/%s/",
+ clar_sandbox_path(), "empty_standard_repo"));
+ cl_git_fail_with(GIT_EOWNER, test_safe_path(path.ptr));
+ git_str_dispose(&path);
+}
- /* Add safe.directory options to the global configuration */
- git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
- cl_must_pass(p_mkdir(config_path.ptr, 0777));
- git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
- config_path.ptr);
+void test_repo_open__can_wildcard_allowlist_with_problematic_ownership(void)
+{
+ cl_git_pass(test_safe_path("*"));
+}
- git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+void test_repo_open__can_allowlist_bare_gitdir(void)
+{
+ git_str path = GIT_STR_INIT;
- cl_git_rewritefile(config_filename.ptr, "[foo]\n"
- "\tbar = Foobar\n"
- "\tbaz = Baz!\n"
- "[safe]\n"
- "\tdirectory = *\n"
- "[bar]\n"
- "\tfoo = barfoo\n");
+ cl_git_pass(git_str_printf(&path, "%s/%s",
+ clar_sandbox_path(), "testrepo.git"));
+ cl_git_pass(test_bare_safe_path(path.ptr));
+ git_str_dispose(&path);
+}
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- git_repository_free(repo);
+void test_repo_open__can_wildcard_allowlist_bare_gitdir(void)
+{
+ cl_git_pass(test_bare_safe_path("*"));
+}
- git_str_dispose(&config_path);
- git_str_dispose(&config_filename);
+void test_repo_open__can_handle_prefixed_safe_paths(void)
+{
+#ifndef GIT_WIN32
+ git_str path = GIT_STR_INIT;
+
+ /*
+ * Using "%(prefix)/" becomes "%(prefix)//tmp/foo" - so
+ * "%(prefix)/" is stripped and means the literal path
+ * follows.
+ */
+ cl_git_pass(git_str_printf(&path, "%%(prefix)/%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+ cl_git_pass(test_safe_path(path.ptr));
+ git_str_dispose(&path);
+#endif
}
-void test_repo_open__can_allowlist_bare_gitdir(void)
+void test_repo_open__prefixed_safe_paths_must_have_two_slashes(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ /*
+ * Using "%(prefix)" becomes "%(prefix)/tmp/foo" - so it's
+ * actually trying to look in the git prefix, for example,
+ * "/usr/local/tmp/foo", which we don't actually support.
+ */
+ cl_git_pass(git_str_printf(&path, "%%(prefix)%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+ cl_git_fail_with(GIT_EOWNER, test_safe_path(path.ptr));
+ git_str_dispose(&path);
+}
+
+void test_repo_open__can_handle_win32_prefixed_safe_paths(void)
{
+#ifdef GIT_WIN32
git_repository *repo;
- git_str config_path = GIT_STR_INIT,
+ git_str unc_path = GIT_STR_INIT,
+ config_path = GIT_STR_INIT,
config_filename = GIT_STR_INIT,
config_data = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
- cl_fixture_sandbox("testrepo.git");
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ /*
+ * On Windows, we can generally map a local drive to a UNC path;
+ * for example C:\Foo\Bar becomes //localhost/C$/Foo/bar
+ */
+ cl_git_pass(git_str_printf(&unc_path, "//localhost/%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+
+ if (unc_path.ptr[13] != ':' || unc_path.ptr[14] != '/')
+ cl_skip();
+
+ unc_path.ptr[13] = '$';
git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
- cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, unc_path.ptr));
/* Add safe.directory options to the global configuration */
git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
@@ -657,64 +742,74 @@ void test_repo_open__can_allowlist_bare_gitdir(void)
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+ /* The blank resets our sandbox directory and opening fails */
+
git_str_printf(&config_data,
- "[foo]\n" \
- "\tbar = Foobar\n" \
- "\tbaz = Baz!\n" \
- "[safe]\n" \
- "\tdirectory = /non/existent/path\n" \
- "\tdirectory = /\n" \
- "\tdirectory = c:\\\\temp\n" \
- "\tdirectory = %s/%s\n" \
- "\tdirectory = /tmp\n" \
- "[bar]\n" \
- "\tfoo = barfoo\n",
- clar_sandbox_path(), "testrepo.git");
+ "[safe]\n\tdirectory = %%(prefix)/%s\n",
+ unc_path.ptr);
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ cl_git_pass(git_repository_open(&repo, unc_path.ptr));
git_repository_free(repo);
git_str_dispose(&config_path);
git_str_dispose(&config_filename);
git_str_dispose(&config_data);
+ git_str_dispose(&unc_path);
+#endif
}
-void test_repo_open__can_wildcard_allowlist_bare_gitdir(void)
+void test_repo_open__can_handle_win32_unc_safe_paths(void)
{
+#ifdef GIT_WIN32
git_repository *repo;
- git_str config_path = GIT_STR_INIT, config_filename = GIT_STR_INIT;
+ git_str unc_path = GIT_STR_INIT,
+ config_path = GIT_STR_INIT,
+ config_filename = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
- cl_fixture_sandbox("testrepo.git");
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ /*
+ * On Windows, we can generally map a local drive to a UNC path;
+ * for example C:\Foo\Bar becomes //localhost/C$/Foo/bar
+ */
+ cl_git_pass(git_str_printf(&unc_path, "//localhost/%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+
+ if (unc_path.ptr[13] != ':' || unc_path.ptr[14] != '/')
+ cl_skip();
+
+ unc_path.ptr[13] = '$';
git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
- cl_git_fail_with(
- GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, unc_path.ptr));
/* Add safe.directory options to the global configuration */
git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
cl_must_pass(p_mkdir(config_path.ptr, 0777));
- git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
- config_path.ptr);
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
- cl_git_rewritefile(config_filename.ptr, "[foo]\n"
- "\tbar = Foobar\n"
- "\tbaz = Baz!\n"
- "[safe]\n"
- "\tdirectory = *\n"
- "[bar]\n"
- "\tfoo = barfoo\n");
+ /* The blank resets our sandbox directory and opening fails */
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ git_str_printf(&config_data,
+ "[safe]\n\tdirectory = %s\n",
+ unc_path.ptr);
+ cl_git_rewritefile(config_filename.ptr, config_data.ptr);
+
+ cl_git_pass(git_repository_open(&repo, unc_path.ptr));
git_repository_free(repo);
git_str_dispose(&config_path);
git_str_dispose(&config_filename);
+ git_str_dispose(&config_data);
+ git_str_dispose(&unc_path);
+#endif
}
void test_repo_open__can_reset_safe_directory_list(void)
diff --git a/tests/libgit2/status/worktree.c b/tests/libgit2/status/worktree.c
index efbf597a723..8a2ea9cb674 100644
--- a/tests/libgit2/status/worktree.c
+++ b/tests/libgit2/status/worktree.c
@@ -1360,3 +1360,13 @@ void test_status_worktree__at_head_parent(void)
git_tree_free(parent_tree);
git_status_list_free(statuslist);
}
+
+void test_status_worktree__skip_hash(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status_skiphash");
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_read(index, true));
+ git_index_free(index);
+}
diff --git a/tests/libgit2/submodule/lookup.c b/tests/libgit2/submodule/lookup.c
index febb7dfad7d..14a624badef 100644
--- a/tests/libgit2/submodule/lookup.c
+++ b/tests/libgit2/submodule/lookup.c
@@ -401,6 +401,24 @@ void test_submodule_lookup__prefix_name(void)
git_submodule_free(sm);
}
+/* ".path" in name of submodule */
+void test_submodule_lookup__dotpath_in_name(void)
+{
+ sm_lookup_data data;
+
+ cl_git_rewritefile(
+ "submod2/.gitmodules", "[submodule \"kwb.pathdict\"]\n"
+ " path = kwb.pathdict\n"
+ " url = ../Test_App\n"
+ "[submodule \"fakin.path.app\"]\n"
+ " path = fakin.path.app\n"
+ " url = ../Test_App\n");
+
+ memset(&data, 0, sizeof(data));
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(9, data.count);
+}
+
void test_submodule_lookup__renamed(void)
{
const char *newpath = "sm_actually_changed";
diff --git a/tests/libgit2/trace/trace.c b/tests/libgit2/trace/trace.c
index 097208bffd6..9fea57668a2 100644
--- a/tests/libgit2/trace/trace.c
+++ b/tests/libgit2/trace/trace.c
@@ -32,16 +32,11 @@ void test_trace_trace__cleanup(void)
void test_trace_trace__sets(void)
{
-#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__can_reset(void)
{
-#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
cl_git_pass(git_trace_set(GIT_TRACE_ERROR, trace_callback));
@@ -51,14 +46,10 @@ void test_trace_trace__can_reset(void)
git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
cl_assert(written == 1);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__can_unset(void)
{
-#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
cl_git_pass(git_trace_set(GIT_TRACE_NONE, NULL));
@@ -67,40 +58,25 @@ void test_trace_trace__can_unset(void)
cl_assert(written == 0);
git_trace(GIT_TRACE_FATAL, "Hello %s!", "world");
cl_assert(written == 0);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__skips_higher_level(void)
{
-#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_DEBUG, "Hello %s!", "world");
cl_assert(written == 0);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__writes(void)
{
-#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_INFO, "Hello %s!", "world");
cl_assert(written == 1);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__writes_lower_level(void)
{
-#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
cl_assert(written == 1);
-#else
- cl_skip();
-#endif
}
diff --git a/tests/libgit2/worktree/config.c b/tests/libgit2/worktree/config.c
index 81dcfe1fa51..1fd1f75b47b 100644
--- a/tests/libgit2/worktree/config.c
+++ b/tests/libgit2/worktree/config.c
@@ -6,15 +6,19 @@
static worktree_fixture fixture =
WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+static worktree_fixture submodule =
+ WORKTREE_FIXTURE_INIT("submodules", "submodules-worktree-parent");
void test_worktree_config__initialize(void)
{
setup_fixture_worktree(&fixture);
+ setup_fixture_worktree(&submodule);
}
void test_worktree_config__cleanup(void)
{
cleanup_fixture_worktree(&fixture);
+ cleanup_fixture_worktree(&submodule);
}
void test_worktree_config__open(void)
@@ -27,7 +31,7 @@ void test_worktree_config__open(void)
git_config_free(cfg);
}
-void test_worktree_config__set(void)
+void test_worktree_config__set_level_local(void)
{
git_config *cfg;
int32_t val;
@@ -45,3 +49,78 @@ void test_worktree_config__set(void)
cl_assert_equal_i(val, 5);
git_config_free(cfg);
}
+
+void test_worktree_config__requires_extension(void)
+{
+ git_config *cfg;
+ git_config *wtcfg;
+ int extension = 0;
+
+ /*
+ * the "submodules" repo does not have extensions.worktreeconfig
+ * set, the worktree configuration should not be available.
+ */
+ cl_git_pass(git_repository_config(&cfg, submodule.repo));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&extension, cfg, "extensions.worktreeconfig"));
+ cl_assert_equal_i(0, extension);
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_open_level(&wtcfg, cfg, GIT_CONFIG_LEVEL_WORKTREE));
+ git_config_free(cfg);
+
+ /* the "testrepo" repo does have the configuration set. */
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_config_get_bool(&extension, cfg, "extensions.worktreeconfig"));
+ cl_assert_equal_i(1, extension);
+ cl_git_pass(git_config_open_level(&wtcfg, cfg, GIT_CONFIG_LEVEL_WORKTREE));
+ git_config_free(wtcfg);
+ git_config_free(cfg);
+}
+
+void test_worktree_config__exists(void)
+{
+ git_config *cfg, *wtcfg, *snap;
+ const char *str;
+
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_repository_config(&wtcfg, fixture.worktree));
+
+ cl_git_pass(git_config_snapshot(&snap, cfg));
+ cl_git_pass(git_config_get_string(&str, snap, "worktreetest.config"));
+ cl_assert_equal_s("mainrepo", str);
+ git_config_free(snap);
+
+ cl_git_pass(git_config_snapshot(&snap, wtcfg));
+ cl_git_pass(git_config_get_string(&str, snap, "worktreetest.config"));
+ cl_assert_equal_s("worktreerepo", str);
+ git_config_free(snap);
+
+ git_config_free(cfg);
+ git_config_free(wtcfg);
+}
+
+void test_worktree_config__set_level_worktree(void)
+{
+ git_config *cfg;
+ git_config *wtcfg;
+ int32_t val;
+
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_config_open_level(&wtcfg, cfg, GIT_CONFIG_LEVEL_WORKTREE));
+ cl_git_pass(git_config_set_int32(wtcfg, "worktree.specific", 42));
+
+ cl_git_pass(git_config_get_int32(&val, cfg, "worktree.specific"));
+ cl_assert_equal_i(val, 42);
+
+ /* reopen to verify config has been set */
+ git_config_free(cfg);
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_config_get_int32(&val, cfg, "worktree.specific"));
+ cl_assert_equal_i(val, 42);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_delete_entry(cfg, "worktree.specific"));
+
+ cl_git_pass(git_config_delete_entry(wtcfg, "worktree.specific"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_int32(&val, cfg, "worktree.specific"));
+
+ git_config_free(cfg);
+ git_config_free(wtcfg);
+}
diff --git a/tests/libgit2/worktree/refs.c b/tests/libgit2/worktree/refs.c
index 557726aafb6..51e7b2b9463 100644
--- a/tests/libgit2/worktree/refs.c
+++ b/tests/libgit2/worktree/refs.c
@@ -20,7 +20,7 @@ void test_worktree_refs__cleanup(void)
cleanup_fixture_worktree(&fixture);
}
-void test_worktree_refs__list(void)
+void test_worktree_refs__list_no_difference_in_worktree(void)
{
git_strarray refs, wtrefs;
unsigned i, j;
@@ -61,6 +61,66 @@ void test_worktree_refs__list(void)
cl_git_pass(error);
}
+void test_worktree_refs__list_worktree_specific(void)
+{
+ git_strarray refs, wtrefs;
+ git_reference *ref, *new_branch;
+ int error = 0;
+ git_oid oid;
+
+ cl_git_pass(git_reference_name_to_id(&oid, fixture.repo, "refs/heads/dir"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, fixture.repo, "refs/bisect/a-bisect-ref"));
+ cl_git_pass(git_reference_create(
+ &new_branch, fixture.worktree, "refs/bisect/a-bisect-ref", &oid,
+ 0, "test"));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, fixture.repo, "refs/bisect/a-bisect-ref"));
+ cl_git_pass(git_reference_lookup(&ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
+
+ cl_git_pass(git_reference_list(&refs, fixture.repo));
+ cl_git_pass(git_reference_list(&wtrefs, fixture.worktree));
+
+ cl_assert_equal_sz(wtrefs.count, refs.count + 1);
+
+ git_reference_free(ref);
+ git_reference_free(new_branch);
+ git_strarray_dispose(&refs);
+ git_strarray_dispose(&wtrefs);
+ cl_git_pass(error);
+}
+
+void test_worktree_refs__list_worktree_specific_hidden_in_main_repo(void)
+{
+ git_strarray refs, wtrefs;
+ git_reference *ref, *new_branch;
+ int error = 0;
+ git_oid oid;
+
+ cl_git_pass(
+ git_reference_name_to_id(&oid, fixture.repo, "refs/heads/dir"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(
+ &ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
+ cl_git_pass(git_reference_create(
+ &new_branch, fixture.repo, "refs/bisect/a-bisect-ref", &oid,
+ 0, "test"));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(
+ &ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
+ cl_git_pass(git_reference_lookup(
+ &ref, fixture.repo, "refs/bisect/a-bisect-ref"));
+
+ cl_git_pass(git_reference_list(&refs, fixture.repo));
+ cl_git_pass(git_reference_list(&wtrefs, fixture.worktree));
+
+ cl_assert_equal_sz(refs.count, wtrefs.count + 1);
+
+ git_reference_free(ref);
+ git_reference_free(new_branch);
+ git_strarray_dispose(&refs);
+ git_strarray_dispose(&wtrefs);
+ cl_git_pass(error);
+}
+
void test_worktree_refs__read_head(void)
{
git_reference *head;
diff --git a/tests/libgit2/worktree/worktree.c b/tests/libgit2/worktree/worktree.c
index fed5c9259ca..00e3e3fe791 100644
--- a/tests/libgit2/worktree/worktree.c
+++ b/tests/libgit2/worktree/worktree.c
@@ -217,6 +217,50 @@ void test_worktree_worktree__init(void)
git_repository_free(repo);
}
+void test_worktree_worktree__add_remove_add(void)
+{
+ git_worktree_add_options add_opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
+ git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_str path = GIT_BUF_INIT;
+ git_reference *branch;
+ git_repository *repo;
+ git_worktree *wt;
+
+ /* Add the worktree */
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-add-remove-add"));
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, NULL));
+
+ /* Open and verify created repo */
+ cl_git_pass(git_repository_open(&repo, path.ptr));
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
+ cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
+ git_reference_free(branch);
+ git_repository_free(repo);
+
+ /* Prune the worktree */
+ opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE;
+ cl_git_pass(git_worktree_prune(wt, &opts));
+ cl_assert(!git_fs_path_exists(wt->gitdir_path));
+ cl_assert(!git_fs_path_exists(wt->gitlink_path));
+ git_worktree_free(wt);
+
+ /* Add the worktree back with default options should fail. */
+ cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
+ /* If allowing checkout of existing branches, it should succeed. */
+ add_opts.checkout_existing = 1;
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
+
+ /* Open and verify created repo */
+ cl_git_pass(git_repository_open(&repo, path.ptr));
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
+ cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
+ git_reference_free(branch);
+ git_repository_free(repo);
+
+ git_str_dispose(&path);
+ git_worktree_free(wt);
+}
+
void test_worktree_worktree__add_locked(void)
{
git_worktree *wt;
@@ -244,6 +288,7 @@ void test_worktree_worktree__add_locked(void)
void test_worktree_worktree__init_existing_branch(void)
{
+ git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
git_reference *head, *branch;
git_commit *commit;
git_worktree *wt;
@@ -251,12 +296,18 @@ void test_worktree_worktree__init_existing_branch(void)
cl_git_pass(git_repository_head(&head, fixture.repo));
cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
- cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false));
+ cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new-exist", commit, false));
- cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
- cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new-exist"));
+
+ /* Add the worktree back with default options should fail. */
+ cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, NULL));
+ /* If allowing checkout of existing branches, it should succeed. */
+ opts.checkout_existing = 1;
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, &opts));
git_str_dispose(&path);
+ git_worktree_free(wt);
git_commit_free(commit);
git_reference_free(head);
git_reference_free(branch);
diff --git a/tests/resources/merge-resolve/.gitted/objects/29 b/tests/resources/merge-resolve/.gitted/objects/29
new file mode 100644
index 00000000000..9661507079f
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/29 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/54/c9d15687fb4f56e08252662962d6d1dbc09d9d b/tests/resources/merge-resolve/.gitted/objects/54/c9d15687fb4f56e08252662962d6d1dbc09d9d
new file mode 100644
index 00000000000..7e0555b3ff6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/54/c9d15687fb4f56e08252662962d6d1dbc09d9d
@@ -0,0 +1,3 @@
+xNI
+1W]/a&#~(:FQ\A۸Mtr8K0YK&Xkwə"$pPwjv\RhgI=ŘK*SDaM*z68.^aG?`
+q6Y83W^BMiQ
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 b/tests/resources/merge-resolve/.gitted/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6
new file mode 100644
index 00000000000..cfc3920fb3f
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/60/b12be2d2f57977ce83d8dfd32e2394ac1ba1a2 b/tests/resources/merge-resolve/.gitted/objects/60/b12be2d2f57977ce83d8dfd32e2394ac1ba1a2
new file mode 100644
index 00000000000..f53f75e0a13
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/60/b12be2d2f57977ce83d8dfd32e2394ac1ba1a2 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a6/5140d5ec9f47064f614ecf8e43776baa5c0c11 b/tests/resources/merge-resolve/.gitted/objects/a6/5140d5ec9f47064f614ecf8e43776baa5c0c11
new file mode 100644
index 00000000000..dc6cf6493cb
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/a6/5140d5ec9f47064f614ecf8e43776baa5c0c11 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ab/347abd8cda4a0e3b8bb42bb620c0c72c7df779 b/tests/resources/merge-resolve/.gitted/objects/ab/347abd8cda4a0e3b8bb42bb620c0c72c7df779
new file mode 100644
index 00000000000..d743a385c21
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/ab/347abd8cda4a0e3b8bb42bb620c0c72c7df779 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/114411903fd2afaa4bb9b85ed13f27e37ac375 b/tests/resources/merge-resolve/.gitted/objects/bc/114411903fd2afaa4bb9b85ed13f27e37ac375
new file mode 100644
index 00000000000..08941ff9538
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/bc/114411903fd2afaa4bb9b85ed13f27e37ac375 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cd/edf9760406dc79e0c6a8899ce9f180ec2a23a0 b/tests/resources/merge-resolve/.gitted/objects/cd/edf9760406dc79e0c6a8899ce9f180ec2a23a0
new file mode 100644
index 00000000000..011b5b3e58c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cd/edf9760406dc79e0c6a8899ce9f180ec2a23a0
@@ -0,0 +1,2 @@
+xA
+B1C]"n)B[U R^Mo3c:dchCi2FGkKzŖ:ײ<ǝ[Ҏj9 zB0X!58猒E;4~*uQo$D
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/de/06afe070b65f94d7d791c39a6d389c58dda60d b/tests/resources/merge-resolve/.gitted/objects/de/06afe070b65f94d7d791c39a6d389c58dda60d
new file mode 100644
index 00000000000..28567b624d1
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/de/06afe070b65f94d7d791c39a6d389c58dda60d differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e5/0a49f9558d09d4d3bfc108363bb24c127ed263 b/tests/resources/merge-resolve/.gitted/objects/e5/0a49f9558d09d4d3bfc108363bb24c127ed263
new file mode 100644
index 00000000000..251c5dfb20c
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/e5/0a49f9558d09d4d3bfc108363bb24c127ed263 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ea/789495e0a72efadcd0f86a48f4c9ed435bb8a3 b/tests/resources/merge-resolve/.gitted/objects/ea/789495e0a72efadcd0f86a48f4c9ed435bb8a3
new file mode 100644
index 00000000000..ed98d70564d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ea/789495e0a72efadcd0f86a48f4c9ed435bb8a3
@@ -0,0 +1,3 @@
+xOIj1Y!VA M~:E~&o>Z;uB[JrN9z)Vb!.Rj
+:
+$ŘK:!c \˃Fキ[?Kby{; pA-wS[_i{WOGR
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames b/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames
new file mode 100644
index 00000000000..89b4eea8ff5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames
@@ -0,0 +1 @@
+ea789495e0a72efadcd0f86a48f4c9ed435bb8a3
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames-branch b/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames-branch
new file mode 100644
index 00000000000..1c6a9f4db55
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames-branch
@@ -0,0 +1 @@
+ab347abd8cda4a0e3b8bb42bb620c0c72c7df779
diff --git a/tests/resources/status_skiphash/.gitted/COMMIT_EDITMSG b/tests/resources/status_skiphash/.gitted/COMMIT_EDITMSG
new file mode 100644
index 00000000000..ea450f959b9
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+New file
diff --git a/tests/resources/status_skiphash/.gitted/HEAD b/tests/resources/status_skiphash/.gitted/HEAD
new file mode 100644
index 00000000000..b870d82622c
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/main
diff --git a/tests/resources/status_skiphash/.gitted/MERGE_RR b/tests/resources/status_skiphash/.gitted/MERGE_RR
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/resources/status_skiphash/.gitted/config b/tests/resources/status_skiphash/.gitted/config
new file mode 100644
index 00000000000..16aebb686c2
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/config
@@ -0,0 +1,9 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+[index]
+ skipHash = true
diff --git a/tests/resources/status_skiphash/.gitted/description b/tests/resources/status_skiphash/.gitted/description
new file mode 100644
index 00000000000..498b267a8c7
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/status_skiphash/.gitted/index b/tests/resources/status_skiphash/.gitted/index
new file mode 100644
index 00000000000..1963fe0d3d4
Binary files /dev/null and b/tests/resources/status_skiphash/.gitted/index differ
diff --git a/tests/resources/status_skiphash/.gitted/info/exclude b/tests/resources/status_skiphash/.gitted/info/exclude
new file mode 100644
index 00000000000..a5196d1be8f
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/status_skiphash/.gitted/logs/HEAD b/tests/resources/status_skiphash/.gitted/logs/HEAD
new file mode 100644
index 00000000000..35e1a747dbe
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 34f4c90b237fcb4c677772a6093f3cba239c41a5 Parnic 1708097798 -0600 commit (initial): New file
diff --git a/tests/resources/status_skiphash/.gitted/logs/refs/heads/main b/tests/resources/status_skiphash/.gitted/logs/refs/heads/main
new file mode 100644
index 00000000000..35e1a747dbe
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/logs/refs/heads/main
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 34f4c90b237fcb4c677772a6093f3cba239c41a5 Parnic 1708097798 -0600 commit (initial): New file
diff --git a/tests/resources/status_skiphash/.gitted/objects/34/f4c90b237fcb4c677772a6093f3cba239c41a5 b/tests/resources/status_skiphash/.gitted/objects/34/f4c90b237fcb4c677772a6093f3cba239c41a5
new file mode 100644
index 00000000000..0513158cb21
Binary files /dev/null and b/tests/resources/status_skiphash/.gitted/objects/34/f4c90b237fcb4c677772a6093f3cba239c41a5 differ
diff --git a/tests/resources/status_skiphash/.gitted/objects/71/a21e67674e9717aa7380e5782ec5e070a8d7e0 b/tests/resources/status_skiphash/.gitted/objects/71/a21e67674e9717aa7380e5782ec5e070a8d7e0
new file mode 100644
index 00000000000..7c48fa4f43b
Binary files /dev/null and b/tests/resources/status_skiphash/.gitted/objects/71/a21e67674e9717aa7380e5782ec5e070a8d7e0 differ
diff --git a/tests/resources/status_skiphash/.gitted/objects/d7/c1f165e51adbbfd7760162b7a5802d4117740c b/tests/resources/status_skiphash/.gitted/objects/d7/c1f165e51adbbfd7760162b7a5802d4117740c
new file mode 100644
index 00000000000..c685321ce69
Binary files /dev/null and b/tests/resources/status_skiphash/.gitted/objects/d7/c1f165e51adbbfd7760162b7a5802d4117740c differ
diff --git a/tests/resources/status_skiphash/.gitted/refs/heads/main b/tests/resources/status_skiphash/.gitted/refs/heads/main
new file mode 100644
index 00000000000..693a12b664c
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/refs/heads/main
@@ -0,0 +1 @@
+34f4c90b237fcb4c677772a6093f3cba239c41a5
diff --git a/tests/resources/status_skiphash/new_file b/tests/resources/status_skiphash/new_file
new file mode 100644
index 00000000000..badcfca348a
--- /dev/null
+++ b/tests/resources/status_skiphash/new_file
@@ -0,0 +1 @@
+new_file
diff --git a/tests/resources/testrepo/.gitted/config b/tests/resources/testrepo/.gitted/config
index d0114012f98..04d750a93bc 100644
--- a/tests/resources/testrepo/.gitted/config
+++ b/tests/resources/testrepo/.gitted/config
@@ -3,6 +3,8 @@
filemode = true
bare = false
logallrefupdates = true
+[extensions]
+ worktreeconfig = true
[remote "test"]
url = git://github.com/libgit2/libgit2
fetch = +refs/heads/*:refs/remotes/test/*
diff --git a/tests/resources/testrepo/.gitted/config.worktree b/tests/resources/testrepo/.gitted/config.worktree
new file mode 100644
index 00000000000..df9f0caf650
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/config.worktree
@@ -0,0 +1,2 @@
+[worktreetest]
+ config = mainrepo
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/config.worktree b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/config.worktree
new file mode 100644
index 00000000000..7a130a7aed7
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/config.worktree
@@ -0,0 +1,2 @@
+[worktreetest]
+ config = worktreerepo
diff --git a/tests/util/path.c b/tests/util/path.c
deleted file mode 100644
index 02ec42fcea2..00000000000
--- a/tests/util/path.c
+++ /dev/null
@@ -1,768 +0,0 @@
-#include "clar_libgit2.h"
-#include "futils.h"
-#include "fs_path.h"
-
-#ifndef GIT_WIN32
-# include
-#endif
-
-static char *path_save;
-
-void test_path__initialize(void)
-{
- path_save = cl_getenv("PATH");
-}
-
-void test_path__cleanup(void)
-{
- cl_setenv("PATH", path_save);
- git__free(path_save);
- path_save = NULL;
-}
-
-static void
-check_dirname(const char *A, const char *B)
-{
- git_str dir = GIT_STR_INIT;
- char *dir2;
-
- cl_assert(git_fs_path_dirname_r(&dir, A) >= 0);
- cl_assert_equal_s(B, dir.ptr);
- git_str_dispose(&dir);
-
- cl_assert((dir2 = git_fs_path_dirname(A)) != NULL);
- cl_assert_equal_s(B, dir2);
- git__free(dir2);
-}
-
-static void
-check_basename(const char *A, const char *B)
-{
- git_str base = GIT_STR_INIT;
- char *base2;
-
- cl_assert(git_fs_path_basename_r(&base, A) >= 0);
- cl_assert_equal_s(B, base.ptr);
- git_str_dispose(&base);
-
- cl_assert((base2 = git_fs_path_basename(A)) != NULL);
- cl_assert_equal_s(B, base2);
- git__free(base2);
-}
-
-static void
-check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
-{
- git_str joined_path = GIT_STR_INIT;
-
- cl_git_pass(git_str_joinpath(&joined_path, path_a, path_b));
- cl_assert_equal_s(expected_path, joined_path.ptr);
-
- git_str_dispose(&joined_path);
-}
-
-static void
-check_joinpath_n(
- const char *path_a,
- const char *path_b,
- const char *path_c,
- const char *path_d,
- const char *expected_path)
-{
- git_str joined_path = GIT_STR_INIT;
-
- cl_git_pass(git_str_join_n(&joined_path, '/', 4,
- path_a, path_b, path_c, path_d));
- cl_assert_equal_s(expected_path, joined_path.ptr);
-
- git_str_dispose(&joined_path);
-}
-
-static void check_setenv(const char* name, const char* value)
-{
- char* check;
-
- cl_git_pass(cl_setenv(name, value));
- check = cl_getenv(name);
-
- if (value)
- cl_assert_equal_s(value, check);
- else
- cl_assert(check == NULL);
-
- git__free(check);
-}
-
-/* get the dirname of a path */
-void test_path__00_dirname(void)
-{
- check_dirname(NULL, ".");
- check_dirname("", ".");
- check_dirname("a", ".");
- check_dirname("/", "/");
- check_dirname("/usr", "/");
- check_dirname("/usr/", "/");
- check_dirname("/usr/lib", "/usr");
- check_dirname("/usr/lib/", "/usr");
- check_dirname("/usr/lib//", "/usr");
- check_dirname("usr/lib", "usr");
- check_dirname("usr/lib/", "usr");
- check_dirname("usr/lib//", "usr");
- check_dirname(".git/", ".");
-
- check_dirname(REP16("/abc"), REP15("/abc"));
-
-#ifdef GIT_WIN32
- check_dirname("C:/", "C:/");
- check_dirname("C:", "C:/");
- check_dirname("C:/path/", "C:/");
- check_dirname("C:/path", "C:/");
- check_dirname("//computername/", "//computername/");
- check_dirname("//computername", "//computername/");
- check_dirname("//computername/path/", "//computername/");
- check_dirname("//computername/path", "//computername/");
- check_dirname("//computername/sub/path/", "//computername/sub");
- check_dirname("//computername/sub/path", "//computername/sub");
-#endif
-}
-
-/* get the base name of a path */
-void test_path__01_basename(void)
-{
- check_basename(NULL, ".");
- check_basename("", ".");
- check_basename("a", "a");
- check_basename("/", "/");
- check_basename("/usr", "usr");
- check_basename("/usr/", "usr");
- check_basename("/usr/lib", "lib");
- check_basename("/usr/lib//", "lib");
- check_basename("usr/lib", "lib");
-
- check_basename(REP16("/abc"), "abc");
- check_basename(REP1024("/abc"), "abc");
-}
-
-/* properly join path components */
-void test_path__05_joins(void)
-{
- check_joinpath("", "", "");
- check_joinpath("", "a", "a");
- check_joinpath("", "/a", "/a");
- check_joinpath("a", "", "a/");
- check_joinpath("a", "/", "a/");
- check_joinpath("a", "b", "a/b");
- check_joinpath("/", "a", "/a");
- check_joinpath("/", "", "/");
- check_joinpath("/a", "/b", "/a/b");
- check_joinpath("/a", "/b/", "/a/b/");
- check_joinpath("/a/", "b/", "/a/b/");
- check_joinpath("/a/", "/b/", "/a/b/");
-
- check_joinpath("/abcd", "/defg", "/abcd/defg");
- check_joinpath("/abcd", "/defg/", "/abcd/defg/");
- check_joinpath("/abcd/", "defg/", "/abcd/defg/");
- check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
-
- check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
- check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
- check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
-
- check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/");
- check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/"));
- check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/");
-
- check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
- REP1024("aaaa") "/" REP1024("bbbb"));
- check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
- REP1024("/aaaa") REP1024("/bbbb"));
-}
-
-/* properly join path components for more than one path */
-void test_path__06_long_joins(void)
-{
- check_joinpath_n("", "", "", "", "");
- check_joinpath_n("", "a", "", "", "a/");
- check_joinpath_n("a", "", "", "", "a/");
- check_joinpath_n("", "", "", "a", "a");
- check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
- check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
- check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
- check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
- check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
-
- check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
- REP1024("a") "/" REP1024("b") "/"
- REP1024("c") "/" REP1024("d"));
- check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
- REP1024("/a") REP1024("/b")
- REP1024("/c") REP1024("/d"));
-}
-
-
-static void
-check_path_to_dir(
- const char* path,
- const char* expected)
-{
- git_str tgt = GIT_STR_INIT;
-
- git_str_sets(&tgt, path);
- cl_git_pass(git_fs_path_to_dir(&tgt));
- cl_assert_equal_s(expected, tgt.ptr);
-
- git_str_dispose(&tgt);
-}
-
-static void
-check_string_to_dir(
- const char* path,
- size_t maxlen,
- const char* expected)
-{
- size_t len = strlen(path);
- char *buf = git__malloc(len + 2);
- cl_assert(buf);
-
- strncpy(buf, path, len + 2);
-
- git_fs_path_string_to_dir(buf, maxlen);
-
- cl_assert_equal_s(expected, buf);
-
- git__free(buf);
-}
-
-/* convert paths to dirs */
-void test_path__07_path_to_dir(void)
-{
- check_path_to_dir("", "");
- check_path_to_dir(".", "./");
- check_path_to_dir("./", "./");
- check_path_to_dir("a/", "a/");
- check_path_to_dir("ab", "ab/");
- /* make sure we try just under and just over an expansion that will
- * require a realloc
- */
- check_path_to_dir("abcdef", "abcdef/");
- check_path_to_dir("abcdefg", "abcdefg/");
- check_path_to_dir("abcdefgh", "abcdefgh/");
- check_path_to_dir("abcdefghi", "abcdefghi/");
- check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
- check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
-
- check_string_to_dir("", 1, "");
- check_string_to_dir(".", 1, ".");
- check_string_to_dir(".", 2, "./");
- check_string_to_dir(".", 3, "./");
- check_string_to_dir("abcd", 3, "abcd");
- check_string_to_dir("abcd", 4, "abcd");
- check_string_to_dir("abcd", 5, "abcd/");
- check_string_to_dir("abcd", 6, "abcd/");
-}
-
-/* join path to itself */
-void test_path__08_self_join(void)
-{
- git_str path = GIT_STR_INIT;
- size_t asize = 0;
-
- asize = path.asize;
- cl_git_pass(git_str_sets(&path, "/foo"));
- cl_assert_equal_s(path.ptr, "/foo");
- cl_assert(asize < path.asize);
-
- asize = path.asize;
- cl_git_pass(git_str_joinpath(&path, path.ptr, "this is a new string"));
- cl_assert_equal_s(path.ptr, "/foo/this is a new string");
- cl_assert(asize < path.asize);
-
- asize = path.asize;
- cl_git_pass(git_str_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer"));
- cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer");
- cl_assert(asize < path.asize);
-
- git_str_dispose(&path);
- cl_git_pass(git_str_sets(&path, "/foo/bar"));
-
- cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "baz"));
- cl_assert_equal_s(path.ptr, "/bar/baz");
-
- asize = path.asize;
- cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
- cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
- cl_assert(asize < path.asize);
-
- git_str_dispose(&path);
-}
-
-static void check_percent_decoding(const char *expected_result, const char *input)
-{
- git_str buf = GIT_STR_INIT;
-
- cl_git_pass(git__percent_decode(&buf, input));
- cl_assert_equal_s(expected_result, git_str_cstr(&buf));
-
- git_str_dispose(&buf);
-}
-
-void test_path__09_percent_decode(void)
-{
- check_percent_decoding("abcd", "abcd");
- check_percent_decoding("a2%", "a2%");
- check_percent_decoding("a2%3", "a2%3");
- check_percent_decoding("a2%%3", "a2%%3");
- check_percent_decoding("a2%3z", "a2%3z");
- check_percent_decoding("a,", "a%2c");
- check_percent_decoding("a21", "a2%31");
- check_percent_decoding("a2%1", "a2%%31");
- check_percent_decoding("a bc ", "a%20bc%20");
- check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
-}
-
-static void check_fromurl(const char *expected_result, const char *input, int should_fail)
-{
- git_str buf = GIT_STR_INIT;
-
- assert(should_fail || expected_result);
-
- if (!should_fail) {
- cl_git_pass(git_fs_path_fromurl(&buf, input));
- cl_assert_equal_s(expected_result, git_str_cstr(&buf));
- } else
- cl_git_fail(git_fs_path_fromurl(&buf, input));
-
- git_str_dispose(&buf);
-}
-
-#ifdef GIT_WIN32
-#define ABS_PATH_MARKER ""
-#else
-#define ABS_PATH_MARKER "/"
-#endif
-
-void test_path__10_fromurl(void)
-{
- /* Failing cases */
- check_fromurl(NULL, "a", 1);
- check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1);
- check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1);
- check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1);
- check_fromurl(NULL, "file:///", 1);
- check_fromurl(NULL, "file:////", 1);
- check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1);
-
- /* Passing cases */
- check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0);
- check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0);
- check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0);
- check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
-}
-
-typedef struct {
- int expect_idx;
- int cancel_after;
- char **expect;
-} check_walkup_info;
-
-#define CANCEL_VALUE 1234
-
-static int check_one_walkup_step(void *ref, const char *path)
-{
- check_walkup_info *info = (check_walkup_info *)ref;
-
- if (!info->cancel_after) {
- cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]");
- return CANCEL_VALUE;
- }
- info->cancel_after--;
-
- cl_assert(info->expect[info->expect_idx] != NULL);
- cl_assert_equal_s(info->expect[info->expect_idx], path);
- info->expect_idx++;
-
- return 0;
-}
-
-void test_path__11_walkup(void)
-{
- git_str p = GIT_STR_INIT;
-
- char *expect[] = {
- /* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- /* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- /* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- /* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- /* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
- /* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
- /* 7 */ "this_is_a_path", "", NULL,
- /* 8 */ "this_is_a_path/", "", NULL,
- /* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
- /* 10 */ "a/b/c/", "a/b/", "a/", "", NULL,
- /* 11 */ "a/b/c", "a/b/", "a/", "", NULL,
- /* 12 */ "a/b/c/", "a/b/", "a/", NULL,
- /* 13 */ "", NULL,
- /* 14 */ "/", NULL,
- /* 15 */ NULL
- };
-
- char *root[] = {
- /* 1 */ NULL,
- /* 2 */ NULL,
- /* 3 */ "/",
- /* 4 */ "",
- /* 5 */ "/a/b",
- /* 6 */ "/a/b/",
- /* 7 */ NULL,
- /* 8 */ NULL,
- /* 9 */ NULL,
- /* 10 */ NULL,
- /* 11 */ NULL,
- /* 12 */ "a/",
- /* 13 */ NULL,
- /* 14 */ NULL,
- };
-
- int i, j;
- check_walkup_info info;
-
- info.expect = expect;
- info.cancel_after = -1;
-
- for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
-
- git_str_sets(&p, expect[i]);
-
- info.expect_idx = i;
- cl_git_pass(
- git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
- );
-
- cl_assert_equal_s(p.ptr, expect[i]);
- cl_assert(expect[info.expect_idx] == NULL);
- i = info.expect_idx;
- }
-
- git_str_dispose(&p);
-}
-
-void test_path__11a_walkup_cancel(void)
-{
- git_str p = GIT_STR_INIT;
- int cancel[] = { 3, 2, 1, 0 };
- char *expect[] = {
- "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL,
- "/a/b/c/d/e", "[CANCEL]", NULL,
- "[CANCEL]", NULL,
- NULL
- };
- char *root[] = { NULL, NULL, "/", "", NULL };
- int i, j;
- check_walkup_info info;
-
- info.expect = expect;
-
- for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
-
- git_str_sets(&p, expect[i]);
-
- info.cancel_after = cancel[j];
- info.expect_idx = i;
-
- cl_assert_equal_i(
- CANCEL_VALUE,
- git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
- );
-
- /* skip to next run of expectations */
- while (expect[i] != NULL) i++;
- }
-
- git_str_dispose(&p);
-}
-
-void test_path__12_offset_to_path_root(void)
-{
- cl_assert(git_fs_path_root("non/rooted/path") == -1);
- cl_assert(git_fs_path_root("/rooted/path") == 0);
-
-#ifdef GIT_WIN32
- /* Windows specific tests */
- cl_assert(git_fs_path_root("C:non/rooted/path") == -1);
- cl_assert(git_fs_path_root("C:/rooted/path") == 2);
- cl_assert(git_fs_path_root("//computername/sharefolder/resource") == 14);
- cl_assert(git_fs_path_root("//computername/sharefolder") == 14);
- cl_assert(git_fs_path_root("//computername") == -1);
-#endif
-}
-
-#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist"
-
-void test_path__13_cannot_prettify_a_non_existing_file(void)
-{
- git_str p = GIT_STR_INIT;
-
- cl_assert_equal_b(git_fs_path_exists(NON_EXISTING_FILEPATH), false);
- cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH, NULL));
- cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL));
-
- git_str_dispose(&p);
-}
-
-void test_path__14_apply_relative(void)
-{
- git_str p = GIT_STR_INIT;
-
- cl_git_pass(git_str_sets(&p, "/this/is/a/base"));
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../test"));
- cl_assert_equal_s("/this/is/a/test", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../../the/./end"));
- cl_assert_equal_s("/this/is/the/end", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "./of/this/../the/string"));
- cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../../../../../.."));
- cl_assert_equal_s("/this/", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../"));
- cl_assert_equal_s("/", p.ptr);
-
- cl_git_fail(git_fs_path_apply_relative(&p, "../../.."));
-
-
- cl_git_pass(git_str_sets(&p, "d:/another/test"));
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../.."));
- cl_assert_equal_s("d:/", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "from/here/to/../and/./back/."));
- cl_assert_equal_s("d:/from/here/and/back/", p.ptr);
-
-
- cl_git_pass(git_str_sets(&p, "https://my.url.com/test.git"));
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../another.git"));
- cl_assert_equal_s("https://my.url.com/another.git", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../full/path/url.patch"));
- cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, ".."));
- cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../../../"));
- cl_assert_equal_s("https://", p.ptr);
-
-
- cl_git_pass(git_str_sets(&p, "../../this/is/relative"));
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../../preserves/the/prefix"));
- cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../../../../that"));
- cl_assert_equal_s("../../that", p.ptr);
-
- cl_git_pass(git_fs_path_apply_relative(&p, "../there"));
- cl_assert_equal_s("../../there", p.ptr);
- git_str_dispose(&p);
-}
-
-static void assert_resolve_relative(
- git_str *buf, const char *expected, const char *path)
-{
- cl_git_pass(git_str_sets(buf, path));
- cl_git_pass(git_fs_path_resolve_relative(buf, 0));
- cl_assert_equal_s(expected, buf->ptr);
-}
-
-void test_path__15_resolve_relative(void)
-{
- git_str buf = GIT_STR_INIT;
-
- assert_resolve_relative(&buf, "", "");
- assert_resolve_relative(&buf, "", ".");
- assert_resolve_relative(&buf, "", "./");
- assert_resolve_relative(&buf, "..", "..");
- assert_resolve_relative(&buf, "../", "../");
- assert_resolve_relative(&buf, "..", "./..");
- assert_resolve_relative(&buf, "../", "./../");
- assert_resolve_relative(&buf, "../", "../.");
- assert_resolve_relative(&buf, "../", ".././");
- assert_resolve_relative(&buf, "../..", "../..");
- assert_resolve_relative(&buf, "../../", "../../");
-
- assert_resolve_relative(&buf, "/", "/");
- assert_resolve_relative(&buf, "/", "/.");
-
- assert_resolve_relative(&buf, "", "a/..");
- assert_resolve_relative(&buf, "", "a/../");
- assert_resolve_relative(&buf, "", "a/../.");
-
- assert_resolve_relative(&buf, "/a", "/a");
- assert_resolve_relative(&buf, "/a/", "/a/.");
- assert_resolve_relative(&buf, "/", "/a/../");
- assert_resolve_relative(&buf, "/", "/a/../.");
- assert_resolve_relative(&buf, "/", "/a/.././");
-
- assert_resolve_relative(&buf, "a", "a");
- assert_resolve_relative(&buf, "a/", "a/");
- assert_resolve_relative(&buf, "a/", "a/.");
- assert_resolve_relative(&buf, "a/", "a/./");
-
- assert_resolve_relative(&buf, "a/b", "a//b");
- assert_resolve_relative(&buf, "a/b/c", "a/b/c");
- assert_resolve_relative(&buf, "b/c", "./b/c");
- assert_resolve_relative(&buf, "a/c", "a/./c");
- assert_resolve_relative(&buf, "a/b/", "a/b/.");
-
- assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
- assert_resolve_relative(&buf, "/", "////");
- assert_resolve_relative(&buf, "/a", "///a");
- assert_resolve_relative(&buf, "/", "///.");
- assert_resolve_relative(&buf, "/", "///a/..");
-
- assert_resolve_relative(&buf, "../../path", "../../test//../././path");
- assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
-
- cl_git_pass(git_str_sets(&buf, "/.."));
- cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
-
- cl_git_pass(git_str_sets(&buf, "/./.."));
- cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
-
- cl_git_pass(git_str_sets(&buf, "/.//.."));
- cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
-
- cl_git_pass(git_str_sets(&buf, "/../."));
- cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
-
- cl_git_pass(git_str_sets(&buf, "/../.././../a"));
- cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
-
- cl_git_pass(git_str_sets(&buf, "////.."));
- cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
-
- /* things that start with Windows network paths */
-#ifdef GIT_WIN32
- assert_resolve_relative(&buf, "//a/b/c", "//a/b/c");
- assert_resolve_relative(&buf, "//a/", "//a/b/..");
- assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c");
-
- cl_git_pass(git_str_sets(&buf, "//a/b/../.."));
- cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
-#else
- assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
- assert_resolve_relative(&buf, "/a/", "//a/b/..");
- assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c");
- assert_resolve_relative(&buf, "/", "//a/b/../..");
-#endif
-
- git_str_dispose(&buf);
-}
-
-#define assert_common_dirlen(i, p, q) \
- cl_assert_equal_i((i), git_fs_path_common_dirlen((p), (q)));
-
-void test_path__16_resolve_relative(void)
-{
- assert_common_dirlen(0, "", "");
- assert_common_dirlen(0, "", "bar.txt");
- assert_common_dirlen(0, "foo.txt", "bar.txt");
- assert_common_dirlen(0, "foo.txt", "");
- assert_common_dirlen(0, "foo/bar.txt", "bar/foo.txt");
- assert_common_dirlen(0, "foo/bar.txt", "../foo.txt");
-
- assert_common_dirlen(1, "/one.txt", "/two.txt");
- assert_common_dirlen(4, "foo/one.txt", "foo/two.txt");
- assert_common_dirlen(5, "/foo/one.txt", "/foo/two.txt");
-
- assert_common_dirlen(6, "a/b/c/foo.txt", "a/b/c/d/e/bar.txt");
- assert_common_dirlen(7, "/a/b/c/foo.txt", "/a/b/c/d/e/bar.txt");
-}
-
-static void fix_path(git_str *s)
-{
-#ifndef GIT_WIN32
- GIT_UNUSED(s);
-#else
- char* c;
-
- for (c = s->ptr; *c; c++) {
- if (*c == '/')
- *c = '\\';
- }
-#endif
-}
-
-void test_path__find_exe_in_path(void)
-{
- char *orig_path;
- git_str sandbox_path = GIT_STR_INIT;
- git_str new_path = GIT_STR_INIT, full_path = GIT_STR_INIT,
- dummy_path = GIT_STR_INIT;
-
-#ifdef GIT_WIN32
- static const char *bogus_path_1 = "c:\\does\\not\\exist\\";
- static const char *bogus_path_2 = "e:\\non\\existent";
-#else
- static const char *bogus_path_1 = "/this/path/does/not/exist/";
- static const char *bogus_path_2 = "/non/existent";
-#endif
-
- orig_path = cl_getenv("PATH");
-
- git_str_puts(&sandbox_path, clar_sandbox_path());
- git_str_joinpath(&dummy_path, sandbox_path.ptr, "dummmmmmmy_libgit2_file");
- cl_git_rewritefile(dummy_path.ptr, "this is a dummy file");
-
- fix_path(&sandbox_path);
- fix_path(&dummy_path);
-
- cl_git_pass(git_str_printf(&new_path, "%s%c%s%c%s%c%s",
- bogus_path_1, GIT_PATH_LIST_SEPARATOR,
- orig_path, GIT_PATH_LIST_SEPARATOR,
- sandbox_path.ptr, GIT_PATH_LIST_SEPARATOR,
- bogus_path_2));
-
- check_setenv("PATH", new_path.ptr);
-
- cl_git_fail_with(GIT_ENOTFOUND, git_fs_path_find_executable(&full_path, "this_file_does_not_exist"));
- cl_git_pass(git_fs_path_find_executable(&full_path, "dummmmmmmy_libgit2_file"));
-
- cl_assert_equal_s(full_path.ptr, dummy_path.ptr);
-
- git_str_dispose(&full_path);
- git_str_dispose(&new_path);
- git_str_dispose(&dummy_path);
- git_str_dispose(&sandbox_path);
- git__free(orig_path);
-}
-
-void test_path__validate_current_user_ownership(void)
-{
- bool is_cur;
-
- cl_must_pass(p_mkdir("testdir", 0777));
- cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testdir"));
- cl_assert_equal_i(is_cur, 1);
-
- cl_git_rewritefile("testfile", "This is a test file.");
- cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testfile"));
- cl_assert_equal_i(is_cur, 1);
-
-#ifdef GIT_WIN32
- cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\"));
- cl_assert_equal_i(is_cur, 0);
-
- cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist"));
-#else
- cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/"));
- cl_assert_equal_i(is_cur, (geteuid() == 0));
-
- cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "/path/does/not/exist"));
-#endif
-}
diff --git a/tests/util/path/core.c b/tests/util/path/core.c
index f30f6c01b0d..d1935a816a9 100644
--- a/tests/util/path/core.c
+++ b/tests/util/path/core.c
@@ -1,6 +1,784 @@
#include "clar_libgit2.h"
+#include "futils.h"
#include "fs_path.h"
+#ifndef GIT_WIN32
+# include
+#endif
+
+static char *path_save;
+
+void test_path_core__initialize(void)
+{
+ path_save = cl_getenv("PATH");
+}
+
+void test_path_core__cleanup(void)
+{
+ cl_setenv("PATH", path_save);
+ git__free(path_save);
+ path_save = NULL;
+}
+
+static void
+check_dirname(const char *A, const char *B)
+{
+ git_str dir = GIT_STR_INIT;
+ char *dir2;
+
+ cl_assert(git_fs_path_dirname_r(&dir, A) >= 0);
+ cl_assert_equal_s(B, dir.ptr);
+ git_str_dispose(&dir);
+
+ cl_assert((dir2 = git_fs_path_dirname(A)) != NULL);
+ cl_assert_equal_s(B, dir2);
+ git__free(dir2);
+}
+
+static void
+check_basename(const char *A, const char *B)
+{
+ git_str base = GIT_STR_INIT;
+ char *base2;
+
+ cl_assert(git_fs_path_basename_r(&base, A) >= 0);
+ cl_assert_equal_s(B, base.ptr);
+ git_str_dispose(&base);
+
+ cl_assert((base2 = git_fs_path_basename(A)) != NULL);
+ cl_assert_equal_s(B, base2);
+ git__free(base2);
+}
+
+static void
+check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
+{
+ git_str joined_path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&joined_path, path_a, path_b));
+ cl_assert_equal_s(expected_path, joined_path.ptr);
+
+ git_str_dispose(&joined_path);
+}
+
+static void
+check_joinpath_n(
+ const char *path_a,
+ const char *path_b,
+ const char *path_c,
+ const char *path_d,
+ const char *expected_path)
+{
+ git_str joined_path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_join_n(&joined_path, '/', 4,
+ path_a, path_b, path_c, path_d));
+ cl_assert_equal_s(expected_path, joined_path.ptr);
+
+ git_str_dispose(&joined_path);
+}
+
+static void check_setenv(const char* name, const char* value)
+{
+ char* check;
+
+ cl_git_pass(cl_setenv(name, value));
+ check = cl_getenv(name);
+
+ if (value)
+ cl_assert_equal_s(value, check);
+ else
+ cl_assert(check == NULL);
+
+ git__free(check);
+}
+
+/* get the dirname of a path */
+void test_path_core__00_dirname(void)
+{
+ check_dirname(NULL, ".");
+ check_dirname("", ".");
+ check_dirname("a", ".");
+ check_dirname("/", "/");
+ check_dirname("/usr", "/");
+ check_dirname("/usr/", "/");
+ check_dirname("/usr/lib", "/usr");
+ check_dirname("/usr/lib/", "/usr");
+ check_dirname("/usr/lib//", "/usr");
+ check_dirname("usr/lib", "usr");
+ check_dirname("usr/lib/", "usr");
+ check_dirname("usr/lib//", "usr");
+ check_dirname(".git/", ".");
+
+ check_dirname(REP16("/abc"), REP15("/abc"));
+
+#ifdef GIT_WIN32
+ check_dirname("C:/", "C:/");
+ check_dirname("C:", "C:/");
+ check_dirname("C:/path/", "C:/");
+ check_dirname("C:/path", "C:/");
+ check_dirname("//computername/", "//computername/");
+ check_dirname("//computername", "//computername/");
+ check_dirname("//computername/path/", "//computername/");
+ check_dirname("//computername/path", "//computername/");
+ check_dirname("//computername/sub/path/", "//computername/sub");
+ check_dirname("//computername/sub/path", "//computername/sub");
+#endif
+}
+
+/* get the base name of a path */
+void test_path_core__01_basename(void)
+{
+ check_basename(NULL, ".");
+ check_basename("", ".");
+ check_basename("a", "a");
+ check_basename("/", "/");
+ check_basename("/usr", "usr");
+ check_basename("/usr/", "usr");
+ check_basename("/usr/lib", "lib");
+ check_basename("/usr/lib//", "lib");
+ check_basename("usr/lib", "lib");
+
+ check_basename(REP16("/abc"), "abc");
+ check_basename(REP1024("/abc"), "abc");
+}
+
+/* properly join path components */
+void test_path_core__05_joins(void)
+{
+ check_joinpath("", "", "");
+ check_joinpath("", "a", "a");
+ check_joinpath("", "/a", "/a");
+ check_joinpath("a", "", "a/");
+ check_joinpath("a", "/", "a/");
+ check_joinpath("a", "b", "a/b");
+ check_joinpath("/", "a", "/a");
+ check_joinpath("/", "", "/");
+ check_joinpath("/a", "/b", "/a/b");
+ check_joinpath("/a", "/b/", "/a/b/");
+ check_joinpath("/a/", "b/", "/a/b/");
+ check_joinpath("/a/", "/b/", "/a/b/");
+
+ check_joinpath("/abcd", "/defg", "/abcd/defg");
+ check_joinpath("/abcd", "/defg/", "/abcd/defg/");
+ check_joinpath("/abcd/", "defg/", "/abcd/defg/");
+ check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
+ check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
+ check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
+
+ check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/");
+ check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/"));
+ check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/");
+
+ check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
+ REP1024("aaaa") "/" REP1024("bbbb"));
+ check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
+ REP1024("/aaaa") REP1024("/bbbb"));
+}
+
+/* properly join path components for more than one path */
+void test_path_core__06_long_joins(void)
+{
+ check_joinpath_n("", "", "", "", "");
+ check_joinpath_n("", "a", "", "", "a/");
+ check_joinpath_n("a", "", "", "", "a/");
+ check_joinpath_n("", "", "", "a", "a");
+ check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
+ check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
+ check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
+ check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
+ check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
+
+ check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
+ REP1024("a") "/" REP1024("b") "/"
+ REP1024("c") "/" REP1024("d"));
+ check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
+ REP1024("/a") REP1024("/b")
+ REP1024("/c") REP1024("/d"));
+}
+
+
+static void
+check_path_to_dir(
+ const char* path,
+ const char* expected)
+{
+ git_str tgt = GIT_STR_INIT;
+
+ git_str_sets(&tgt, path);
+ cl_git_pass(git_fs_path_to_dir(&tgt));
+ cl_assert_equal_s(expected, tgt.ptr);
+
+ git_str_dispose(&tgt);
+}
+
+static void
+check_string_to_dir(
+ const char* path,
+ size_t maxlen,
+ const char* expected)
+{
+ size_t len = strlen(path);
+ char *buf = git__malloc(len + 2);
+ cl_assert(buf);
+
+ strncpy(buf, path, len + 2);
+
+ git_fs_path_string_to_dir(buf, maxlen);
+
+ cl_assert_equal_s(expected, buf);
+
+ git__free(buf);
+}
+
+/* convert paths to dirs */
+void test_path_core__07_path_to_dir(void)
+{
+ check_path_to_dir("", "");
+ check_path_to_dir(".", "./");
+ check_path_to_dir("./", "./");
+ check_path_to_dir("a/", "a/");
+ check_path_to_dir("ab", "ab/");
+ /* make sure we try just under and just over an expansion that will
+ * require a realloc
+ */
+ check_path_to_dir("abcdef", "abcdef/");
+ check_path_to_dir("abcdefg", "abcdefg/");
+ check_path_to_dir("abcdefgh", "abcdefgh/");
+ check_path_to_dir("abcdefghi", "abcdefghi/");
+ check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
+ check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
+
+ check_string_to_dir("", 1, "");
+ check_string_to_dir(".", 1, ".");
+ check_string_to_dir(".", 2, "./");
+ check_string_to_dir(".", 3, "./");
+ check_string_to_dir("abcd", 3, "abcd");
+ check_string_to_dir("abcd", 4, "abcd");
+ check_string_to_dir("abcd", 5, "abcd/");
+ check_string_to_dir("abcd", 6, "abcd/");
+}
+
+/* join path to itself */
+void test_path_core__08_self_join(void)
+{
+ git_str path = GIT_STR_INIT;
+ size_t asize = 0;
+
+ asize = path.asize;
+ cl_git_pass(git_str_sets(&path, "/foo"));
+ cl_assert_equal_s(path.ptr, "/foo");
+ cl_assert(asize < path.asize);
+
+ asize = path.asize;
+ cl_git_pass(git_str_joinpath(&path, path.ptr, "this is a new string"));
+ cl_assert_equal_s(path.ptr, "/foo/this is a new string");
+ cl_assert(asize < path.asize);
+
+ asize = path.asize;
+ cl_git_pass(git_str_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer"));
+ cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer");
+ cl_assert(asize < path.asize);
+
+ git_str_dispose(&path);
+ cl_git_pass(git_str_sets(&path, "/foo/bar"));
+
+ cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "baz"));
+ cl_assert_equal_s(path.ptr, "/bar/baz");
+
+ asize = path.asize;
+ cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
+ cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
+ cl_assert(asize < path.asize);
+
+ git_str_dispose(&path);
+}
+
+static void check_percent_decoding(const char *expected_result, const char *input)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git__percent_decode(&buf, input));
+ cl_assert_equal_s(expected_result, git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+}
+
+void test_path_core__09_percent_decode(void)
+{
+ check_percent_decoding("abcd", "abcd");
+ check_percent_decoding("a2%", "a2%");
+ check_percent_decoding("a2%3", "a2%3");
+ check_percent_decoding("a2%%3", "a2%%3");
+ check_percent_decoding("a2%3z", "a2%3z");
+ check_percent_decoding("a,", "a%2c");
+ check_percent_decoding("a21", "a2%31");
+ check_percent_decoding("a2%1", "a2%%31");
+ check_percent_decoding("a bc ", "a%20bc%20");
+ check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
+}
+
+static void check_fromurl(const char *expected_result, const char *input, int should_fail)
+{
+ git_str buf = GIT_STR_INIT;
+
+ assert(should_fail || expected_result);
+
+ if (!should_fail) {
+ cl_git_pass(git_fs_path_fromurl(&buf, input));
+ cl_assert_equal_s(expected_result, git_str_cstr(&buf));
+ } else
+ cl_git_fail(git_fs_path_fromurl(&buf, input));
+
+ git_str_dispose(&buf);
+}
+
+#ifdef GIT_WIN32
+#define ABS_PATH_MARKER ""
+#else
+#define ABS_PATH_MARKER "/"
+#endif
+
+void test_path_core__10_fromurl(void)
+{
+ /* Failing cases */
+ check_fromurl(NULL, "a", 1);
+ check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file:///", 1);
+ check_fromurl(NULL, "file:////", 1);
+ check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1);
+
+ /* Passing cases */
+ check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
+}
+
+typedef struct {
+ int expect_idx;
+ int cancel_after;
+ char **expect;
+} check_walkup_info;
+
+#define CANCEL_VALUE 1234
+
+static int check_one_walkup_step(void *ref, const char *path)
+{
+ check_walkup_info *info = (check_walkup_info *)ref;
+
+ if (!info->cancel_after) {
+ cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]");
+ return CANCEL_VALUE;
+ }
+ info->cancel_after--;
+
+ cl_assert(info->expect[info->expect_idx] != NULL);
+ cl_assert_equal_s(info->expect[info->expect_idx], path);
+ info->expect_idx++;
+
+ return 0;
+}
+
+void test_path_core__11_walkup(void)
+{
+ git_str p = GIT_STR_INIT;
+
+ char *expect[] = {
+ /* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ /* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ /* 7 */ "this_is_a_path", "", NULL,
+ /* 8 */ "this_is_a_path/", "", NULL,
+ /* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
+ /* 10 */ "a/b/c/", "a/b/", "a/", "", NULL,
+ /* 11 */ "a/b/c", "a/b/", "a/", "", NULL,
+ /* 12 */ "a/b/c/", "a/b/", "a/", NULL,
+ /* 13 */ "", NULL,
+ /* 14 */ "/", NULL,
+ /* 15 */ NULL
+ };
+
+ char *root[] = {
+ /* 1 */ NULL,
+ /* 2 */ NULL,
+ /* 3 */ "/",
+ /* 4 */ "",
+ /* 5 */ "/a/b",
+ /* 6 */ "/a/b/",
+ /* 7 */ NULL,
+ /* 8 */ NULL,
+ /* 9 */ NULL,
+ /* 10 */ NULL,
+ /* 11 */ NULL,
+ /* 12 */ "a/",
+ /* 13 */ NULL,
+ /* 14 */ NULL,
+ };
+
+ int i, j;
+ check_walkup_info info;
+
+ info.expect = expect;
+ info.cancel_after = -1;
+
+ for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
+
+ git_str_sets(&p, expect[i]);
+
+ info.expect_idx = i;
+ cl_git_pass(
+ git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
+ );
+
+ cl_assert_equal_s(p.ptr, expect[i]);
+ cl_assert(expect[info.expect_idx] == NULL);
+ i = info.expect_idx;
+ }
+
+ git_str_dispose(&p);
+}
+
+void test_path_core__11a_walkup_cancel(void)
+{
+ git_str p = GIT_STR_INIT;
+ int cancel[] = { 3, 2, 1, 0 };
+ char *expect[] = {
+ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL,
+ "/a/b/c/d/e", "[CANCEL]", NULL,
+ "[CANCEL]", NULL,
+ NULL
+ };
+ char *root[] = { NULL, NULL, "/", "", NULL };
+ int i, j;
+ check_walkup_info info;
+
+ info.expect = expect;
+
+ for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
+
+ git_str_sets(&p, expect[i]);
+
+ info.cancel_after = cancel[j];
+ info.expect_idx = i;
+
+ cl_assert_equal_i(
+ CANCEL_VALUE,
+ git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
+ );
+
+ /* skip to next run of expectations */
+ while (expect[i] != NULL) i++;
+ }
+
+ git_str_dispose(&p);
+}
+
+void test_path_core__12_offset_to_path_root(void)
+{
+ cl_assert(git_fs_path_root("non/rooted/path") == -1);
+ cl_assert(git_fs_path_root("/rooted/path") == 0);
+
+#ifdef GIT_WIN32
+ /* Windows specific tests */
+ cl_assert(git_fs_path_root("C:non/rooted/path") == -1);
+ cl_assert(git_fs_path_root("C:/rooted/path") == 2);
+ cl_assert(git_fs_path_root("//computername/sharefolder/resource") == 14);
+ cl_assert(git_fs_path_root("//computername/sharefolder") == 14);
+ cl_assert(git_fs_path_root("//computername") == -1);
+#endif
+}
+
+#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist"
+
+void test_path_core__13_cannot_prettify_a_non_existing_file(void)
+{
+ git_str p = GIT_STR_INIT;
+
+ cl_assert_equal_b(git_fs_path_exists(NON_EXISTING_FILEPATH), false);
+ cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH, NULL));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL));
+
+ git_str_dispose(&p);
+}
+
+void test_path_core__14_apply_relative(void)
+{
+ git_str p = GIT_STR_INIT;
+
+ cl_git_pass(git_str_sets(&p, "/this/is/a/base"));
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../test"));
+ cl_assert_equal_s("/this/is/a/test", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../the/./end"));
+ cl_assert_equal_s("/this/is/the/end", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "./of/this/../the/string"));
+ cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../../../../.."));
+ cl_assert_equal_s("/this/", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../"));
+ cl_assert_equal_s("/", p.ptr);
+
+ cl_git_fail(git_fs_path_apply_relative(&p, "../../.."));
+
+
+ cl_git_pass(git_str_sets(&p, "d:/another/test"));
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../.."));
+ cl_assert_equal_s("d:/", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "from/here/to/../and/./back/."));
+ cl_assert_equal_s("d:/from/here/and/back/", p.ptr);
+
+
+ cl_git_pass(git_str_sets(&p, "https://my.url.com/test.git"));
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../another.git"));
+ cl_assert_equal_s("https://my.url.com/another.git", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../full/path/url.patch"));
+ cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, ".."));
+ cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../../"));
+ cl_assert_equal_s("https://", p.ptr);
+
+
+ cl_git_pass(git_str_sets(&p, "../../this/is/relative"));
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../preserves/the/prefix"));
+ cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../../../that"));
+ cl_assert_equal_s("../../that", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../there"));
+ cl_assert_equal_s("../../there", p.ptr);
+ git_str_dispose(&p);
+}
+
+static void assert_resolve_relative(
+ git_str *buf, const char *expected, const char *path)
+{
+ cl_git_pass(git_str_sets(buf, path));
+ cl_git_pass(git_fs_path_resolve_relative(buf, 0));
+ cl_assert_equal_s(expected, buf->ptr);
+}
+
+void test_path_core__15_resolve_relative(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ assert_resolve_relative(&buf, "", "");
+ assert_resolve_relative(&buf, "", ".");
+ assert_resolve_relative(&buf, "", "./");
+ assert_resolve_relative(&buf, "..", "..");
+ assert_resolve_relative(&buf, "../", "../");
+ assert_resolve_relative(&buf, "..", "./..");
+ assert_resolve_relative(&buf, "../", "./../");
+ assert_resolve_relative(&buf, "../", "../.");
+ assert_resolve_relative(&buf, "../", ".././");
+ assert_resolve_relative(&buf, "../..", "../..");
+ assert_resolve_relative(&buf, "../../", "../../");
+
+ assert_resolve_relative(&buf, "/", "/");
+ assert_resolve_relative(&buf, "/", "/.");
+
+ assert_resolve_relative(&buf, "", "a/..");
+ assert_resolve_relative(&buf, "", "a/../");
+ assert_resolve_relative(&buf, "", "a/../.");
+
+ assert_resolve_relative(&buf, "/a", "/a");
+ assert_resolve_relative(&buf, "/a/", "/a/.");
+ assert_resolve_relative(&buf, "/", "/a/../");
+ assert_resolve_relative(&buf, "/", "/a/../.");
+ assert_resolve_relative(&buf, "/", "/a/.././");
+
+ assert_resolve_relative(&buf, "a", "a");
+ assert_resolve_relative(&buf, "a/", "a/");
+ assert_resolve_relative(&buf, "a/", "a/.");
+ assert_resolve_relative(&buf, "a/", "a/./");
+
+ assert_resolve_relative(&buf, "a/b", "a//b");
+ assert_resolve_relative(&buf, "a/b/c", "a/b/c");
+ assert_resolve_relative(&buf, "b/c", "./b/c");
+ assert_resolve_relative(&buf, "a/c", "a/./c");
+ assert_resolve_relative(&buf, "a/b/", "a/b/.");
+
+ assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
+ assert_resolve_relative(&buf, "/", "////");
+ assert_resolve_relative(&buf, "/a", "///a");
+ assert_resolve_relative(&buf, "/", "///.");
+ assert_resolve_relative(&buf, "/", "///a/..");
+
+ assert_resolve_relative(&buf, "../../path", "../../test//../././path");
+ assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
+
+ cl_git_pass(git_str_sets(&buf, "/.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "/./.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "/.//.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "/../."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "/../.././../a"));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "////.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ /* things that start with Windows network paths */
+#ifdef GIT_WIN32
+ assert_resolve_relative(&buf, "//a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "//a/", "//a/b/..");
+ assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c");
+
+ cl_git_pass(git_str_sets(&buf, "//a/b/../.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+#else
+ assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "/a/", "//a/b/..");
+ assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c");
+ assert_resolve_relative(&buf, "/", "//a/b/../..");
+#endif
+
+ git_str_dispose(&buf);
+}
+
+#define assert_common_dirlen(i, p, q) \
+ cl_assert_equal_i((i), git_fs_path_common_dirlen((p), (q)));
+
+void test_path_core__16_resolve_relative(void)
+{
+ assert_common_dirlen(0, "", "");
+ assert_common_dirlen(0, "", "bar.txt");
+ assert_common_dirlen(0, "foo.txt", "bar.txt");
+ assert_common_dirlen(0, "foo.txt", "");
+ assert_common_dirlen(0, "foo/bar.txt", "bar/foo.txt");
+ assert_common_dirlen(0, "foo/bar.txt", "../foo.txt");
+
+ assert_common_dirlen(1, "/one.txt", "/two.txt");
+ assert_common_dirlen(4, "foo/one.txt", "foo/two.txt");
+ assert_common_dirlen(5, "/foo/one.txt", "/foo/two.txt");
+
+ assert_common_dirlen(6, "a/b/c/foo.txt", "a/b/c/d/e/bar.txt");
+ assert_common_dirlen(7, "/a/b/c/foo.txt", "/a/b/c/d/e/bar.txt");
+}
+
+static void fix_path(git_str *s)
+{
+#ifndef GIT_WIN32
+ GIT_UNUSED(s);
+#else
+ char* c;
+
+ for (c = s->ptr; *c; c++) {
+ if (*c == '/')
+ *c = '\\';
+ }
+#endif
+}
+
+void test_path_core__find_exe_in_path(void)
+{
+ char *orig_path;
+ git_str sandbox_path = GIT_STR_INIT;
+ git_str new_path = GIT_STR_INIT, full_path = GIT_STR_INIT,
+ dummy_path = GIT_STR_INIT;
+
+#ifdef GIT_WIN32
+ static const char *bogus_path_1 = "c:\\does\\not\\exist\\";
+ static const char *bogus_path_2 = "e:\\non\\existent";
+#else
+ static const char *bogus_path_1 = "/this/path/does/not/exist/";
+ static const char *bogus_path_2 = "/non/existent";
+#endif
+
+ orig_path = cl_getenv("PATH");
+
+ git_str_puts(&sandbox_path, clar_sandbox_path());
+ git_str_joinpath(&dummy_path, sandbox_path.ptr, "dummmmmmmy_libgit2_file");
+ cl_git_rewritefile(dummy_path.ptr, "this is a dummy file");
+
+ fix_path(&sandbox_path);
+ fix_path(&dummy_path);
+
+ cl_git_pass(git_str_printf(&new_path, "%s%c%s%c%s%c%s",
+ bogus_path_1, GIT_PATH_LIST_SEPARATOR,
+ orig_path, GIT_PATH_LIST_SEPARATOR,
+ sandbox_path.ptr, GIT_PATH_LIST_SEPARATOR,
+ bogus_path_2));
+
+ check_setenv("PATH", new_path.ptr);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_fs_path_find_executable(&full_path, "this_file_does_not_exist"));
+ cl_git_pass(git_fs_path_find_executable(&full_path, "dummmmmmmy_libgit2_file"));
+
+ cl_assert_equal_s(full_path.ptr, dummy_path.ptr);
+
+ git_str_dispose(&full_path);
+ git_str_dispose(&new_path);
+ git_str_dispose(&dummy_path);
+ git_str_dispose(&sandbox_path);
+ git__free(orig_path);
+}
+
+void test_path_core__validate_current_user_ownership(void)
+{
+ bool is_cur;
+
+ cl_must_pass(p_mkdir("testdir", 0777));
+ cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testdir"));
+ cl_assert_equal_i(is_cur, 1);
+
+ cl_git_rewritefile("testfile", "This is a test file.");
+ cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testfile"));
+ cl_assert_equal_i(is_cur, 1);
+
+#ifdef GIT_WIN32
+ cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\"));
+ cl_assert_equal_i(is_cur, 0);
+
+ cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist"));
+#else
+ cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/"));
+ cl_assert_equal_i(is_cur, (geteuid() == 0));
+
+ cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "/path/does/not/exist"));
+#endif
+}
+
+void test_path_core__dirlen(void)
+{
+ cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf"));
+ cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf/"));
+ cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf//"));
+ cl_assert_equal_sz(3, git_fs_path_dirlen("foo////"));
+ cl_assert_equal_sz(3, git_fs_path_dirlen("foo"));
+ cl_assert_equal_sz(1, git_fs_path_dirlen("/"));
+ cl_assert_equal_sz(1, git_fs_path_dirlen("////"));
+ cl_assert_equal_sz(0, git_fs_path_dirlen(""));
+}
+
static void test_make_relative(
const char *expected_path,
const char *path,
@@ -341,3 +1119,29 @@ void test_path_core__join_unrooted_respects_funny_windows_roots(void)
test_join_unrooted("💩:/foo/bar/foobar", 13, "💩:/foo/bar/foobar", "💩:/foo/bar");
test_join_unrooted("💩:/foo/bar/foobar", 9, "💩:/foo/bar/foobar", "💩:/foo/");
}
+
+void test_path_core__is_root(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_root("/"));
+ cl_assert_equal_b(false, git_fs_path_is_root("//"));
+ cl_assert_equal_b(false, git_fs_path_is_root("foo/"));
+ cl_assert_equal_b(false, git_fs_path_is_root("/foo/"));
+ cl_assert_equal_b(false, git_fs_path_is_root("/foo"));
+ cl_assert_equal_b(false, git_fs_path_is_root("\\"));
+
+#ifdef GIT_WIN32
+ cl_assert_equal_b(true, git_fs_path_is_root("A:\\"));
+ cl_assert_equal_b(false, git_fs_path_is_root("B:\\foo"));
+ cl_assert_equal_b(false, git_fs_path_is_root("B:\\foo\\"));
+ cl_assert_equal_b(true, git_fs_path_is_root("C:\\"));
+ cl_assert_equal_b(true, git_fs_path_is_root("c:\\"));
+ cl_assert_equal_b(true, git_fs_path_is_root("z:\\"));
+ cl_assert_equal_b(false, git_fs_path_is_root("z:\\\\"));
+ cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost"));
+ cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\"));
+ cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\"));
+ cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\Foo"));
+ cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\Foo\\"));
+ cl_assert_equal_b(false, git_fs_path_is_root("\\\\Volume\\12345\\Foo\\Bar.txt"));
+#endif
+}
diff --git a/tests/util/path/win32.c b/tests/util/path/win32.c
index 1aaf6867a26..6f90b447dda 100644
--- a/tests/util/path/win32.c
+++ b/tests/util/path/win32.c
@@ -278,5 +278,38 @@ void test_path_win32__8dot3_name(void)
cl_must_pass(p_mkdir(".bar", 0777));
cl_assert_equal_s("BAR~2", (shortname = git_win32_path_8dot3_name(".bar")));
git__free(shortname);
+
+ p_rmdir(".foo");
+ p_rmdir(".bar");
+ p_unlink("bar~1");
+#endif
+}
+
+void test_path_win32__realpath(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT;
+ char result[GIT_PATH_MAX];
+
+ /* Ensure relative paths become absolute */
+ cl_must_pass(git_str_joinpath(&expected, clar_sandbox_path(), "abcdef"));
+ cl_must_pass(p_mkdir("abcdef", 0777));
+ cl_assert(p_realpath("./abcdef", result) != NULL);
+ cl_assert_equal_s(expected.ptr, result);
+
+ /* Ensure case is canonicalized */
+ git_str_clear(&expected);
+ cl_must_pass(git_str_joinpath(&expected, clar_sandbox_path(), "FOO"));
+ cl_must_pass(p_mkdir("FOO", 0777));
+ cl_assert(p_realpath("foo", result) != NULL);
+ cl_assert_equal_s(expected.ptr, result);
+
+ cl_assert(p_realpath("nonexistent", result) == NULL);
+ cl_assert_equal_i(ENOENT, errno);
+
+ git_str_dispose(&expected);
+
+ p_rmdir("abcdef");
+ p_rmdir("FOO");
#endif
}