--- /dev/null
+{
+ "postCreateCommand": "bash .devcontainer/setup.sh"
+}
--- /dev/null
+#!/bin/sh
+set -e
+
+sudo apt-get update
+sudo apt-get -y --no-install-recommends install cmake
+
+mkdir build
+cd build
+cmake ..
\ No newline at end of file
* text=auto
+tests/resources/** linguist-vendored
--- /dev/null
+name: "CodeQL"
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '21 3 * * 1'
+
+env:
+ docker-registry: docker.pkg.github.com
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: 'cpp'
+
+ - name: Build
+ run: |
+ mkdir build
+ cd build
+ cmake .. -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ cmake --build .
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
# Continuous integration and pull request validation builds for the
-# master and maintenance branches.
+# main and maintenance branches.
name: CI Build
on:
push:
- branches: [ master, maint/* ]
+ branches: [ main, maint/* ]
pull_request:
- branches: [ master, maint/* ]
+ branches: [ main, maint/* ]
+ workflow_dispatch:
env:
docker-registry: docker.pkg.github.com
- docker-config-path: azure-pipelines/docker
+ docker-config-path: source/ci/docker
jobs:
# Build the docker container images that we will use for our Linux
strategy:
matrix:
container:
- - xenial
- - bionic
- - focal
- - docurium
+ - name: xenial
+ - name: bionic
+ - name: focal
+ - name: docurium
+ - name: bionic-x86
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:x86-bionic
+ qemu: true
+ - name: bionic-arm32
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:armhf-bionic
+ qemu: true
+ - name: bionic-arm64
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:arm64-bionic
+ qemu: true
+ - name: centos7
+ - name: centos8
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2
with:
+ path: source
fetch-depth: 0
- if: github.event_name == 'push'
+ if: github.event_name != 'pull_request'
+ - name: Setup QEMU
+ run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
+ if: matrix.container.qemu == true
- name: Download existing container
- run: azure-pipelines/getcontainer.sh ${{ env.docker-config-path }}/${{ matrix.container }}
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.container.name }}" "${{ matrix.container.dockerfile }}"
env:
DOCKER_REGISTRY: ${{ env.docker-registry }}
GITHUB_TOKEN: ${{ secrets.github_token }}
- if: github.event_name == 'push'
+ working-directory: ${{ env.docker-config-path }}
+ if: github.event_name != 'pull_request'
- name: Build and publish image
run: |
- docker build -t ${{ env.docker-registry-container-sha }} --build-arg BASE=${{ matrix.container.base }} -f ${{ matrix.container }} .
+ if [ "${{ matrix.container.base }}" != "" ]; then
+ BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
+ fi
+ docker build -t ${{ env.docker-registry-container-sha }} ${BASE_ARG} -f ${{ env.dockerfile }} .
+ docker tag ${{ env.docker-registry-container-sha }} ${{ env.docker-registry-container-latest }}
docker push ${{ env.docker-registry-container-sha }}
+ docker push ${{ env.docker-registry-container-latest }}
working-directory: ${{ env.docker-config-path }}
- if: github.event_name == 'push' && env.docker-container-exists != 'true'
+ if: github.event_name != 'pull_request' && env.docker-container-exists != 'true'
# 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:
name: Build
- needs: [build_containers]
+ needs: [ build_containers ]
strategy:
matrix:
platform:
- # Xenial, GCC, OpenSSL
- image: xenial
+ container:
+ name: xenial
env:
CC: gcc
CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
os: ubuntu-latest
- # Xenial, GCC, mbedTLS
- image: xenial
+ container:
+ name: xenial
env:
CC: gcc
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
os: ubuntu-latest
- # Xenial, Clang, OpenSSL
- image: xenial
+ container:
+ name: xenial
env:
CC: clang
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
os: ubuntu-latest
- # Xenial, Clang, mbedTLS
- image: xenial
+ container:
+ name: xenial
env:
CC: clang
CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
CMAKE_GENERATOR: Ninja
os: ubuntu-latest
- # Focal, Clang 10, mbedTLS, MemorySanitizer
- image: focal
+ container:
+ name: focal
env:
CC: clang-10
CFLAGS: -fsanitize=memory -fsanitize-memory-track-origins=2 -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
os: ubuntu-latest
- # Focal, Clang 10, OpenSSL, UndefinedBehaviorSanitizer
- image: focal
+ container:
+ name: focal
env:
CC: clang-10
CFLAGS: -fsanitize=undefined,nullability -fno-sanitize-recover=undefined,nullability -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
+ os: ubuntu-latest
+ - # Focal, Clang 10, OpenSSL, ThreadSanitizer
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ 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
os: ubuntu-latest
- # macOS
os: macos-10.15
env:
CC: clang
CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
- CMAKE_GENERATOR: Ninja
PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
env:
ARCH: amd64
CMAKE_GENERATOR: Visual Studio 16 2019
- CMAKE_OPTIONS: -A x64 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- # Windows x86 Visual Studio
env:
ARCH: x86
CMAKE_GENERATOR: Visual Studio 16 2019
- CMAKE_OPTIONS: -A Win32 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- # Windows amd64 mingw
- name: Check out repository
uses: actions/checkout@v2
with:
+ path: source
fetch-depth: 0
- name: Set up build environment
- run: azure-pipelines/setup-${{ matrix.platform.setup-script }}.sh
+ run: source/ci/setup-${{ matrix.platform.setup-script }}.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: Download container
- run: azure-pipelines/getcontainer.sh ${{ env.docker-config-path }}/${{ matrix.platform.image }}
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
env:
DOCKER_REGISTRY: ${{ env.docker-registry }}
GITHUB_TOKEN: ${{ secrets.github_token }}
- if: matrix.platform.image != ''
+ working-directory: ${{ env.docker-config-path }}
+ if: matrix.platform.container.name != ''
- name: Create container
- run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ matrix.platform.image }} .
+ run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ env.dockerfile }} .
working-directory: ${{ env.docker-config-path }}
- if: matrix.platform.image != '' && env.docker-container-exists != 'true'
+ if: matrix.platform.container.name != '' && env.docker-container-exists != 'true'
- name: Build and test
run: |
export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
- if [ -n "${{ matrix.platform.image }}" ]; then
+ if [ -n "${{ matrix.platform.container.name }}" ]; then
docker run \
--rm \
- -v "$(pwd):/home/libgit2/source" \
- -w /home/libgit2/source \
+ --user libgit2:libgit2 \
+ -v "$(pwd)/source:/home/libgit2/source" \
+ -w /home/libgit2 \
-e ASAN_SYMBOLIZER_PATH \
-e CC \
-e CFLAGS \
-e PKG_CONFIG_PATH \
-e SKIP_NEGOTIATE_TESTS \
-e SKIP_SSH_TESTS \
+ -e TSAN_OPTIONS \
+ -e UBSAN_OPTIONS \
${{ env.docker-registry-container-sha }} \
- /bin/bash -c "mkdir build && cd build && ../azure-pipelines/build.sh && ../azure-pipelines/test.sh"
+ /bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh"
else
mkdir build && cd build
- ../azure-pipelines/build.sh
- ../azure-pipelines/test.sh
+ ../source/ci/build.sh
+ ../source/ci/test.sh
fi
shell: bash
# Generate documentation using docurium. We'll upload the documentation
# as a build artifact so that it can be reviewed as part of a pull
# request or in a forked build. For CI builds in the main repository's
- # master branch, we'll push the gh-pages branch back up so that it is
+ # main branch, we'll push the gh-pages branch back up so that it is
# published to our documentation site.
documentation:
name: Generate documentation
- name: Check out repository
uses: actions/checkout@v2
with:
+ path: source
fetch-depth: 0
- name: Generate documentation
+ working-directory: source
run: |
git config user.name 'Documentation Generation'
git config user.email 'libgit2@users.noreply.github.com'
docker login https://${{ env.docker-registry }} -u ${{ github.actor }} -p ${{ github.token }}
docker run \
--rm \
- -v "$(pwd):/home/libgit2/source" \
- -w /home/libgit2/source \
+ -v "$(pwd):/home/libgit2" \
+ -w /home/libgit2 \
${{ env.docker-registry }}/${{ github.repository }}/docurium:latest \
cm doc api.docurium
git checkout gh-pages
name: Upload artifact
with:
name: api-documentation
- path: api-documentation.zip
+ path: source/api-documentation.zip
- name: Push documentation branch
+ working-directory: source
run: git push origin gh-pages
- if: github.event_name == 'push' && github.repository == 'libgit2/libgit2'
+ if: github.event_name != 'pull_request' && github.repository == 'libgit2/libgit2'
--- /dev/null
+# Nightly build for the main branch across multiple targets.
+name: Nightly Build
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '15 1 * * *'
+
+env:
+ docker-registry: docker.pkg.github.com
+ docker-config-path: source/ci/docker
+
+jobs:
+ # Run our nightly 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:
+ name: Build
+ strategy:
+ matrix:
+ platform:
+ - # Xenial, GCC, OpenSSL
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ os: ubuntu-latest
+ - # Xenial, GCC, mbedTLS
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ os: ubuntu-latest
+ - # Xenial, Clang, OpenSSL
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ os: ubuntu-latest
+ - # Xenial, Clang, mbedTLS
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ CMAKE_GENERATOR: Ninja
+ os: ubuntu-latest
+ - # Xenial, GCC, thread-free
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_OPTIONS: -DTHREADSAFE=OFF -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ CMAKE_GENERATOR: Ninja
+ os: ubuntu-latest
+ - # Xenial, Clang, OpenSSL (dynamically loaded)
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ CMAKE_GENERATOR: Ninja
+ os: ubuntu-latest
+ - # Focal, Clang 10, mbedTLS, MemorySanitizer
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=memory -fsanitize-memory-track-origins=2 -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local/msan -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ os: ubuntu-latest
+ - # Focal, Clang 10, OpenSSL, UndefinedBehaviorSanitizer
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=undefined,nullability -fno-sanitize-recover=undefined,nullability -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ os: ubuntu-latest
+ - # Focal, Clang 10, OpenSSL, ThreadSanitizer
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
+ os: ubuntu-latest
+ - # Focal, Clang 10, mmap emulation (NO_MMAP)
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -DNO_MMAP
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ os: ubuntu-latest
+ - # CentOS 7
+ container:
+ name: centos7
+ env:
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ os: ubuntu-latest
+ - # CentOS 7, OpenSSL (dynamically loaded)
+ container:
+ name: centos7
+ env:
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ os: ubuntu-latest
+ - # CentOS 8
+ container:
+ name: centos8
+ env:
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ SKIP_SSH_TESTS: true
+ os: ubuntu-latest
+ - # CentOS 8, OpenSSL (dynamically loaded)
+ container:
+ name: centos8
+ env:
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ SKIP_SSH_TESTS: true
+ os: ubuntu-latest
+ - # macOS
+ os: macos-10.15
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ setup-script: osx
+ - # Windows amd64 Visual Studio
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - # Windows amd64 Visual Studio (NO_MMAP)
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CFLAGS: -DNO_MMAP
+ CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - # Windows x86 Visual Studio
+ os: windows-2019
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - # Windows amd64 mingw
+ os: windows-2019
+ setup-script: mingw
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: MinGW Makefiles
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
+ BUILD_TEMP: D:\Temp
+ BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - # Windows x86 mingw
+ os: windows-2019
+ setup-script: mingw
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: MinGW Makefiles
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
+ BUILD_TEMP: D:\Temp
+ BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - # Bionic, GCC, OpenSSL (dynamically loaded)
+ container:
+ name: bionic
+ dockerfile: bionic
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ RUN_INVASIVE_TESTS: true
+ os: ubuntu-latest
+ - # Bionic, x86, Clang, OpenSSL
+ container:
+ name: bionic-x86
+ dockerfile: bionic
+ qemu: true
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ RUN_INVASIVE_TESTS: true
+ os: ubuntu-latest
+ - # Bionic, x86, GCC, OpenSSL
+ container:
+ name: bionic-x86
+ dockerfile: bionic
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ RUN_INVASIVE_TESTS: true
+ os: ubuntu-latest
+ - # Bionic, arm32, GCC, OpenSSL
+ container:
+ name: bionic-arm32
+ dockerfile: bionic
+ qemu: true
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON
+ RUN_INVASIVE_TESTS: true
+ SKIP_PROXY_TESTS: true
+ os: ubuntu-latest
+ - # Bionic, arm64, GCC, OpenSSL
+ container:
+ name: bionic-arm64
+ dockerfile: bionic
+ qemu: true
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON
+ RUN_INVASIVE_TESTS: true
+ SKIP_PROXY_TESTS: true
+ os: ubuntu-latest
+ fail-fast: false
+ env: ${{ matrix.platform.env }}
+ runs-on: ${{ matrix.platform.os }}
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v2
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Set up build environment
+ run: source/ci/setup-${{ matrix.platform.setup-script }}.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: Download container
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
+ env:
+ DOCKER_REGISTRY: ${{ env.docker-registry }}
+ GITHUB_TOKEN: ${{ secrets.github_token }}
+ working-directory: ${{ env.docker-config-path }}
+ if: matrix.platform.container.name != ''
+ - name: Create container
+ run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ env.dockerfile }} .
+ working-directory: ${{ env.docker-config-path }}
+ if: matrix.platform.container.name != '' && env.docker-container-exists != 'true'
+ - name: Build and test
+ run: |
+ export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
+
+ if [ -n "${{ matrix.platform.container.name }}" ]; then
+ docker run \
+ --rm \
+ --user libgit2:libgit2 \
+ -v "$(pwd)/source:/home/libgit2/source" \
+ -w /home/libgit2 \
+ -e ASAN_SYMBOLIZER_PATH \
+ -e CC \
+ -e CFLAGS \
+ -e CMAKE_GENERATOR \
+ -e CMAKE_OPTIONS \
+ -e GITTEST_NEGOTIATE_PASSWORD \
+ -e PKG_CONFIG_PATH \
+ -e SKIP_NEGOTIATE_TESTS \
+ -e SKIP_SSH_TESTS \
+ -e TSAN_OPTIONS \
+ ${{ env.docker-registry-container-sha }} \
+ /bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh"
+ else
+ mkdir build && cd build
+ ../source/ci/build.sh
+ ../source/ci/test.sh
+ fi
+ shell: bash
+
+ coverity:
+ name: Coverity
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v2
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Download container
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" xenial
+ env:
+ DOCKER_REGISTRY: ${{ env.docker-registry }}
+ GITHUB_TOKEN: ${{ secrets.github_token }}
+ working-directory: ${{ env.docker-config-path }}
+ - name: Run Coverity
+ run: source/ci/coverity.sh
+ env:
+ COVERITY_TOKEN: ${{ secrets.coverity_token }}
-build/
+/build/
.DS_Store
*~
.*.swp
-tags
+/tags
CMakeSettings.json
.vs
+.idea
--- /dev/null
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${workspaceFolder}/build/libgit2_clar",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${fileDirname}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build",
+ "type": "shell",
+ "command": "cd build && cmake --build . --parallel",
+ "group": "build",
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
+ },
+ {
+ "label": "Run Tests",
+ "type": "shell",
+ "command": "build/libgit2_clar -v",
+ "group": "test",
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
+ }
+ ]
+ }
\ No newline at end of file
CMAKE_MINIMUM_REQUIRED(VERSION 3.5.1)
-project(libgit2 VERSION "1.1.0" LANGUAGES C)
+project(libgit2 VERSION "1.3.0" LANGUAGES C)
# Add find modules to the path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${libgit2_SOURCE_DIR}/cmake/")
INCLUDE(FindPkgLibraries)
INCLUDE(FindThreads)
INCLUDE(FindStatNsec)
+INCLUDE(Findfutimens)
INCLUDE(GNUInstallDirs)
INCLUDE(IdeSplitSources)
INCLUDE(FeatureSummary)
OPTION(USE_STANDALONE_FUZZERS "Enable standalone fuzzers (compatible with gcc)" OFF)
OPTION(USE_LEAK_CHECKER "Run tests with leak checker" OFF)
OPTION(DEBUG_POOL "Enable debug pool allocator" OFF)
+OPTION(DEBUG_STRICT_ALLOC "Enable strict allocator behavior" OFF)
+OPTION(DEBUG_STRICT_OPEN "Enable path validation in open" OFF)
OPTION(ENABLE_WERROR "Enable compilation with -Werror" OFF)
-OPTION(USE_BUNDLED_ZLIB "Use the bundled version of zlib" OFF)
+OPTION(USE_BUNDLED_ZLIB "Use the bundled version of zlib. Can be set to one of Bundled(ON)/Chromium. The Chromium option requires a x86_64 processor with SSE4.2 and CLMUL" OFF)
SET(USE_HTTP_PARSER "" CACHE STRING "Specifies the HTTP Parser implementation; either system or builtin.")
OPTION(DEPRECATE_HARD "Do not include deprecated functions in the library" OFF)
SET(REGEX_BACKEND "" CACHE STRING "Regular expression implementation. One of regcomp_l, pcre2, pcre, regcomp, or builtin.")
ENDIF()
IF(MSVC)
- # Enable MSVC CRTDBG memory leak reporting when in debug mode.
- OPTION(MSVC_CRTDBG "Enable CRTDBG memory leak reporting" OFF)
+ # Enable leak checking using the debugging C runtime.
+ OPTION(WIN32_LEAKCHECK "Enable leak reporting via crtdbg" OFF)
ENDIF()
IF (DEPRECATE_HARD)
SET(CRT_FLAG_RELEASE "/MD")
ENDIF()
- IF (MSVC_CRTDBG)
- SET(GIT_MSVC_CRTDBG 1)
+ IF (WIN32_LEAKCHECK)
+ SET(GIT_WIN32_LEAKCHECK 1)
SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG}")
SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} Dbghelp.lib")
ENDIF()
enable_warnings(unused-const-variable)
enable_warnings(unused-function)
enable_warnings(int-conversion)
+ enable_warnings(c11-extensions)
+ enable_warnings(c99-c11-compat)
# MinGW uses gcc, which expects POSIX formatting for printf, but
# uses the Windows C library, which uses its own format specifiers.
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
-
+
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+----------------------------------------------------------------------
+
+Portions of the OpenSSL headers are included under the OpenSSL license:
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+====================================================================
+Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
| Build Status | |
| ------------ | - |
-| **master** branch CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=master)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=master) |
-| **v1.0 branch** CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=maint/v1.0)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=maint/v1.0) |
-| **v0.28 branch** CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=maint/v0.28)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=maint/v0.28) |
-| **Nightly** builds | [![Azure Pipelines Build Status](https://libgit2.visualstudio.com/libgit2/_apis/build/status/nightly?branchName=master&label=Full+Build)](https://libgit2.visualstudio.com/libgit2/_build/latest?definitionId=9&branchName=master) [![Coverity Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/coverity?branchName=master&label=Coverity+Build)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=21?branchName=master) [![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639) |
+| **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.2 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.2&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.2) |
+| **v1.1 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.1&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.1) |
+| **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
provided as a linkable library with a solid API, allowing to build Git
Table of Contents
=================
+* [Using libgit2](#using-libgit2)
* [Quick Start](#quick-start)
* [Getting Help](#getting-help)
* [What It Can Do](#what-it-can-do)
* [How Can I Contribute?](#how-can-i-contribute)
* [License](#license)
+Using libgit2
+=============
+
+Most of these instructions assume that you're writing an application
+in C and want to use libgit2 directly. If you're _not_ using C,
+and you're writing in a different language or platform like .NET,
+Node.js, or Ruby, then there is probably a
+"[language binding](#language-bindings)" that you can use to take care
+of the messy tasks of calling into native code.
+
+But if you _do_ want to use libgit2 directly - because you're building
+an application in C - then you may be able use an existing binary.
+There are packages for the
+[vcpkg](https://github.com/Microsoft/vcpkg) and
+[conan](https://conan.io/center/libgit2)
+package managers. And libgit2 is available in
+[Homebrew](https://formulae.brew.sh/formula/libgit2) and most Linux
+distributions.
+
+However, these versions _may_ be outdated and we recommend using the
+latest version if possible. Thankfully libgit2 is not hard to compile.
+
Quick Start
===========
**Chat with us**
-- via IRC: join [#libgit2](https://webchat.freenode.net/#libgit2) on Freenode
+- via IRC: join [#libgit2](https://web.libera.chat/#libgit2) on
+ [libera](https://libera.chat).
- via Slack: visit [slack.libgit2.org](http://slack.libgit2.org/) to sign up,
then join us in `#libgit2`
Alternatively you can point the CMake GUI tool to the CMakeLists.txt file and generate platform specific build project or IDE workspace.
+If you're not familiar with CMake, [a more detailed explanation](https://preshing.com/20170511/how-to-build-a-cmake-based-project/) may be helpful.
+
Running Tests
-------------
$ ./libgit2_clar -sindex
To run a single test named `index::racy::diff`, which corresponds to the test
-function [`test_index_racy__diff`](https://github.com/libgit2/libgit2/blob/master/tests/index/racy.c#L23):
+function [`test_index_racy__diff`](https://github.com/libgit2/libgit2/blob/main/tests/index/racy.c#L23):
$ ./libgit2_clar -sindex::racy::diff
**Note:** There should be _no_ failing tests when you build an unmodified
source tree from a [release](https://github.com/libgit2/libgit2/releases),
-or from the [master branch](https://github.com/libgit2/libgit2/tree/master).
+or from the [main branch](https://github.com/libgit2/libgit2/tree/main).
Please contact us or [open an issue](https://github.com/libgit2/libgit2/issues)
if you see test failures.
* dlibgit <https://github.com/s-ludwig/dlibgit>
* Delphi
* GitForDelphi <https://github.com/libgit2/GitForDelphi>
+ * libgit2-delphi <https://github.com/todaysoftware/libgit2-delphi>
* Erlang
* Geef <https://github.com/carlosmn/geef>
* Go
* hgit2 <https://github.com/jwiegley/gitlib>
* Java
* Jagged <https://github.com/ethomson/jagged>
+ * Git24j <https://github.com/git24j/git24j>
* Javascript / WebAssembly ( browser and nodejs )
* WASM-git <https://github.com/petersalomonsen/wasm-git>
* Julia
* Python
* pygit2 <https://github.com/libgit2/pygit2>
* R
+ * gert <https://docs.ropensci.org/gert>
* git2r <https://github.com/ropensci/git2r>
* Ruby
* Rugged <https://github.com/libgit2/rugged>
+++ /dev/null
-resources:
-- repo: self
-
-trigger:
-- master
-- maint/*
-
-jobs:
-- job: linux_amd64_xenial_gcc_openssl
- displayName: 'Linux (amd64; Xenial; GCC; OpenSSL)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: azure-pipelines/docker.yml
- parameters:
- docker:
- image: xenial
- base: ubuntu:xenial
- environmentVariables: |
- CC=gcc
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
- GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }}
-
-- job: linux_amd64_xenial_gcc_mbedtls
- displayName: 'Linux (amd64; Xenial; GCC; mbedTLS)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: azure-pipelines/docker.yml
- parameters:
- docker:
- image: xenial
- base: ubuntu:xenial
- environmentVariables: |
- CC=gcc
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
- GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }}
-
-- job: linux_amd64_xenial_clang_openssl
- displayName: 'Linux (amd64; Xenial; Clang; OpenSSL)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: azure-pipelines/docker.yml
- parameters:
- docker:
- image: xenial
- base: ubuntu:xenial
- environmentVariables: |
- CC=clang
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
- GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }}
-
-- job: linux_amd64_xenial_clang_mbedtls
- displayName: 'Linux (amd64; Xenial; Clang; mbedTLS)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: azure-pipelines/docker.yml
- parameters:
- docker:
- image: xenial
- base: ubuntu:xenial
- environmentVariables: |
- CC=clang
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
- GITTEST_NEGOTIATE_PASSWORD=${{ variables.GITTEST_NEGOTIATE_PASSWORD }}
-
-- job: macos
- displayName: 'macOS (amd64; 10.15)'
- pool:
- vmImage: 'macOS-10.15'
- steps:
- - bash: . '$(Build.SourcesDirectory)/azure-pipelines/setup-osx.sh'
- displayName: Setup
- - template: azure-pipelines/bash.yml
- parameters:
- environmentVariables:
- TMPDIR: $(Agent.TempDirectory)
- PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
- CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
- SKIP_SSH_TESTS: true
- GITTEST_NEGOTIATE_PASSWORD: ${{ variables.GITTEST_NEGOTIATE_PASSWORD }}
-
-- job: windows_vs_amd64
- displayName: 'Windows (amd64; Visual Studio)'
- pool:
- vmImage: 'vs2017-win2016'
- steps:
- - template: azure-pipelines/bash.yml
- parameters:
- environmentVariables:
- CMAKE_GENERATOR: Visual Studio 15 2017
- CMAKE_OPTIONS: -A x64 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
-
-- job: windows_vs_x86
- displayName: 'Windows (x86; Visual Studio)'
- pool:
- vmImage: 'vs2017-win2016'
- steps:
- - template: azure-pipelines/bash.yml
- parameters:
- environmentVariables:
- CMAKE_GENERATOR: Visual Studio 15 2017
- CMAKE_OPTIONS: -A Win32 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
-
-- job: windows_mingw_amd64
- displayName: 'Windows (amd64; MinGW)'
- pool:
- vmImage: 'vs2017-win2016'
- steps:
- - bash: . '$(Build.SourcesDirectory)\azure-pipelines\setup-mingw.sh'
- displayName: Setup
- env:
- TEMP: $(Agent.TempDirectory)
- ARCH: amd64
- - template: azure-pipelines/bash.yml
- parameters:
- environmentVariables:
- BUILD_PATH: $(Agent.TempDirectory)\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
- CMAKE_GENERATOR: MinGW Makefiles
- CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
-
-- job: windows_mingw_x86
- displayName: 'Windows (x86; MinGW)'
- pool:
- vmImage: 'vs2017-win2016'
- steps:
- - bash: . '$(Build.SourcesDirectory)\azure-pipelines\setup-mingw.sh'
- displayName: Setup
- workingDirectory: '$(Build.BinariesDirectory)'
- env:
- TEMP: $(Agent.TempDirectory)
- ARCH: x86
- - template: azure-pipelines/bash.yml
- parameters:
- environmentVariables:
- BUILD_PATH: $(Agent.TempDirectory)\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
- CMAKE_GENERATOR: MinGW Makefiles
- CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
+++ /dev/null
-# These are the steps used for building on machines with bash.
-steps:
-- bash: . '$(Build.SourcesDirectory)/azure-pipelines/build.sh'
- displayName: Build
- workingDirectory: '$(Build.BinariesDirectory)'
- env: ${{ parameters.environmentVariables }}
-- bash: . '$(Build.SourcesDirectory)/azure-pipelines/test.sh'
- displayName: Test
- workingDirectory: '$(Build.BinariesDirectory)'
- env: ${{ parameters.environmentVariables }}
-- task: PublishTestResults@2
- displayName: Publish Test Results
- condition: succeededOrFailed()
- inputs:
- testResultsFiles: 'results_*.xml'
- searchFolder: '$(Build.BinariesDirectory)'
- mergeTestResults: true
+++ /dev/null
-#!/usr/bin/env bash
-#
-# Environment variables:
-#
-# SOURCE_DIR: Set to the directory of the libgit2 source (optional)
-# If not set, it will be derived relative to this script.
-
-set -e
-
-SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
-BUILD_DIR=$(pwd)
-BUILD_PATH=${BUILD_PATH:=$PATH}
-CMAKE=$(which cmake)
-CMAKE_GENERATOR=${CMAKE_GENERATOR:-Unix Makefiles}
-
-if [[ "$(uname -s)" == MINGW* ]]; then
- BUILD_PATH=$(cygpath "$BUILD_PATH")
-fi
-
-indent() { sed "s/^/ /"; }
-
-echo "Source directory: ${SOURCE_DIR}"
-echo "Build directory: ${BUILD_DIR}"
-echo ""
-
-if [ "$(uname -s)" = "Darwin" ]; then
- echo "macOS version:"
- sw_vers | indent
-fi
-
-if [ -f "/etc/debian_version" ]; then
- echo "Debian version:"
- (source /etc/lsb-release && echo "${DISTRIB_DESCRIPTION}") | indent
-fi
-
-echo "Kernel version:"
-uname -a 2>&1 | indent
-
-echo "CMake version:"
-env PATH="${BUILD_PATH}" "${CMAKE}" --version 2>&1 | indent
-
-if test -n "${CC}"; then
- echo "Compiler version:"
- "${CC}" --version 2>&1 | indent
-fi
-echo "Environment:"
-if test -n "${CC}"; then
- echo "CC=${CC}" | indent
-fi
-if test -n "${CFLAGS}"; then
- echo "CFLAGS=${CFLAGS}" | indent
-fi
-echo ""
-
-echo "##############################################################################"
-echo "## Configuring build environment"
-echo "##############################################################################"
-
-echo cmake -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\"
-env PATH="${BUILD_PATH}" "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G "${CMAKE_GENERATOR}" ${CMAKE_OPTIONS} -S "${SOURCE_DIR}"
-
-echo ""
-echo "##############################################################################"
-echo "## Building libgit2"
-echo "##############################################################################"
-
-env PATH="${BUILD_PATH}" "${CMAKE}" --build .
+++ /dev/null
-#!/bin/bash -e
-
-if test -z "$COVERITY_TOKEN"
-then
- echo "Need to set a coverity token"
- exit 1
-fi
-
-case $(uname -m) in
- i?86)
- BITS=32;;
- amd64|x86_64)
- BITS=64;;
- *)
- echo "Unsupported arch '$(uname -m)'"
- exit 1;;
-esac
-
-SCAN_TOOL=https://scan.coverity.com/download/cxx/linux${BITS}
-SOURCE_DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")"/..)
-BUILD_DIR=${SOURCE_DIR}/coverity-build
-TOOL_DIR=${BUILD_DIR}/coverity-tools
-
-# Install coverity tools
-if ! test -d "$TOOL_DIR"
-then
- mkdir -p "$TOOL_DIR"
- curl --silent --show-error --location --data "project=libgit2&token=$COVERITY_TOKEN" "$SCAN_TOOL" |
- tar -xzC "$TOOL_DIR"
- ln -s "$(find "$TOOL_DIR" -type d -name 'cov-analysis*')" "$TOOL_DIR"/cov-analysis
-fi
-
-cp "${SOURCE_DIR}/script/user_nodefs.h" "$TOOL_DIR"/cov-analysis/config/
-
-# Build libgit2 with Coverity
-mkdir -p "$BUILD_DIR"
-cd "$BUILD_DIR"
-cmake "$SOURCE_DIR"
-COVERITY_UNSUPPORTED=1 \
- "$TOOL_DIR/cov-analysis/bin/cov-build" --dir cov-int \
- cmake --build .
-
-# Upload results
-tar -czf libgit2.tgz cov-int
-REVISION=$(cd ${SOURCE_DIR} && git rev-parse --short HEAD)
-HTML="$(curl \
- --silent --show-error \
- --write-out "\n%{http_code}" \
- --form token="$COVERITY_TOKEN" \
- --form email=libgit2@gmail.com \
- --form file=@libgit2.tgz \
- --form version="$REVISION" \
- --form description="libgit2 build" \
- https://scan.coverity.com/builds?project=libgit2)"
-
-# Status code is the last line
-STATUS_CODE="$(echo "$HTML" | tail -n1)"
-if test "${STATUS_CODE}" != 200 && test "${STATUS_CODE}" != 201
-then
- echo "Received error code ${STATUS_CODE} from Coverity"
- exit 1
-fi
+++ /dev/null
-resources:
-- repo: self
-
-jobs:
-- job: coverity
- displayName: 'Coverity'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - script: |
- cd $(Build.SourcesDirectory)/azure-pipelines/docker
- docker build -t libgit2/xenial --build-arg BASE=ubuntu:xenial -f xenial .
- displayName: 'Build Docker image'
- - task: Docker@0
- displayName: Analyze
- inputs:
- action: 'Run an image'
- imageName: libgit2/xenial
- volumes: |
- $(Build.SourcesDirectory):/home/libgit2/source
- $(Build.BinariesDirectory):/home/libgit2/build
- envVars: |
- COVERITY_TOKEN=$(COVERITY_TOKEN)
- workDir: '/home/libgit2/build'
- containerCommand: '/home/libgit2/source/azure-pipelines/coverity.sh'
- detached: false
+++ /dev/null
-# These are the steps used in a container-based build in VSTS.
-steps:
-- ${{ if eq(parameters.qemu, 'true') }}:
- - script: docker run --rm --privileged multiarch/qemu-user-static:register --reset
- displayName: 'Register Docker QEMU'
-
-- task: cache@2
- displayName: Cache Docker layers
- inputs:
- key: docker
- path: /tmp/dockercache
-- script: |
- if [ -f /tmp/dockercache/${{parameters.docker.image}}.tar ]; then docker load < /tmp/dockercache/${{parameters.docker.image}}.tar; fi
- displayName: 'Load Docker cache'
-- script: |
- cd $(Build.SourcesDirectory)/azure-pipelines/docker &&
- docker build -t libgit2/${{parameters.docker.image}} --build-arg BASE=${{parameters.docker.base}} -f ${{parameters.docker.image}} . &&
- if [ ! -d /tmp/dockercache ]; then mkdir /tmp/dockercache; fi &&
- docker save libgit2/${{parameters.docker.image}} $(docker history -q libgit2/${{parameters.docker.image}} | grep -v '<missing>') > /tmp/dockercache/${{parameters.docker.image}}.tar
- displayName: 'Build Docker image'
-- task: docker@0
- displayName: Build
- inputs:
- action: 'Run an image'
- imageName: libgit2/${{ parameters.docker.image }}
- volumes: |
- $(Build.SourcesDirectory):/home/libgit2/source
- $(Build.BinariesDirectory):/home/libgit2/build
- envVars: ${{ parameters.environmentVariables }}
- workDir: '/home/libgit2/build'
- containerCommand: '/home/libgit2/source/azure-pipelines/build.sh'
- detached: false
-- task: docker@0
- displayName: Test
- inputs:
- action: 'Run an image'
- imageName: libgit2/${{ parameters.docker.image }}
- volumes: |
- $(Build.SourcesDirectory):/home/libgit2/source
- $(Build.BinariesDirectory):/home/libgit2/build
- envVars: ${{ parameters.environmentVariables }}
- workDir: '/home/libgit2/build'
- containerCommand: '/home/libgit2/source/azure-pipelines/test.sh'
- detached: false
-- task: publishtestresults@2
- displayName: Publish Test Results
- condition: succeededOrFailed()
- inputs:
- testResultsFiles: 'results_*.xml'
- searchFolder: '$(Build.BinariesDirectory)'
- mergeTestResults: true
+++ /dev/null
-FROM ubuntu:bionic AS apt
-RUN apt-get update && \
- DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
- clang \
- cmake \
- curl \
- gcc \
- git \
- libcurl4-openssl-dev \
- libpcre3-dev \
- libssh2-1-dev \
- libssl-dev \
- libz-dev \
- ninja-build \
- openjdk-8-jre-headless \
- openssh-server \
- openssl \
- pkgconf \
- python \
- sudo \
- valgrind \
- && \
- rm -rf /var/lib/apt/lists/*
-
-FROM apt AS mbedtls
-RUN cd /tmp && \
- curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
- tar -xz && \
- cd mbedtls-2.16.2 && \
- scripts/config.pl set MBEDTLS_MD4_C 1 && \
- CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
- ninja install && \
- cd .. && \
- rm -rf mbedtls-2.16.2
-
-FROM mbedtls AS configure
-COPY entrypoint.sh /usr/local/bin/entrypoint.sh
-RUN chmod a+x /usr/local/bin/entrypoint.sh
-RUN mkdir /var/run/sshd
-
-ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
+++ /dev/null
-FROM ubuntu:bionic
-RUN apt update && apt install -y cmake pkg-config ruby ruby-dev llvm libclang-dev libssl-dev python-pygments
-RUN gem install docurium
+++ /dev/null
-#!/bin/bash -e
-useradd --shell /bin/bash libgit2
-chown --recursive libgit2:libgit2 /home/libgit2
-exec sudo --preserve-env --set-home --user=libgit2 "$@"
+++ /dev/null
-FROM ubuntu:focal AS apt
-RUN apt-get update && \
- DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
- bzip2 \
- clang-10 \
- cmake \
- curl \
- gcc-10 \
- git \
- krb5-user \
- libcurl4-gnutls-dev \
- libgcrypt20-dev \
- libkrb5-dev \
- libpcre3-dev \
- libssl-dev \
- libz-dev \
- llvm-10 \
- make \
- ninja-build \
- openjdk-8-jre-headless \
- openssh-server \
- openssl \
- pkgconf \
- python \
- sudo \
- valgrind \
- && \
- rm -rf /var/lib/apt/lists/* && \
- mkdir /usr/local/msan
-
-FROM apt AS mbedtls
-RUN cd /tmp && \
- curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
- tar -xz && \
- cd mbedtls-2.16.2 && \
- scripts/config.pl unset MBEDTLS_AESNI_C && \
- scripts/config.pl set MBEDTLS_MD4_C 1 && \
- mkdir build build-msan && \
- cd build && \
- CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
- ninja install && \
- cd ../build-msan && \
- CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=MemSanDbg -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
- ninja install && \
- cd .. && \
- rm -rf mbedtls-2.16.2
-
-FROM mbedtls AS libssh2
-RUN cd /tmp && \
- curl --insecure --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.2.tar.gz | \
- tar -xz && \
- cd libssh2-1.8.2 && \
- mkdir build build-msan && \
- cd build && \
- CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
- ninja install && \
- cd ../build-msan && \
- CC=clang-10 CFLAGS="-fPIC -fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer" LDFLAGS="-fsanitize=memory" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=mbedTLS -DCMAKE_PREFIX_PATH=/usr/local/msan -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
- ninja install && \
- cd .. && \
- rm -rf libssh2-1.8.2
-
-FROM libssh2 AS valgrind
-RUN cd /tmp && \
- curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
- tar -xj && \
- cd valgrind-3.15.0 && \
- CC=clang-10 ./configure && \
- make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
- make install && \
- cd .. && \
- rm -rf valgrind-3.15.0
-
-FROM valgrind AS configure
-COPY entrypoint.sh /usr/local/bin/entrypoint.sh
-RUN chmod a+x /usr/local/bin/entrypoint.sh
-RUN mkdir /var/run/sshd
-
-ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
+++ /dev/null
-FROM ubuntu:xenial AS apt
-RUN apt-get update && \
- DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
- bzip2 \
- clang \
- cmake \
- curl \
- gcc \
- git \
- krb5-user \
- libcurl4-gnutls-dev \
- libgcrypt20-dev \
- libkrb5-dev \
- libpcre3-dev \
- libssl-dev \
- libz-dev \
- make \
- ninja-build \
- openjdk-8-jre-headless \
- openssh-server \
- openssl \
- pkgconf \
- python \
- sudo \
- valgrind \
- && \
- rm -rf /var/lib/apt/lists/*
-
-FROM apt AS mbedtls
-RUN cd /tmp && \
- curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
- tar -xz && \
- cd mbedtls-2.16.2 && \
- scripts/config.pl set MBEDTLS_MD4_C 1 && \
- CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
- ninja install && \
- cd .. && \
- rm -rf mbedtls-2.16.2
-
-FROM mbedtls AS libssh2
-RUN cd /tmp && \
- curl --insecure --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.2.tar.gz | \
- tar -xz && \
- cd libssh2-1.8.2 && \
- CFLAGS=-fPIC cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt . && \
- ninja install && \
- cd .. && \
- rm -rf libssh2-1.8.2
-
-FROM libssh2 AS valgrind
-RUN cd /tmp && \
- curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
- tar -xj && \
- cd valgrind-3.15.0 && \
- ./configure && \
- make && \
- make install && \
- cd .. && \
- rm -rf valgrind-3.15.0
-
-FROM valgrind AS configure
-COPY entrypoint.sh /usr/local/bin/entrypoint.sh
-RUN chmod a+x /usr/local/bin/entrypoint.sh
-RUN mkdir /var/run/sshd
-
-ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
+++ /dev/null
-#!/bin/bash
-
-set -e
-
-DOCKERFILE_PATH=$1
-
-if [ "${DOCKERFILE_PATH}" = "" ]; then
- echo "usage: $0 dockerfile"
- exit 1
-fi
-
-if [ "${DOCKER_REGISTRY}" = "" ]; then
- echo "DOCKER_REGISTRY environment variable is unset."
- echo "Not running inside GitHub Actions or misconfigured?"
- exit 1
-fi
-
-DOCKER_CONTAINER="${GITHUB_REPOSITORY}/$(basename ${DOCKERFILE_PATH})"
-DOCKER_REGISTRY_CONTAINER="${DOCKER_REGISTRY}/${DOCKER_CONTAINER}"
-
-echo "::set-env name=docker-container::${DOCKER_CONTAINER}"
-echo "::set-env name=docker-registry-container::${DOCKER_REGISTRY_CONTAINER}"
-
-# Identify the last git commit that touched the Dockerfiles
-# Use this as a hash to identify the resulting docker containers
-DOCKER_SHA=$(git log -1 --pretty=format:"%h" -- "${DOCKERFILE_PATH}")
-echo "::set-env name=docker-sha::${DOCKER_SHA}"
-
-DOCKER_REGISTRY_CONTAINER_SHA="${DOCKER_REGISTRY_CONTAINER}:${DOCKER_SHA}"
-
-echo "::set-env name=docker-registry-container-sha::${DOCKER_REGISTRY_CONTAINER_SHA}"
-echo "::set-env name=docker-registry-container-latest::${DOCKER_REGISTRY_CONTAINER}:latest"
-
-exists="true"
-docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false"
-
-if [ "${exists}" != "false" ]; then
- docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false"
-fi
-
-if [ "${exists}" = "true" ]; then
- echo "::set-env name=docker-container-exists::true"
-else
- echo "::set-env name=docker-container-exists::false"
-fi
+++ /dev/null
-resources:
-- repo: self
-
-jobs:
-- job: linux_amd64_xenial_gcc_openssl
- displayName: 'Linux (amd64; Xenial; GCC; OpenSSL)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: docker.yml
- parameters:
- docker:
- image: xenial
- base: ubuntu:xenial
- environmentVariables: |
- CC=gcc
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind
- RUN_INVASIVE_TESTS=true
-
-- job: linux_amd64_xenial_gcc_mbedtls
- displayName: 'Linux (amd64; Xenial; GCC; mbedTLS)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: docker.yml
- parameters:
- docker:
- image: xenial
- base: ubuntu:xenial
- environmentVariables: |
- CC=gcc
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind
- RUN_INVASIVE_TESTS=true
-
-- job: linux_amd64_xenial_clang_openssl
- displayName: 'Linux (amd64; Xenial; Clang; OpenSSL)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: docker.yml
- parameters:
- docker:
- image: xenial
- base: ubuntu:xenial
- environmentVariables: |
- CC=clang
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind
- RUN_INVASIVE_TESTS=true
-
-- job: linux_amd64_xenial_clang_mbedtls
- displayName: 'Linux (amd64; Xenial; Clang; mbedTLS)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: docker.yml
- parameters:
- docker:
- image: xenial
- base: ubuntu:xenial
- environmentVariables: |
- CC=clang
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind
- RUN_INVASIVE_TESTS=true
-
-- job: macos
- displayName: 'macOS (amd64; 10.15)'
- pool:
- vmImage: 'macOS-10.15'
- steps:
- - bash: . '$(Build.SourcesDirectory)/azure-pipelines/setup-osx.sh'
- displayName: Setup
- - template: bash.yml
- parameters:
- environmentVariables:
- TMPDIR: $(Agent.TempDirectory)
- PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
- CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
- RUN_INVASIVE_TESTS: true
- SKIP_SSH_TESTS: true
-
-- job: windows_vs_amd64
- displayName: 'Windows (amd64; Visual Studio)'
- pool:
- vmImage: 'vs2017-win2016'
- steps:
- - template: bash.yml
- parameters:
- environmentVariables:
- CMAKE_GENERATOR: Visual Studio 15 2017
- CMAKE_OPTIONS: -A x64 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON
- RUN_INVASIVE_TESTS: true
- SKIP_SSH_TESTS: true
-
-- job: windows_vs_x86
- displayName: 'Windows (x86; Visual Studio)'
- pool:
- vmImage: 'vs2017-win2016'
- steps:
- - template: bash.yml
- parameters:
- environmentVariables:
- CMAKE_GENERATOR: Visual Studio 15 2017
- CMAKE_OPTIONS: -A Win32 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS
- RUN_INVASIVE_TESTS: true
- SKIP_SSH_TESTS: true
-
-- job: windows_mingw_amd64
- displayName: 'Windows (amd64; MinGW)'
- pool:
- vmImage: 'vs2017-win2016'
- steps:
- - bash: . '$(Build.SourcesDirectory)\azure-pipelines\setup-mingw.sh'
- displayName: Setup
- env:
- TEMP: $(Agent.TempDirectory)
- ARCH: amd64
- - template: bash.yml
- parameters:
- environmentVariables:
- BUILD_PATH: $(Agent.TempDirectory)\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
- CMAKE_GENERATOR: MinGW Makefiles
- CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
- RUN_INVASIVE_TESTS: true
- SKIP_SSH_TESTS: true
-
-- job: windows_mingw_x86
- displayName: 'Windows (x86; MinGW)'
- pool:
- vmImage: 'vs2017-win2016'
- steps:
- - bash: . '$(Build.SourcesDirectory)\azure-pipelines\setup-mingw.sh'
- displayName: Setup
- workingDirectory: '$(Build.BinariesDirectory)'
- env:
- TEMP: $(Agent.TempDirectory)
- ARCH: x86
- - template: bash.yml
- parameters:
- environmentVariables:
- BUILD_PATH: $(Agent.TempDirectory)\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
- CMAKE_GENERATOR: MinGW Makefiles
- CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
- RUN_INVASIVE_TESTS: true
- SKIP_SSH_TESTS: true
-
-- job: linux_x86_bionic_gcc_openssl
- displayName: 'Linux (x86; Bionic; GCC; OpenSSL)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: docker.yml
- parameters:
- qemu: 'true'
- docker:
- image: bionic
- base: multiarch/ubuntu-core:x86-bionic
- environmentVariables: |
- CC=gcc
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind
- RUN_INVASIVE_TESTS=true
-
-- job: linux_x86_bionic_clang_openssl
- displayName: 'Linux (x86; Bionic; Clang; OpenSSL)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: docker.yml
- parameters:
- qemu: 'true'
- docker:
- image: bionic
- base: multiarch/ubuntu-core:x86-bionic
- environmentVariables: |
- CC=clang
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind
- RUN_INVASIVE_TESTS=true
-
-- job: linux_arm32_bionic_gcc_openssl
- displayName: 'Linux (arm32; Bionic; GCC; OpenSSL)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: docker.yml
- parameters:
- qemu: 'true'
- docker:
- image: bionic
- base: multiarch/ubuntu-core:armhf-bionic
- environmentVariables: |
- CC=gcc
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON
- RUN_INVASIVE_TESTS=true
- SKIP_PROXY_TESTS=true
-
-- job: linux_arm64_bionic_gcc_openssl
- displayName: 'Linux (arm64; Bionic; GCC; OpenSSL)'
- pool:
- vmImage: 'ubuntu-18.04'
- steps:
- - template: docker.yml
- parameters:
- qemu: 'true'
- docker:
- image: bionic
- base: multiarch/ubuntu-core:arm64-bionic
- environmentVariables: |
- CC=gcc
- CMAKE_GENERATOR=Ninja
- CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON
- RUN_INVASIVE_TESTS=true
- SKIP_PROXY_TESTS=true
+++ /dev/null
-#!/bin/sh -e
-
-echo "##############################################################################"
-echo "## Downloading mingw"
-echo "##############################################################################"
-
-BUILD_TEMP=${BUILD_TEMP:=$TEMP}
-BUILD_TEMP=$(cygpath $BUILD_TEMP)
-
-case "$ARCH" in
- amd64)
- MINGW_URI="https://bintray.com/libgit2/build-dependencies/download_file?file_path=mingw-w64-x86_64-8.1.0-release-win32-seh-rt_v6-rev0.zip";;
- x86)
- MINGW_URI="https://bintray.com/libgit2/build-dependencies/download_file?file_path=mingw-w64-i686-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
-esac
-
-if [ -z "$MINGW_URI" ]; then
- echo "No URL"
- exit 1
-fi
-
-mkdir -p "$BUILD_TEMP"
-
-curl -s -L "$MINGW_URI" -o "$BUILD_TEMP"/mingw-"$ARCH".zip
-unzip -q "$BUILD_TEMP"/mingw-"$ARCH".zip -d "$BUILD_TEMP"
+++ /dev/null
-#!/bin/sh
-
-set -x
-
-brew update
-brew install pkgconfig zlib curl openssl libssh2 ninja
-
-ln -s /Applications/Xcode.app/Contents/Developer/usr/lib/libLeaksAtExit.dylib /usr/local/lib
+++ /dev/null
-#!/usr/bin/env bash
-
-set -e
-
-if [ -n "$SKIP_TESTS" ]; then
- exit 0
-fi
-
-# Windows doesn't run the NTLM tests properly (yet)
-if [[ "$(uname -s)" == MINGW* ]]; then
- SKIP_NTLM_TESTS=1
-fi
-
-SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
-BUILD_DIR=$(pwd)
-TMPDIR=${TMPDIR:-/tmp}
-USER=${USER:-$(whoami)}
-
-SUCCESS=1
-
-cleanup() {
- echo "Cleaning up..."
-
- if [ ! -z "$GITDAEMON_PID" ]; then
- echo "Stopping git daemon..."
- kill $GITDAEMON_PID
- fi
-
- if [ ! -z "$SSHD_DIR" -a -f "${SSHD_DIR}/pid" ]; then
- echo "Stopping SSH..."
- kill $(cat "${SSHD_DIR}/pid")
- fi
-
- echo "Done."
-}
-
-run_test() {
- if [[ "$GITTEST_FLAKY_RETRY" > 0 ]]; then
- ATTEMPTS_REMAIN=$GITTEST_FLAKY_RETRY
- else
- ATTEMPTS_REMAIN=1
- fi
-
- FAILED=0
- while [[ "$ATTEMPTS_REMAIN" > 0 ]]; do
- if [ "$FAILED" -eq 1 ]; then
- echo ""
- echo "Re-running flaky ${1} tests..."
- echo ""
- fi
-
- RETURN_CODE=0
-
- CLAR_SUMMARY="${BUILD_DIR}/results_${1}.xml" ctest -V -R "^${1}$" || RETURN_CODE=$? && true
-
- if [ "$RETURN_CODE" -eq 0 ]; then
- FAILED=0
- break
- fi
-
- echo "Test exited with code: $RETURN_CODE"
- ATTEMPTS_REMAIN="$(($ATTEMPTS_REMAIN-1))"
- FAILED=1
- done
-
- if [ "$FAILED" -ne 0 ]; then
- SUCCESS=0
- fi
-}
-
-# Configure the test environment; run them early so that we're certain
-# that they're started by the time we need them.
-
-echo "##############################################################################"
-echo "## Configuring test environment"
-echo "##############################################################################"
-
-if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
- echo "Starting git daemon..."
- GITDAEMON_DIR=`mktemp -d ${TMPDIR}/gitdaemon.XXXXXXXX`
- git init --bare "${GITDAEMON_DIR}/test.git"
- git daemon --listen=localhost --export-all --enable=receive-pack --base-path="${GITDAEMON_DIR}" "${GITDAEMON_DIR}" 2>/dev/null &
- GITDAEMON_PID=$!
- disown $GITDAEMON_PID
-fi
-
-if [ -z "$SKIP_PROXY_TESTS" ]; then
- curl --location --silent --show-error https://github.com/ethomson/poxyproxy/releases/download/v0.7.0/poxyproxy-0.7.0.jar >poxyproxy.jar
-
- echo ""
- echo "Starting HTTP proxy (Basic)..."
- java -jar poxyproxy.jar --address 127.0.0.1 --port 8080 --credentials foo:bar --auth-type basic --quiet &
-
- echo ""
- echo "Starting HTTP proxy (NTLM)..."
- java -jar poxyproxy.jar --address 127.0.0.1 --port 8090 --credentials foo:bar --auth-type ntlm --quiet &
-fi
-
-if [ -z "$SKIP_NTLM_TESTS" ]; then
- curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.4.0/poxygit-0.4.0.jar >poxygit.jar
-
- echo ""
- echo "Starting HTTP server..."
- NTLM_DIR=`mktemp -d ${TMPDIR}/ntlm.XXXXXXXX`
- git init --bare "${NTLM_DIR}/test.git"
- java -jar poxygit.jar --address 127.0.0.1 --port 9000 --credentials foo:baz --quiet "${NTLM_DIR}" &
-fi
-
-if [ -z "$SKIP_SSH_TESTS" ]; then
- echo "Starting ssh daemon..."
- HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
- SSHD_DIR=`mktemp -d ${TMPDIR}/sshd.XXXXXXXX`
- git init --bare "${SSHD_DIR}/test.git"
- cat >"${SSHD_DIR}/sshd_config" <<-EOF
- Port 2222
- ListenAddress 0.0.0.0
- Protocol 2
- HostKey ${SSHD_DIR}/id_rsa
- PidFile ${SSHD_DIR}/pid
- AuthorizedKeysFile ${HOME}/.ssh/authorized_keys
- LogLevel DEBUG
- RSAAuthentication yes
- PasswordAuthentication yes
- PubkeyAuthentication yes
- ChallengeResponseAuthentication no
- StrictModes no
- # Required here as sshd will simply close connection otherwise
- UsePAM no
- EOF
- ssh-keygen -t rsa -f "${SSHD_DIR}/id_rsa" -N "" -q
- /usr/sbin/sshd -f "${SSHD_DIR}/sshd_config" -E "${SSHD_DIR}/log"
-
- # Set up keys
- mkdir "${HOME}/.ssh"
- ssh-keygen -t rsa -f "${HOME}/.ssh/id_rsa" -N "" -q
- cat "${HOME}/.ssh/id_rsa.pub" >>"${HOME}/.ssh/authorized_keys"
- while read algorithm key comment; do
- echo "[localhost]:2222 $algorithm $key" >>"${HOME}/.ssh/known_hosts"
- done <"${SSHD_DIR}/id_rsa.pub"
-
- # Get the fingerprint for localhost and remove the colons so we can
- # parse it as a hex number. Older versions have a different output
- # format.
- if [[ $(ssh -V 2>&1) == OpenSSH_6* ]]; then
- SSH_FINGERPRINT=$(ssh-keygen -F '[localhost]:2222' -f "${HOME}/.ssh/known_hosts" -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
- else
- SSH_FINGERPRINT=$(ssh-keygen -E md5 -F '[localhost]:2222' -f "${HOME}/.ssh/known_hosts" -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
- fi
-fi
-
-# Run the tests that do not require network connectivity.
-
-if [ -z "$SKIP_OFFLINE_TESTS" ]; then
- echo ""
- echo "##############################################################################"
- echo "## Running (offline) tests"
- echo "##############################################################################"
-
- run_test offline
-fi
-
-if [ -n "$RUN_INVASIVE_TESTS" ]; then
- echo ""
- echo "Running invasive tests"
- echo ""
-
- export GITTEST_INVASIVE_FS_SIZE=1
- export GITTEST_INVASIVE_MEMORY=1
- export GITTEST_INVASIVE_SPEED=1
- run_test invasive
- unset GITTEST_INVASIVE_FS_SIZE
- unset GITTEST_INVASIVE_MEMORY
- unset GITTEST_INVASIVE_SPEED
-fi
-
-if [ -z "$SKIP_ONLINE_TESTS" ]; then
- # Run the various online tests. The "online" test suite only includes the
- # default online tests that do not require additional configuration. The
- # "proxy" and "ssh" test suites require further setup.
-
- echo ""
- echo "##############################################################################"
- echo "## Running (online) tests"
- echo "##############################################################################"
-
- export GITTEST_FLAKY_RETRY=5
- run_test online
- unset GITTEST_FLAKY_RETRY
-fi
-
-if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
- echo ""
- echo "Running gitdaemon tests"
- echo ""
-
- export GITTEST_REMOTE_URL="git://localhost/test.git"
- run_test gitdaemon
- unset GITTEST_REMOTE_URL
-fi
-
-if [ -z "$SKIP_PROXY_TESTS" ]; then
- echo ""
- echo "Running proxy tests (Basic authentication)"
- echo ""
-
- export GITTEST_REMOTE_PROXY_HOST="localhost:8080"
- export GITTEST_REMOTE_PROXY_USER="foo"
- export GITTEST_REMOTE_PROXY_PASS="bar"
- run_test proxy
- unset GITTEST_REMOTE_PROXY_HOST
- unset GITTEST_REMOTE_PROXY_USER
- unset GITTEST_REMOTE_PROXY_PASS
-
- echo ""
- echo "Running proxy tests (NTLM authentication)"
- echo ""
-
- export GITTEST_REMOTE_PROXY_HOST="localhost:8090"
- export GITTEST_REMOTE_PROXY_USER="foo"
- export GITTEST_REMOTE_PROXY_PASS="bar"
- export GITTEST_FLAKY_RETRY=5
- run_test proxy
- unset GITTEST_FLAKY_RETRY
- unset GITTEST_REMOTE_PROXY_HOST
- unset GITTEST_REMOTE_PROXY_USER
- unset GITTEST_REMOTE_PROXY_PASS
-fi
-
-if [ -z "$SKIP_NTLM_TESTS" ]; then
- echo ""
- echo "Running NTLM tests (IIS emulation)"
- echo ""
-
- export GITTEST_REMOTE_URL="http://localhost:9000/ntlm/test.git"
- export GITTEST_REMOTE_USER="foo"
- export GITTEST_REMOTE_PASS="baz"
- run_test auth_clone_and_push
- unset GITTEST_REMOTE_URL
- unset GITTEST_REMOTE_USER
- unset GITTEST_REMOTE_PASS
-
- echo ""
- echo "Running NTLM tests (Apache emulation)"
- echo ""
-
- export GITTEST_REMOTE_URL="http://localhost:9000/broken-ntlm/test.git"
- export GITTEST_REMOTE_USER="foo"
- export GITTEST_REMOTE_PASS="baz"
- run_test auth_clone_and_push
- unset GITTEST_REMOTE_URL
- unset GITTEST_REMOTE_USER
- unset GITTEST_REMOTE_PASS
-fi
-
-if [ -z "$SKIP_NEGOTIATE_TESTS" -a -n "$GITTEST_NEGOTIATE_PASSWORD" ]; then
- echo ""
- echo "Running SPNEGO tests"
- echo ""
-
- if [ "$(uname -s)" = "Darwin" ]; then
- KINIT_FLAGS="--password-file=STDIN"
- fi
-
- echo $GITTEST_NEGOTIATE_PASSWORD | kinit $KINIT_FLAGS test@LIBGIT2.ORG
- klist -5f
-
- export GITTEST_REMOTE_URL="https://test.libgit2.org/kerberos/empty.git"
- export GITTEST_REMOTE_DEFAULT="true"
- run_test auth_clone
- unset GITTEST_REMOTE_URL
- unset GITTEST_REMOTE_DEFAULT
-
- echo ""
- echo "Running SPNEGO tests (expect/continue)"
- echo ""
-
- export GITTEST_REMOTE_URL="https://test.libgit2.org/kerberos/empty.git"
- export GITTEST_REMOTE_DEFAULT="true"
- export GITTEST_REMOTE_EXPECTCONTINUE="true"
- run_test auth_clone
- unset GITTEST_REMOTE_URL
- unset GITTEST_REMOTE_DEFAULT
- unset GITTEST_REMOTE_EXPECTCONTINUE
-
- kdestroy -A
-fi
-
-if [ -z "$SKIP_SSH_TESTS" ]; then
- echo ""
- echo "Running ssh tests"
- echo ""
-
- export GITTEST_REMOTE_URL="ssh://localhost:2222/$SSHD_DIR/test.git"
- export GITTEST_REMOTE_USER=$USER
- export GITTEST_REMOTE_SSH_KEY="${HOME}/.ssh/id_rsa"
- export GITTEST_REMOTE_SSH_PUBKEY="${HOME}/.ssh/id_rsa.pub"
- export GITTEST_REMOTE_SSH_PASSPHRASE=""
- export GITTEST_REMOTE_SSH_FINGERPRINT="${SSH_FINGERPRINT}"
- run_test ssh
- unset GITTEST_REMOTE_URL
- unset GITTEST_REMOTE_USER
- unset GITTEST_REMOTE_SSH_KEY
- unset GITTEST_REMOTE_SSH_PUBKEY
- unset GITTEST_REMOTE_SSH_PASSPHRASE
- unset GITTEST_REMOTE_SSH_FINGERPRINT
-fi
-
-if [ -z "$SKIP_FUZZERS" ]; then
- echo ""
- echo "##############################################################################"
- echo "## Running fuzzers"
- echo "##############################################################################"
-
- ctest -V -R 'fuzzer'
-fi
-
-cleanup
-
-if [ "$SUCCESS" -ne 1 ]; then
- echo "Some tests failed."
- exit 1
-fi
-
-echo "Success."
-exit 0
--- /dev/null
+#!/usr/bin/env bash
+#
+# Environment variables:
+#
+# SOURCE_DIR: Set to the directory of the libgit2 source (optional)
+# If not set, it will be derived relative to this script.
+
+set -e
+
+SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
+BUILD_DIR=$(pwd)
+BUILD_PATH=${BUILD_PATH:=$PATH}
+CMAKE=$(which cmake)
+CMAKE_GENERATOR=${CMAKE_GENERATOR:-Unix Makefiles}
+
+if [[ "$(uname -s)" == MINGW* ]]; then
+ BUILD_PATH=$(cygpath "$BUILD_PATH")
+fi
+
+indent() { sed "s/^/ /"; }
+
+echo "Source directory: ${SOURCE_DIR}"
+echo "Build directory: ${BUILD_DIR}"
+echo ""
+
+if [ "$(uname -s)" = "Darwin" ]; then
+ echo "macOS version:"
+ sw_vers | indent
+fi
+
+if [ -f "/etc/debian_version" ]; then
+ echo "Debian version:"
+ (source /etc/lsb-release && echo "${DISTRIB_DESCRIPTION}") | indent
+fi
+
+CORES=$(getconf _NPROCESSORS_ONLN || true)
+echo "Number of cores: ${CORES:-(Unknown)}"
+
+echo "Kernel version:"
+uname -a 2>&1 | indent
+
+echo "CMake version:"
+env PATH="${BUILD_PATH}" "${CMAKE}" --version 2>&1 | indent
+
+if test -n "${CC}"; then
+ echo "Compiler version:"
+ "${CC}" --version 2>&1 | indent
+fi
+echo "Environment:"
+if test -n "${CC}"; then
+ echo "CC=${CC}" | indent
+fi
+if test -n "${CFLAGS}"; then
+ echo "CFLAGS=${CFLAGS}" | indent
+fi
+echo ""
+
+echo "##############################################################################"
+echo "## Configuring build environment"
+echo "##############################################################################"
+
+echo cmake -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\"
+env PATH="${BUILD_PATH}" "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G "${CMAKE_GENERATOR}" ${CMAKE_OPTIONS} -S "${SOURCE_DIR}"
+
+echo ""
+echo "##############################################################################"
+echo "## Building libgit2"
+echo "##############################################################################"
+
+# Determine parallelism; newer cmake supports `--build --parallel` but
+# we cannot yet rely on that.
+if [ "${CMAKE_GENERATOR}" = "Unix Makefiles" -a "${CORES}" != "" ]; then
+ BUILDER=(make -j ${CORES})
+else
+ BUILDER=("${CMAKE}" --build .)
+fi
+
+env PATH="${BUILD_PATH}" "${BUILDER[@]}"
--- /dev/null
+#!/bin/bash -e
+
+if test -z "$COVERITY_TOKEN"
+then
+ echo "Need to set a coverity token"
+ exit 1
+fi
+
+case $(uname -m) in
+ i?86)
+ BITS=32;;
+ amd64|x86_64)
+ BITS=64;;
+ *)
+ echo "Unsupported arch '$(uname -m)'"
+ exit 1;;
+esac
+
+SCAN_TOOL=https://scan.coverity.com/download/cxx/linux${BITS}
+SOURCE_DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")"/..)
+BUILD_DIR=${SOURCE_DIR}/coverity-build
+TOOL_DIR=${BUILD_DIR}/coverity-tools
+
+# Install coverity tools
+if ! test -d "$TOOL_DIR"
+then
+ mkdir -p "$TOOL_DIR"
+ curl --silent --show-error --location --data "project=libgit2&token=$COVERITY_TOKEN" "$SCAN_TOOL" |
+ tar -xzC "$TOOL_DIR"
+ ln -s "$(find "$TOOL_DIR" -type d -name 'cov-analysis*')" "$TOOL_DIR"/cov-analysis
+fi
+
+cp "${SOURCE_DIR}/script/user_nodefs.h" "$TOOL_DIR"/cov-analysis/config/
+
+# Build libgit2 with Coverity
+mkdir -p "$BUILD_DIR"
+cd "$BUILD_DIR"
+cmake "$SOURCE_DIR"
+COVERITY_UNSUPPORTED=1 \
+ "$TOOL_DIR/cov-analysis/bin/cov-build" --dir cov-int \
+ cmake --build .
+
+# Upload results
+tar -czf libgit2.tgz cov-int
+REVISION=$(cd ${SOURCE_DIR} && git rev-parse --short HEAD)
+HTML="$(curl \
+ --silent --show-error \
+ --write-out "\n%{http_code}" \
+ --form token="$COVERITY_TOKEN" \
+ --form email=libgit2@gmail.com \
+ --form file=@libgit2.tgz \
+ --form version="$REVISION" \
+ --form description="libgit2 build" \
+ https://scan.coverity.com/builds?project=libgit2)"
+
+# Status code is the last line
+STATUS_CODE="$(echo "$HTML" | tail -n1)"
+if test "${STATUS_CODE}" != 200 && test "${STATUS_CODE}" != 201
+then
+ echo "Received error code ${STATUS_CODE} from Coverity"
+ exit 1
+fi
--- /dev/null
+ARG BASE=ubuntu:bionic
+
+FROM ${BASE} AS apt
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ clang \
+ cmake \
+ curl \
+ gcc \
+ git \
+ krb5-user \
+ libcurl4-openssl-dev \
+ libkrb5-dev \
+ libpcre3-dev \
+ libssh2-1-dev \
+ libssl-dev \
+ libz-dev \
+ ninja-build \
+ openjdk-8-jre-headless \
+ openssh-server \
+ openssl \
+ pkgconf \
+ python \
+ sudo \
+ valgrind \
+ && \
+ rm -rf /var/lib/apt/lists/*
+
+FROM apt AS mbedtls
+RUN cd /tmp && \
+ curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
+ tar -xz && \
+ cd mbedtls-2.16.2 && \
+ scripts/config.pl set MBEDTLS_MD4_C 1 && \
+ CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
+ ninja install && \
+ cd .. && \
+ rm -rf mbedtls-2.16.2
+
+FROM mbedtls AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
+
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
--- /dev/null
+ARG BASE=centos:7
+
+FROM ${BASE} AS yum
+RUN yum install -y \
+ which \
+ bzip2 \
+ git \
+ libarchive \
+ gcc \
+ gcc-c++ \
+ make \
+ openssl-devel \
+ openssh-server \
+ git-daemon \
+ java-1.8.0-openjdk-headless \
+ sudo \
+ python
+
+FROM yum AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.0.tar.gz | tar -xz && \
+ cd libssh2-1.8.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf libssh-1.8.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS cmake
+RUN cd /tmp && \
+ curl -L https://github.com/Kitware/CMake/releases/download/v3.21.1/cmake-3.21.1.tar.gz | tar -xz && \
+ cd cmake-3.21.1 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf cmake-3.21.1
+
+FROM cmake AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
+
+FROM adduser AS configure
+ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
+RUN mkdir /var/run/sshd
--- /dev/null
+ARG BASE=centos:8
+
+FROM ${BASE} AS yum
+RUN yum install -y \
+ which \
+ bzip2 \
+ git \
+ libarchive \
+ cmake \
+ gcc \
+ make \
+ openssl-devel \
+ openssh-server \
+ git-daemon \
+ java-1.8.0-openjdk-headless \
+ sudo \
+ python39 \
+ krb5-workstation \
+ krb5-libs
+
+FROM yum AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.0.tar.gz | tar -xz && \
+ cd libssh2-1.8.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf libssh2-1.8.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
+
+FROM adduser AS configure
+ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
+RUN mkdir /var/run/sshd
+RUN echo "/usr/local/lib" > /etc/ld.so.conf.d/local && \
+ ldconfig
--- /dev/null
+ARG BASE=ubuntu:bionic
+
+FROM ${BASE}
+RUN apt update && apt install -y cmake pkg-config ruby ruby-dev llvm libclang-dev libssl-dev python-pygments
+RUN gem install docurium
--- /dev/null
+ARG BASE=ubuntu:focal
+
+FROM ${BASE} AS apt
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ bzip2 \
+ clang-10 \
+ cmake \
+ curl \
+ gcc-10 \
+ git \
+ krb5-user \
+ libcurl4-gnutls-dev \
+ libgcrypt20-dev \
+ libkrb5-dev \
+ libpcre3-dev \
+ libssl-dev \
+ libz-dev \
+ llvm-10 \
+ make \
+ ninja-build \
+ openjdk-8-jre-headless \
+ openssh-server \
+ openssl \
+ pkgconf \
+ python \
+ sudo \
+ valgrind \
+ && \
+ rm -rf /var/lib/apt/lists/* && \
+ mkdir /usr/local/msan
+
+FROM apt AS mbedtls
+RUN cd /tmp && \
+ curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
+ tar -xz && \
+ cd mbedtls-2.16.2 && \
+ scripts/config.pl unset MBEDTLS_AESNI_C && \
+ scripts/config.pl set MBEDTLS_MD4_C 1 && \
+ mkdir build build-msan && \
+ cd build && \
+ CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
+ ninja install && \
+ cd ../build-msan && \
+ CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=MemSanDbg -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
+ ninja install && \
+ cd .. && \
+ rm -rf mbedtls-2.16.2
+
+FROM mbedtls AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.9.0.tar.gz | tar -xz && \
+ cd libssh2-1.9.0 && \
+ mkdir build build-msan && \
+ cd build && \
+ CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
+ ninja install && \
+ cd ../build-msan && \
+ CC=clang-10 CFLAGS="-fPIC -fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer" LDFLAGS="-fsanitize=memory" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=mbedTLS -DCMAKE_PREFIX_PATH=/usr/local/msan -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
+ ninja install && \
+ cd .. && \
+ rm -rf libssh2-1.9.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ CC=clang-10 ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
+
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
--- /dev/null
+ARG BASE=ubuntu:xenial
+
+FROM ${BASE} AS apt
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ bzip2 \
+ clang \
+ cmake \
+ curl \
+ gcc \
+ git \
+ krb5-user \
+ libcurl4-gnutls-dev \
+ libgcrypt20-dev \
+ libkrb5-dev \
+ libpcre3-dev \
+ libssl-dev \
+ libz-dev \
+ make \
+ ninja-build \
+ openjdk-8-jre-headless \
+ openssh-server \
+ openssl \
+ pkgconf \
+ python \
+ sudo \
+ valgrind \
+ && \
+ rm -rf /var/lib/apt/lists/*
+
+FROM apt AS mbedtls
+RUN cd /tmp && \
+ curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
+ tar -xz && \
+ cd mbedtls-2.16.2 && \
+ scripts/config.pl set MBEDTLS_MD4_C 1 && \
+ CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
+ ninja install && \
+ cd .. && \
+ rm -rf mbedtls-2.16.2
+
+FROM mbedtls AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.2.tar.gz | tar -xz && \
+ cd libssh2-1.8.2 && \
+ CFLAGS=-fPIC cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt . && \
+ ninja install && \
+ cd .. && \
+ rm -rf libssh2-1.8.2
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS adduser
+RUN useradd --shell /bin/bash libgit2 --create-home
+
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
--- /dev/null
+#!/bin/bash
+
+set -e
+
+IMAGE_NAME=$1
+DOCKERFILE_PATH=$2
+
+if [ "${IMAGE_NAME}" = "" ]; then
+ echo "usage: $0 image_name [dockerfile]"
+ exit 1
+fi
+
+if [ "${DOCKERFILE_PATH}" = "" ]; then
+ DOCKERFILE_PATH="${IMAGE_NAME}"
+fi
+
+if [ "${DOCKER_REGISTRY}" = "" ]; then
+ echo "DOCKER_REGISTRY environment variable is unset."
+ echo "Not running inside GitHub Actions or misconfigured?"
+ exit 1
+fi
+
+DOCKER_CONTAINER="${GITHUB_REPOSITORY}/${IMAGE_NAME}"
+DOCKER_REGISTRY_CONTAINER="${DOCKER_REGISTRY}/${DOCKER_CONTAINER}"
+
+echo "dockerfile=${DOCKERFILE_PATH}" >> $GITHUB_ENV
+echo "docker-container=${DOCKER_CONTAINER}" >> $GITHUB_ENV
+echo "docker-registry-container=${DOCKER_REGISTRY_CONTAINER}" >> $GITHUB_ENV
+
+# Identify the last git commit that touched the Dockerfiles
+# Use this as a hash to identify the resulting docker containers
+DOCKER_SHA=$(git log -1 --pretty=format:"%h" -- "${DOCKERFILE_PATH}")
+echo "docker-sha=${DOCKER_SHA}" >> $GITHUB_ENV
+
+DOCKER_REGISTRY_CONTAINER_SHA="${DOCKER_REGISTRY_CONTAINER}:${DOCKER_SHA}"
+
+echo "docker-registry-container-sha=${DOCKER_REGISTRY_CONTAINER_SHA}" >> $GITHUB_ENV
+echo "docker-registry-container-latest=${DOCKER_REGISTRY_CONTAINER}:latest" >> $GITHUB_ENV
+
+exists="true"
+docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false"
+
+if [ "${exists}" != "false" ]; then
+ docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false"
+fi
+
+if [ "${exists}" = "true" ]; then
+ echo "docker-container-exists=true" >> $GITHUB_ENV
+else
+ echo "docker-container-exists=false" >> $GITHUB_ENV
+fi
--- /dev/null
+#!/bin/sh -e
+
+echo "##############################################################################"
+echo "## Downloading mingw"
+echo "##############################################################################"
+
+BUILD_TEMP=${BUILD_TEMP:=$TEMP}
+BUILD_TEMP=$(cygpath $BUILD_TEMP)
+
+case "$ARCH" in
+ amd64)
+ MINGW_URI="https://github.com/libgit2/ci-dependencies/releases/download/2021-05-04/mingw-x86_64-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
+ x86)
+ MINGW_URI="https://github.com/libgit2/ci-dependencies/releases/download/2021-05-04/mingw-i686-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
+esac
+
+if [ -z "$MINGW_URI" ]; then
+ echo "No URL"
+ exit 1
+fi
+
+mkdir -p "$BUILD_TEMP"
+
+curl -s -L "$MINGW_URI" -o "$BUILD_TEMP"/mingw-"$ARCH".zip
+unzip -q "$BUILD_TEMP"/mingw-"$ARCH".zip -d "$BUILD_TEMP"
--- /dev/null
+#!/bin/sh
+
+set -x
+
+brew update
+brew install pkgconfig zlib curl openssl libssh2 ninja
+
+ln -s /Applications/Xcode.app/Contents/Developer/usr/lib/libLeaksAtExit.dylib /usr/local/lib
--- /dev/null
+#!/usr/bin/env bash
+
+set -e
+
+if [ -n "$SKIP_TESTS" ]; then
+ exit 0
+fi
+
+# Windows doesn't run the NTLM tests properly (yet)
+if [[ "$(uname -s)" == MINGW* ]]; then
+ SKIP_NTLM_TESTS=1
+fi
+
+SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
+BUILD_DIR=$(pwd)
+TMPDIR=${TMPDIR:-/tmp}
+USER=${USER:-$(whoami)}
+
+SUCCESS=1
+CONTINUE_ON_FAILURE=0
+
+cleanup() {
+ echo "Cleaning up..."
+
+ if [ ! -z "$GITDAEMON_PID" ]; then
+ echo "Stopping git daemon..."
+ kill $GITDAEMON_PID
+ fi
+
+ if [ ! -z "$SSHD_DIR" -a -f "${SSHD_DIR}/pid" ]; then
+ echo "Stopping SSH..."
+ kill $(cat "${SSHD_DIR}/pid")
+ fi
+
+ echo "Done."
+}
+
+run_test() {
+ if [[ "$GITTEST_FLAKY_RETRY" > 0 ]]; then
+ ATTEMPTS_REMAIN=$GITTEST_FLAKY_RETRY
+ else
+ ATTEMPTS_REMAIN=1
+ fi
+
+ FAILED=0
+ while [[ "$ATTEMPTS_REMAIN" > 0 ]]; do
+ if [ "$FAILED" -eq 1 ]; then
+ echo ""
+ echo "Re-running flaky ${1} tests..."
+ echo ""
+ fi
+
+ RETURN_CODE=0
+
+ CLAR_SUMMARY="${BUILD_DIR}/results_${1}.xml" ctest -V -R "^${1}$" || RETURN_CODE=$? && true
+
+ if [ "$RETURN_CODE" -eq 0 ]; then
+ FAILED=0
+ break
+ fi
+
+ echo "Test exited with code: $RETURN_CODE"
+ ATTEMPTS_REMAIN="$(($ATTEMPTS_REMAIN-1))"
+ FAILED=1
+ done
+
+ if [ "$FAILED" -ne 0 ]; then
+ if [ "$CONTINUE_ON_FAILURE" -ne 1 ]; then
+ exit 1
+ fi
+
+ SUCCESS=0
+ fi
+}
+
+# Configure the test environment; run them early so that we're certain
+# that they're started by the time we need them.
+
+echo "##############################################################################"
+echo "## Configuring test environment"
+echo "##############################################################################"
+
+if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
+ echo "Starting git daemon..."
+ GITDAEMON_DIR=`mktemp -d ${TMPDIR}/gitdaemon.XXXXXXXX`
+ git init --bare "${GITDAEMON_DIR}/test.git"
+ git daemon --listen=localhost --export-all --enable=receive-pack --base-path="${GITDAEMON_DIR}" "${GITDAEMON_DIR}" 2>/dev/null &
+ GITDAEMON_PID=$!
+ disown $GITDAEMON_PID
+fi
+
+if [ -z "$SKIP_PROXY_TESTS" ]; then
+ curl --location --silent --show-error https://github.com/ethomson/poxyproxy/releases/download/v0.7.0/poxyproxy-0.7.0.jar >poxyproxy.jar
+
+ echo ""
+ echo "Starting HTTP proxy (Basic)..."
+ java -jar poxyproxy.jar --address 127.0.0.1 --port 8080 --credentials foo:bar --auth-type basic --quiet &
+
+ echo ""
+ echo "Starting HTTP proxy (NTLM)..."
+ java -jar poxyproxy.jar --address 127.0.0.1 --port 8090 --credentials foo:bar --auth-type ntlm --quiet &
+fi
+
+if [ -z "$SKIP_NTLM_TESTS" ]; then
+ curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.4.0/poxygit-0.4.0.jar >poxygit.jar
+
+ echo ""
+ echo "Starting HTTP server..."
+ NTLM_DIR=`mktemp -d ${TMPDIR}/ntlm.XXXXXXXX`
+ git init --bare "${NTLM_DIR}/test.git"
+ java -jar poxygit.jar --address 127.0.0.1 --port 9000 --credentials foo:baz --quiet "${NTLM_DIR}" &
+fi
+
+if [ -z "$SKIP_SSH_TESTS" ]; then
+ echo "Starting ssh daemon..."
+ HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
+ SSHD_DIR=`mktemp -d ${TMPDIR}/sshd.XXXXXXXX`
+ git init --bare "${SSHD_DIR}/test.git"
+ cat >"${SSHD_DIR}/sshd_config" <<-EOF
+ Port 2222
+ ListenAddress 0.0.0.0
+ Protocol 2
+ HostKey ${SSHD_DIR}/id_rsa
+ PidFile ${SSHD_DIR}/pid
+ AuthorizedKeysFile ${HOME}/.ssh/authorized_keys
+ LogLevel DEBUG
+ RSAAuthentication yes
+ PasswordAuthentication yes
+ PubkeyAuthentication yes
+ ChallengeResponseAuthentication no
+ StrictModes no
+ # Required here as sshd will simply close connection otherwise
+ UsePAM no
+ EOF
+ ssh-keygen -t rsa -f "${SSHD_DIR}/id_rsa" -N "" -q
+ /usr/sbin/sshd -f "${SSHD_DIR}/sshd_config" -E "${SSHD_DIR}/log"
+
+ # Set up keys
+ mkdir "${HOME}/.ssh"
+ ssh-keygen -t rsa -f "${HOME}/.ssh/id_rsa" -N "" -q
+ cat "${HOME}/.ssh/id_rsa.pub" >>"${HOME}/.ssh/authorized_keys"
+ while read algorithm key comment; do
+ echo "[localhost]:2222 $algorithm $key" >>"${HOME}/.ssh/known_hosts"
+ done <"${SSHD_DIR}/id_rsa.pub"
+
+ # Get the fingerprint for localhost and remove the colons so we can
+ # parse it as a hex number. Older versions have a different output
+ # format.
+ if [[ $(ssh -V 2>&1) == OpenSSH_6* ]]; then
+ SSH_FINGERPRINT=$(ssh-keygen -F '[localhost]:2222' -f "${HOME}/.ssh/known_hosts" -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
+ else
+ SSH_FINGERPRINT=$(ssh-keygen -E md5 -F '[localhost]:2222' -f "${HOME}/.ssh/known_hosts" -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
+ fi
+fi
+
+# Run the tests that do not require network connectivity.
+
+if [ -z "$SKIP_OFFLINE_TESTS" ]; then
+ echo ""
+ echo "##############################################################################"
+ echo "## Running (offline) tests"
+ echo "##############################################################################"
+
+ run_test offline
+fi
+
+if [ -n "$RUN_INVASIVE_TESTS" ]; then
+ echo ""
+ echo "Running invasive tests"
+ echo ""
+
+ export GITTEST_INVASIVE_FS_SIZE=1
+ export GITTEST_INVASIVE_MEMORY=1
+ export GITTEST_INVASIVE_SPEED=1
+ run_test invasive
+ unset GITTEST_INVASIVE_FS_SIZE
+ unset GITTEST_INVASIVE_MEMORY
+ unset GITTEST_INVASIVE_SPEED
+fi
+
+if [ -z "$SKIP_ONLINE_TESTS" ]; then
+ # Run the online tests. The "online" test suite only includes the
+ # default online tests that do not require additional configuration.
+ # The "proxy" and "ssh" test suites require further setup.
+
+ echo ""
+ echo "##############################################################################"
+ echo "## Running (online) tests"
+ echo "##############################################################################"
+
+ export GITTEST_FLAKY_RETRY=5
+ run_test online
+ unset GITTEST_FLAKY_RETRY
+
+ # Run the online tests that immutably change global state separately
+ # to avoid polluting the test environment.
+ echo ""
+ echo "##############################################################################"
+ echo "## Running (online_customcert) tests"
+ echo "##############################################################################"
+ run_test online_customcert
+fi
+
+if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
+ echo ""
+ echo "Running gitdaemon tests"
+ echo ""
+
+ export GITTEST_REMOTE_URL="git://localhost/test.git"
+ run_test gitdaemon
+ unset GITTEST_REMOTE_URL
+fi
+
+if [ -z "$SKIP_PROXY_TESTS" ]; then
+ echo ""
+ echo "Running proxy tests (Basic authentication)"
+ echo ""
+
+ export GITTEST_REMOTE_PROXY_HOST="localhost:8080"
+ export GITTEST_REMOTE_PROXY_USER="foo"
+ export GITTEST_REMOTE_PROXY_PASS="bar"
+ run_test proxy
+ unset GITTEST_REMOTE_PROXY_HOST
+ unset GITTEST_REMOTE_PROXY_USER
+ unset GITTEST_REMOTE_PROXY_PASS
+
+ echo ""
+ echo "Running proxy tests (NTLM authentication)"
+ echo ""
+
+ export GITTEST_REMOTE_PROXY_HOST="localhost:8090"
+ export GITTEST_REMOTE_PROXY_USER="foo"
+ export GITTEST_REMOTE_PROXY_PASS="bar"
+ export GITTEST_FLAKY_RETRY=5
+ run_test proxy
+ unset GITTEST_FLAKY_RETRY
+ unset GITTEST_REMOTE_PROXY_HOST
+ unset GITTEST_REMOTE_PROXY_USER
+ unset GITTEST_REMOTE_PROXY_PASS
+fi
+
+if [ -z "$SKIP_NTLM_TESTS" ]; then
+ echo ""
+ echo "Running NTLM tests (IIS emulation)"
+ echo ""
+
+ export GITTEST_REMOTE_URL="http://localhost:9000/ntlm/test.git"
+ export GITTEST_REMOTE_USER="foo"
+ export GITTEST_REMOTE_PASS="baz"
+ run_test auth_clone_and_push
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_USER
+ unset GITTEST_REMOTE_PASS
+
+ echo ""
+ echo "Running NTLM tests (Apache emulation)"
+ echo ""
+
+ export GITTEST_REMOTE_URL="http://localhost:9000/broken-ntlm/test.git"
+ export GITTEST_REMOTE_USER="foo"
+ export GITTEST_REMOTE_PASS="baz"
+ run_test auth_clone_and_push
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_USER
+ unset GITTEST_REMOTE_PASS
+fi
+
+if [ -z "$SKIP_NEGOTIATE_TESTS" -a -n "$GITTEST_NEGOTIATE_PASSWORD" ]; then
+ echo ""
+ echo "Running SPNEGO tests"
+ echo ""
+
+ if [ "$(uname -s)" = "Darwin" ]; then
+ KINIT_FLAGS="--password-file=STDIN"
+ fi
+
+ echo $GITTEST_NEGOTIATE_PASSWORD | kinit $KINIT_FLAGS test@LIBGIT2.ORG
+ klist -5f
+
+ export GITTEST_REMOTE_URL="https://test.libgit2.org/kerberos/empty.git"
+ export GITTEST_REMOTE_DEFAULT="true"
+ run_test auth_clone
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_DEFAULT
+
+ echo ""
+ echo "Running SPNEGO tests (expect/continue)"
+ echo ""
+
+ export GITTEST_REMOTE_URL="https://test.libgit2.org/kerberos/empty.git"
+ export GITTEST_REMOTE_DEFAULT="true"
+ export GITTEST_REMOTE_EXPECTCONTINUE="true"
+ run_test auth_clone
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_DEFAULT
+ unset GITTEST_REMOTE_EXPECTCONTINUE
+
+ kdestroy -A
+fi
+
+if [ -z "$SKIP_SSH_TESTS" ]; then
+ echo ""
+ echo "Running ssh tests"
+ echo ""
+
+ export GITTEST_REMOTE_URL="ssh://localhost:2222/$SSHD_DIR/test.git"
+ export GITTEST_REMOTE_USER=$USER
+ export GITTEST_REMOTE_SSH_KEY="${HOME}/.ssh/id_rsa"
+ export GITTEST_REMOTE_SSH_PUBKEY="${HOME}/.ssh/id_rsa.pub"
+ export GITTEST_REMOTE_SSH_PASSPHRASE=""
+ export GITTEST_REMOTE_SSH_FINGERPRINT="${SSH_FINGERPRINT}"
+ run_test ssh
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_USER
+ unset GITTEST_REMOTE_SSH_KEY
+ unset GITTEST_REMOTE_SSH_PUBKEY
+ unset GITTEST_REMOTE_SSH_PASSPHRASE
+ unset GITTEST_REMOTE_SSH_FINGERPRINT
+fi
+
+if [ -z "$SKIP_FUZZERS" ]; then
+ echo ""
+ echo "##############################################################################"
+ echo "## Running fuzzers"
+ echo "##############################################################################"
+
+ ctest -V -R 'fuzzer'
+fi
+
+cleanup
+
+if [ "$SUCCESS" -ne 1 ]; then
+ echo "Some tests failed."
+ exit 1
+fi
+
+echo "Success."
+exit 0
--- /dev/null
+# LIBSSH2_FOUND - system has the libssh2 library
+# LIBSSH2_INCLUDE_DIR - the libssh2 include directory
+# LIBSSH2_LIBRARY - the libssh2 library name
+
+FIND_PATH(LIBSSH2_INCLUDE_DIR libssh2.h)
+
+FIND_LIBRARY(LIBSSH2_LIBRARY NAMES ssh2 libssh2)
+
+INCLUDE(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LibSSH2
+ REQUIRED_VARS LIBSSH2_LIBRARY LIBSSH2_INCLUDE_DIR)
+
+MARK_AS_ADVANCED(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY)
--- /dev/null
+INCLUDE(EnableWarnings)
+
+IF (APPLE)
+ # We cannot simply CHECK_FUNCTION_EXISTS on macOS because
+ # MACOSX_DEPLOYMENT_TARGET may be set to a version in the past
+ # that doesn't have futimens. Instead we need to enable warnings
+ # as errors, then check for the symbol existing in `sys/stat.h`,
+ # then reset warnings as errors.
+ ENABLE_WARNINGS(error)
+ CHECK_SYMBOL_EXISTS(futimens sys/stat.h HAVE_FUTIMENS)
+ DISABLE_WARNINGS(error)
+ELSE ()
+ CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
+ENDIF ()
LIST(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES})
ELSEIF (USE_HTTPS STREQUAL "WinHTTP")
# WinHTTP setup was handled in the WinHTTP-specific block above
+ ELSEIF (USE_HTTPS STREQUAL "OpenSSL-Dynamic")
+ SET(GIT_OPENSSL 1)
+ SET(GIT_OPENSSL_DYNAMIC 1)
+ LIST(APPEND LIBGIT2_LIBS dl)
ELSE()
MESSAGE(FATAL_ERROR "Asked for backend ${USE_HTTPS} but it wasn't found")
ENDIF()
MESSAGE(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
ENDIF()
+list(APPEND SRC_SHA1 "hash/sha1.h")
list(SORT SRC_SHA1)
ADD_FEATURE_INFO(SHA ON "using ${USE_SHA1}")
+v1.3
+----
+
+This is release v1.3.0, "Zugunruhe". This release includes only minor new features that will be helpful for users to have an orderly transition to the v2.0 lineage.
+
+## New Features
+* Support custom git extensions by @ethomson in https://github.com/libgit2/libgit2/pull/6031
+* Introduce `git_email_create`; deprecate `git_diff_format_email` by @ethomson in https://github.com/libgit2/libgit2/pull/6061
+
+## Deprecated APIs
+* `git_oidarray_free` is deprecated; callers should use `git_oidarray_dispose`
+
+## Bug fixes
+* #6028: Check if `threadstate->error_t.message` is not `git_buf__initbuf` before freeing. by @arroz in https://github.com/libgit2/libgit2/pull/6029
+* remote: Mark `git_remote_name_is_valid` as `GIT_EXTERN` by @lhchavez in https://github.com/libgit2/libgit2/pull/6032
+* Fix config parsing for multiline with multiple quoted comment chars by @basile-henry in https://github.com/libgit2/libgit2/pull/6043
+* indexer: Avoid one `mmap(2)`/`munmap(2)` pair per `git_indexer_append` call by @lhchavez in https://github.com/libgit2/libgit2/pull/6039
+* merge: Check file mode when resolving renames by @ccstolley in https://github.com/libgit2/libgit2/pull/6060
+* Allow proxy options when connecting with a detached remote. by @lrm29 in https://github.com/libgit2/libgit2/pull/6058
+* win32: allow empty environment variables by @ethomson in https://github.com/libgit2/libgit2/pull/6063
+* Fixes for deprecated APIs by @ethomson in https://github.com/libgit2/libgit2/pull/6066
+* filter: use a `git_oid` in filter options, not a pointer by @ethomson in https://github.com/libgit2/libgit2/pull/6067
+* diff: update `GIT_DIFF_IGNORE_BLANK_LINES` by @ethomson in https://github.com/libgit2/libgit2/pull/6068
+* Attribute lookups are always on relative paths by @ethomson in https://github.com/libgit2/libgit2/pull/6073
+* Handle long paths when querying attributes by @ethomson in https://github.com/libgit2/libgit2/pull/6075
+
+## Code cleanups
+* notes: use a buffer internally by @ethomson in https://github.com/libgit2/libgit2/pull/6047
+* Fix coding style for pointer by @punkymaniac in https://github.com/libgit2/libgit2/pull/6045
+* Use __typeof__ GNUC keyword for ISO C compatibility by @duncanthomson in https://github.com/libgit2/libgit2/pull/6041
+* Discover libssh2 without pkg-config by @stac47 in https://github.com/libgit2/libgit2/pull/6053
+* Longpath filter bug by @lrm29 in https://github.com/libgit2/libgit2/pull/6055
+* Add test to ensure empty proxy env behaves like unset env by @sathieu in https://github.com/libgit2/libgit2/pull/6052
+* Stdint header condition has been reverted. by @lolgear in https://github.com/libgit2/libgit2/pull/6020
+* buf: `common_prefix` takes a string array by @ethomson in https://github.com/libgit2/libgit2/pull/6077
+* oidarray: introduce `git_oidarray_dispose` by @ethomson in https://github.com/libgit2/libgit2/pull/6076
+* examples: Free the git_config and git_config_entry after use by @257 in https://github.com/libgit2/libgit2/pull/6071
+
+## CI Improvements
+* ci: pull libssh2 from www.libssh2.org by @ethomson in https://github.com/libgit2/libgit2/pull/6064
+
+## Documentation changes
+* Update README.md by @shijinglu in https://github.com/libgit2/libgit2/pull/6050
+
+## New Contributors
+* @basile-henry made their first contribution in https://github.com/libgit2/libgit2/pull/6043
+* @duncanthomson made their first contribution in https://github.com/libgit2/libgit2/pull/6041
+* @stac47 made their first contribution in https://github.com/libgit2/libgit2/pull/6053
+* @shijinglu made their first contribution in https://github.com/libgit2/libgit2/pull/6050
+* @ccstolley made their first contribution in https://github.com/libgit2/libgit2/pull/6060
+* @sathieu made their first contribution in https://github.com/libgit2/libgit2/pull/6052
+* @257 made their first contribution in https://github.com/libgit2/libgit2/pull/6071
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.2.0...v1.3.0
+
+---------------------------------------------------------------------
+
+v1.2
+-----
+
+This is release v1.2.0, "Absacker". This release includes many new features: in particular, support for commit graphs, multi-pack indexes, and `core.longpaths` support.
+
+This is meant to be the final minor release in the v1 lineage. v2.0 will be the next major release and will remove deprecated APIs and may include breaking changes.
+
+## Deprecated APIs
+
+* revspec: rename git_revparse_mode_t to git_revspec_t by @ethomson in https://github.com/libgit2/libgit2/pull/5786
+* tree: deprecate `git_treebuilder_write_with_buffer` by @ethomson in https://github.com/libgit2/libgit2/pull/5815
+* Deprecate `is_valid_name` functions; replace with `name_is_valid` functions by @ethomson in https://github.com/libgit2/libgit2/pull/5659
+* filter: stop taking git_buf as user input by @ethomson in https://github.com/libgit2/libgit2/pull/5859
+* remote: introduce remote_ready_cb, deprecate resolve_url callback by @ethomson in https://github.com/libgit2/libgit2/pull/6012
+* Introduce `create_commit_cb`, deprecate `signing_cb` by @ethomson in https://github.com/libgit2/libgit2/pull/6016
+* filter: filter drivers stop taking git_buf as user input by @ethomson in https://github.com/libgit2/libgit2/pull/6011
+* buf: deprecate public git_buf writing functions by @ethomson in https://github.com/libgit2/libgit2/pull/6017
+
+## New features
+
+* winhttp: support optional client cert by @ianhattendorf in https://github.com/libgit2/libgit2/pull/5384
+* Add support for additional SSH hostkey types. by @arroz in https://github.com/libgit2/libgit2/pull/5750
+* Handle ipv6 addresses by @ethomson in https://github.com/libgit2/libgit2/pull/5741
+* zlib: Add support for building with Chromium's zlib implementation by @lhchavez in https://github.com/libgit2/libgit2/pull/5748
+* commit-graph: Introduce a parser for commit-graph files by @lhchavez in https://github.com/libgit2/libgit2/pull/5762
+* patch: add owner accessor by @KOLANICH in https://github.com/libgit2/libgit2/pull/5731
+* commit-graph: Support lookups of entries in a commit-graph by @lhchavez in https://github.com/libgit2/libgit2/pull/5763
+* commit-graph: Introduce `git_commit_graph_needs_refresh()` by @lhchavez in https://github.com/libgit2/libgit2/pull/5764
+* Working directory path validation by @ethomson in https://github.com/libgit2/libgit2/pull/5823
+* Support `core.longpaths` on Windows by @ethomson in https://github.com/libgit2/libgit2/pull/5857
+* git_reference_create_matching: Treat all-zero OID as "must be absent" by @novalis in https://github.com/libgit2/libgit2/pull/5842
+* diff:add option to ignore blank line changes by @yuuri in https://github.com/libgit2/libgit2/pull/5853
+* [Submodule] Git submodule dup by @lolgear in https://github.com/libgit2/libgit2/pull/5890
+* commit-graph: Use the commit-graph in revwalks by @lhchavez in https://github.com/libgit2/libgit2/pull/5765
+* commit-graph: Introduce `git_commit_list_generation_cmp` by @lhchavez in https://github.com/libgit2/libgit2/pull/5766
+* graph: Create `git_graph_reachable_from_any()` by @lhchavez in https://github.com/libgit2/libgit2/pull/5767
+* Support reading attributes from a specific commit by @ethomson in https://github.com/libgit2/libgit2/pull/5952
+* [Branch] Branch upstream with format by @lolgear in https://github.com/libgit2/libgit2/pull/5861
+* Dynamically load OpenSSL (optionally) by @ethomson in https://github.com/libgit2/libgit2/pull/5974
+* Set refs/remotes/origin/HEAD to default branch when branch is specified by @A-Ovchinnikov-mx in https://github.com/libgit2/libgit2/pull/6010
+* midx: Add a way to write multi-pack-index files by @lhchavez in https://github.com/libgit2/libgit2/pull/5404
+* Use error code GIT_EAUTH for authentication failures by @josharian in https://github.com/libgit2/libgit2/pull/5395
+* midx: Introduce git_odb_write_multi_pack_index() by @lhchavez in https://github.com/libgit2/libgit2/pull/5405
+* Checkout dry-run by @J0Nes90 in https://github.com/libgit2/libgit2/pull/5841
+* mbedTLS: Fix setting certificate directory by @mikezackles in https://github.com/libgit2/libgit2/pull/6004
+* remote: introduce remote_ready_cb, deprecate resolve_url callback by @ethomson in https://github.com/libgit2/libgit2/pull/6012
+* Introduce `create_commit_cb`, deprecate `signing_cb` by @ethomson in https://github.com/libgit2/libgit2/pull/6016
+* commit-graph: Add a way to write commit-graph files by @lhchavez in https://github.com/libgit2/libgit2/pull/5778
+
+## Bug fixes
+
+* Define `git___load` when building with `-DTHREADSAFE=OFF` by @lhchavez in https://github.com/libgit2/libgit2/pull/5664
+* Make the Windows leak detection more robust by @lhchavez in https://github.com/libgit2/libgit2/pull/5661
+* Refactor "global" state by @ethomson in https://github.com/libgit2/libgit2/pull/5546
+* threadstate: rename tlsdata when building w/o threads by @ethomson in https://github.com/libgit2/libgit2/pull/5668
+* Include `${MBEDTLS_INCLUDE_DIR}` when compiling `crypt_mbedtls.c` by @staticfloat in https://github.com/libgit2/libgit2/pull/5685
+* Fix the `-DTHREADSAFE=OFF` build by @lhchavez in https://github.com/libgit2/libgit2/pull/5690
+* Add missing worktree_dir check and test case by @rbmclean in https://github.com/libgit2/libgit2/pull/5692
+* msvc crtdbg -> win32 leakcheck by @ethomson in https://github.com/libgit2/libgit2/pull/5580
+* Introduce GIT_ASSERT macros by @ethomson in https://github.com/libgit2/libgit2/pull/5327
+* Also add the raw hostkey to `git_cert_hostkey` by @lhchavez in https://github.com/libgit2/libgit2/pull/5704
+* Make the odb race-free by @lhchavez in https://github.com/libgit2/libgit2/pull/5595
+* Make the pack and mwindow implementations data-race-free by @lhchavez in https://github.com/libgit2/libgit2/pull/5593
+* Thread-free implementation by @ethomson in https://github.com/libgit2/libgit2/pull/5719
+* Thread-local storage: a generic internal library (with no allocations) by @ethomson in https://github.com/libgit2/libgit2/pull/5720
+* Friendlier getting started in the lack of git_libgit2_init by @ethomson in https://github.com/libgit2/libgit2/pull/5578
+* Make git__strntol64() ~70%* faster by @lhchavez in https://github.com/libgit2/libgit2/pull/5735
+* Cache the parsed submodule config when diffing by @lhchavez in https://github.com/libgit2/libgit2/pull/5727
+* pack: continue zlib while we can make progress by @ethomson in https://github.com/libgit2/libgit2/pull/5740
+* Avoid using `__builtin_mul_overflow` with the clang+32-bit combo by @lhchavez in https://github.com/libgit2/libgit2/pull/5742
+* repository: use intptr_t's in the config map cache by @ethomson in https://github.com/libgit2/libgit2/pull/5746
+* Build with NO_MMAP by @0xdky in https://github.com/libgit2/libgit2/pull/5583
+* Add documentation for git_blob_filter_options.version by @JoshuaS3 in https://github.com/libgit2/libgit2/pull/5759
+* blob: fix name of `GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD` by @ethomson in https://github.com/libgit2/libgit2/pull/5760
+* Cope with empty default branch by @ethomson in https://github.com/libgit2/libgit2/pull/5770
+* README: instructions for using libgit2 without compiling by @ethomson in https://github.com/libgit2/libgit2/pull/5772
+* Use `p_pwrite`/`p_pread` consistently throughout the codebase by @lhchavez in https://github.com/libgit2/libgit2/pull/5769
+* midx: Fix a bug in `git_midx_needs_refresh()` by @lhchavez in https://github.com/libgit2/libgit2/pull/5768
+* mwindow: Fix a bug in the LRU window finding code by @lhchavez in https://github.com/libgit2/libgit2/pull/5783
+* refdb_fs: Check git_sortedcache wlock/rlock errors by @mamapanda in https://github.com/libgit2/libgit2/pull/5800
+* index: Check git_vector_dup error in write_entries by @mamapanda in https://github.com/libgit2/libgit2/pull/5801
+* Fix documentation formating on repository.h by @punkymaniac in https://github.com/libgit2/libgit2/pull/5806
+* include: fix typos in comments by @tniessen in https://github.com/libgit2/libgit2/pull/5805
+* Fix some typos by @aaronfranke in https://github.com/libgit2/libgit2/pull/5797
+* Check git_signature_dup failure by @mamapanda in https://github.com/libgit2/libgit2/pull/5817
+* merge: Check insert_head_ids error in create_virtual_base by @mamapanda in https://github.com/libgit2/libgit2/pull/5818
+* winhttp: skip certificate check if unable to send request by @ianhattendorf in https://github.com/libgit2/libgit2/pull/5814
+* Default to GIT_BRANCH_DEFAULT if init.defaultBranch is empty string by @ianhattendorf in https://github.com/libgit2/libgit2/pull/5832
+* Fix diff_entrycount -> diff_num_deltas doc typo by @mjsir911 in https://github.com/libgit2/libgit2/pull/5838
+* repo: specify init.defaultbranch is meant to be a branch name by @carlosmn in https://github.com/libgit2/libgit2/pull/5835
+* repo: remove an inappropriate use of PASSTHROUGH by @carlosmn in https://github.com/libgit2/libgit2/pull/5834
+* src: fix typos in header files by @tniessen in https://github.com/libgit2/libgit2/pull/5843
+* test: clean up memory leaks by @ethomson in https://github.com/libgit2/libgit2/pull/5858
+* buf: remove unnecessary buf_text namespace by @ethomson in https://github.com/libgit2/libgit2/pull/5860
+* Fix bug in git_diff_find_similar. by @staktrace in https://github.com/libgit2/libgit2/pull/5839
+* Fix issues with Proxy Authentication after httpclient refactor by @implausible in https://github.com/libgit2/libgit2/pull/5852
+* tests: clean up memory leak, fail on leak for win32 by @ethomson in https://github.com/libgit2/libgit2/pull/5892
+* Tolerate readlink size less than st_size by @dtolnay in https://github.com/libgit2/libgit2/pull/5900
+* Define WINHTTP_NO_CLIENT_CERT_CONTEXT if needed by @jacquesg in https://github.com/libgit2/libgit2/pull/5929
+* Update from regex to pcre licensing information in docs/contributing.md by @boretrk in https://github.com/libgit2/libgit2/pull/5916
+* Consider files executable only if the user can execute them by @novalis in https://github.com/libgit2/libgit2/pull/5915
+* git__timer: Limit ITimer usage to AmigaOS4 by @boretrk in https://github.com/libgit2/libgit2/pull/5936
+* Fix memory leak in git_smart__connect by @punkymaniac in https://github.com/libgit2/libgit2/pull/5908
+* config: fix included configs not refreshed more than once by @Batchyx in https://github.com/libgit2/libgit2/pull/5926
+* Fix wrong time_t used in function by @NattyNarwhal in https://github.com/libgit2/libgit2/pull/5938
+* fix check for ignoring of negate rules by @palmin in https://github.com/libgit2/libgit2/pull/5824
+* Make `FIND_PACKAGE(PythonInterp)` prefer `python3` by @lhchavez in https://github.com/libgit2/libgit2/pull/5913
+* git__timer: Allow compilation on systems without CLOCK_MONOTONIC by @boretrk in https://github.com/libgit2/libgit2/pull/5945
+* stdintification: use int64_t and INT64_C instead of long long by @NattyNarwhal in https://github.com/libgit2/libgit2/pull/5941
+* Optional stricter allocation checking (for `malloc(0)` cases) by @ethomson in https://github.com/libgit2/libgit2/pull/5951
+* Variadic arguments aren't in C89 by @NattyNarwhal in https://github.com/libgit2/libgit2/pull/5948
+* Fix typo in general.c by @Crayon2000 in https://github.com/libgit2/libgit2/pull/5954
+* common.h: use inline when compiling for C99 and later by @boretrk in https://github.com/libgit2/libgit2/pull/5953
+* Fix one memory leak in master by @lhchavez in https://github.com/libgit2/libgit2/pull/5957
+* tests: reset odb backend priority by @ethomson in https://github.com/libgit2/libgit2/pull/5961
+* cmake: extended futimens checking on macOS by @ethomson in https://github.com/libgit2/libgit2/pull/5962
+* amiga: use ';' as path list separator on AmigaOS by @boretrk in https://github.com/libgit2/libgit2/pull/5978
+* Respect the force flag on refspecs in git_remote_fetch by @alexjg in https://github.com/libgit2/libgit2/pull/5854
+* Fix LIBGIT2_FILENAME not being passed to the resource compiler by @jairbubbles in https://github.com/libgit2/libgit2/pull/5994
+* sha1dc: remove conditional for <sys/types.h> by @boretrk in https://github.com/libgit2/libgit2/pull/5997
+* openssl: don't fail when we can't customize allocators by @ethomson in https://github.com/libgit2/libgit2/pull/5999
+* C11 warnings by @boretrk in https://github.com/libgit2/libgit2/pull/6005
+* open: input validation for empty segments in path by @boretrk in https://github.com/libgit2/libgit2/pull/5950
+* Introduce GIT_WARN_UNUSED_RESULT by @lhchavez in https://github.com/libgit2/libgit2/pull/5802
+* GCC C11 warnings by @boretrk in https://github.com/libgit2/libgit2/pull/6006
+* array: check dereference from void * type by @boretrk in https://github.com/libgit2/libgit2/pull/6007
+* Homogenize semantics for atomic-related functions by @lhchavez in https://github.com/libgit2/libgit2/pull/5747
+* git_array_alloc: return objects of correct type by @boretrk in https://github.com/libgit2/libgit2/pull/6008
+* CMake. hash sha1 header has been added. by @lolgear in https://github.com/libgit2/libgit2/pull/6013
+* tests: change comments to c89 style by @boretrk in https://github.com/libgit2/libgit2/pull/6015
+* Set Host Header to match CONNECT authority target by @lollipopman in https://github.com/libgit2/libgit2/pull/6022
+* Fix worktree iteration when repository has no common directory by @kcsaul in https://github.com/libgit2/libgit2/pull/5943
+
+## Documentation improvements
+
+* Update README.md for additional Delphi bindings by @todaysoftware in https://github.com/libgit2/libgit2/pull/5831
+* Fix documentation formatting by @punkymaniac in https://github.com/libgit2/libgit2/pull/5850
+* docs: fix incorrect comment marker by @tiennou in https://github.com/libgit2/libgit2/pull/5897
+* Patch documentation by @punkymaniac in https://github.com/libgit2/libgit2/pull/5903
+* Fix misleading doc for `git_index_find` by @arxanas in https://github.com/libgit2/libgit2/pull/5910
+* docs: stop mentioning libgit2's "master" branch by @Batchyx in https://github.com/libgit2/libgit2/pull/5925
+* docs: fix some missing includes that cause Docurium to error out by @tiennou in https://github.com/libgit2/libgit2/pull/5917
+* Patch documentation by @punkymaniac in https://github.com/libgit2/libgit2/pull/5940
+
+## Development improvements
+
+* WIP: .devcontainer: settings for a codespace workflow by @ethomson in https://github.com/libgit2/libgit2/pull/5508
+
+## CI Improvements
+
+* Add a ThreadSanitizer build by @lhchavez in https://github.com/libgit2/libgit2/pull/5597
+* ci: more GitHub Actions by @ethomson in https://github.com/libgit2/libgit2/pull/5706
+* ci: run coverity in the nightly builds by @ethomson in https://github.com/libgit2/libgit2/pull/5707
+* ci: only report main branch in README status by @ethomson in https://github.com/libgit2/libgit2/pull/5708
+* Fix the `ENABLE_WERROR=ON` build in Groovy Gorilla (gcc 10.2) by @lhchavez in https://github.com/libgit2/libgit2/pull/5715
+* Re-enable the RC4 test by @carlosmn in https://github.com/libgit2/libgit2/pull/4418
+* ci: run codeql by @ethomson in https://github.com/libgit2/libgit2/pull/5709
+* github-actions: Also rename the main branch here by @lhchavez in https://github.com/libgit2/libgit2/pull/5771
+* ci: don't use ninja on macOS by @ethomson in https://github.com/libgit2/libgit2/pull/5780
+* ci: use GitHub for storing mingw-w64 build dependency by @ethomson in https://github.com/libgit2/libgit2/pull/5855
+* docker: remove the entrypoint by @ethomson in https://github.com/libgit2/libgit2/pull/5980
+* http: don't require a password by @ethomson in https://github.com/libgit2/libgit2/pull/5972
+* ci: update nightly to use source path by @ethomson in https://github.com/libgit2/libgit2/pull/5989
+* ci: add centos 7 and centos 8 by @ethomson in https://github.com/libgit2/libgit2/pull/5992
+* ci: update centos builds by @ethomson in https://github.com/libgit2/libgit2/pull/5995
+* ci: tag new containers with the latest tag by @ethomson in https://github.com/libgit2/libgit2/pull/6000
+
+## Dependency updates
+
+* ntlm: [ntlmclient](https://github.com/ethomson/ntlmclient) is now v0.9.1
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.1.0...v1.2.0
+
+---------------------------------------------------------------------
+
v1.1
----
has been fixed. Previously, fetching from servers like Gerrit, that
sent large data packets, would error.
+---------------------------------------------------------------------
+
v1.0
----
The breaking change is that the `username` member of the underlying struct
is now hidden, and a new `git_cred_get_username` function has been provided.
+* Some errors of class `GIT_ERROR_NET` now have class `GIT_ERROR_HTTP`.
+ Most authentication failures now have error code `GIT_EAUTH` instead of `GIT_ERROR`.
+
### Breaking CMake configuration changes
* The CMake option to use a system http-parser library, instead of the
* Tyler Ang-Wanek
* Tyler Wanek
+---------------------------------------------------------------------
+
v0.28
-----
out such files is not allowed as this can make a Git implementation write
outside of the repository and bypass the fsck checks for CVE-2018-11235.
+---------------------------------------------------------------------
+
v0.27
---------
`git_odb_backend` interface have changed their signatures to allow providing
the object's size and type to the caller.
+---------------------------------------------------------------------
+
v0.26
-----
to provide the name of a merge driver to be used to handle files changed
during a merge.
+---------------------------------------------------------------------
+
v0.24
-------
* `git_remote_connect()` now takes a `custom_headers` argument to set
the extra HTTP header fields to send.
+---------------------------------------------------------------------
+
v0.23
------
* It is no longer allowed to call `git_buf_grow()` on buffers
borrowing the memory they point to.
+---------------------------------------------------------------------
+
v0.22
------
*
* @param s String to froznicate
* @return A newly allocated string or `NULL` in case an error occurred.
- * /
+ */
char *froznicate(const char *s);
```
by the following licenses:
- http-parser is licensed under [MIT license](../deps/http-parser/COPYING)
-- regex is governed by [LGPL v2.1+ license](../deps/regex/COPYING)
+- pcre is governed by [BSD license](../deps/pcre/LICENCE)
- winhttp is governed by [LGPL v2.1+](../deps/winhttp/COPYING.LGPL) and [GPL v2 with linking exception](../deps/winhttp/COPYING.GPL)
- zlib is governed by [zlib license](../deps/zlib/COPYING)
## Libgit2 Versions
-The `master` branch is the main branch where development happens.
+The `main` branch is the main branch where development happens.
Releases are tagged
(e.g. [v0.21.0](https://github.com/libgit2/libgit2/releases/tag/v0.21.0) )
and when a critical bug fix needs to be backported, it will be done on a
Using [`git describe`](http://git-scm.com/docs/git-describe) is a
great way to tell us what version you're working with.
-If you're not running against the latest `master` branch version,
+If you're not running against the latest `main` branch version,
please compile and test against that to avoid re-reporting an issue that's
already been fixed.
contributors fork the [libgit2 repository](https://github.com/libgit2/libgit2),
make their changes on branch, and submit a
[Pull Request](https://help.github.com/articles/using-pull-requests)
-(a.k.a. "PR"). Pull requests should usually be targeted at the `master`
+(a.k.a. "PR"). Pull requests should usually be targeted at the `main`
branch.
Life will be a lot easier for you (and us) if you follow this pattern
-(i.e. fork, named branch, submit PR). If you use your fork's `master`
+(i.e. fork, named branch, submit PR). If you use your fork's `main`
branch directly, things can get messy.
Please include a nice description of your changes when you submit your PR;
# Releasing the library
-We have three kinds of releases: "full" releases, maintenance releases and security releases. Full ones release the state of the `master` branch whereas maintenance releases provide bugfixes building on top of the currently released series. Security releases are also for the current series but only contain security fixes on top of the previous release.
+We have three kinds of releases: "full" releases, maintenance releases and security releases. Full ones release the state of the `main` branch whereas maintenance releases provide bugfixes building on top of the currently released series. Security releases are also for the current series but only contain security fixes on top of the previous release.
## Full release
During the freeze, and certainly after the first release candidate, any bindings the core team work with should be updated in order to discover any issues that might come up with the multitude of approaches to memory management, embedding or linking.
-Create a branch `maint/v0.X` at the current state of `master` after you've created the tag. This will be used for maintenance releases and lets our dependents track the latest state of the series.
+Create a branch `maint/v0.X` at the current state of `main` after you've created the tag. This will be used for maintenance releases and lets our dependents track the latest state of the series.
## Maintenance release
gem install docurium
-and run it against our description file with the tip of master checked out.
+and run it against our description file with the tip of `main` checked out.
cm doc api.docurium
--- /dev/null
+core.longpaths support
+======================
+
+Historically, Windows has limited absolute path lengths to `MAX_PATH`
+(260) characters.
+
+Unfortunately, 260 characters is a punishing small maximum. This is
+especially true for developers where dependencies may have dependencies
+in a folder, each dependency themselves having dependencies in a
+sub-folder, ad (seemingly) infinitum.
+
+So although the Windows APIs _by default_ honor this 260 character
+maximum, you can get around this by using separate APIs. Git honors a
+`core.longpaths` configuration option that allows some paths on Windows
+to exceed these 260 character limits.
+
+And because they've gone and done it, that means that libgit2 has to
+honor this value, too.
+
+Since `core.longpaths` is a _configuration option_ that means that we
+need to be able to resolve a configuration - including in _the repository
+itself_ in order to know whether long paths should be supported.
+
+Therefore, in libgit2, `core.longpaths` affects paths in working
+directories _only_. Paths to the repository, and to items inside the
+`.git` folder, must be no longer than 260 characters.
+
+This definition is required to avoid a paradoxical setting: if you
+had a repository in a folder that was 280 characters long, how would
+you know whether `core.longpaths` support should be enabled? Even if
+`core.longpaths` was set to true in a system configuration file, the
+repository itself may set `core.longpaths` to false in _its_ configuration
+file, which you could only read if `core.longpaths` were set to true.
+
+Thus, `core.longpaths` must _only_ apply to working directory items,
+and cannot apply to the `.git` folder or its contents.
*/
if (o.commitspec) {
check_lg2(git_revparse(&revspec, repo, o.commitspec), "Couldn't parse commit spec", NULL);
- if (revspec.flags & GIT_REVPARSE_SINGLE) {
+ if (revspec.flags & GIT_REVSPEC_SINGLE) {
git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.from));
git_object_free(revspec.from);
} else {
#include "common.h"
-/* Define the printf format specifer to use for size_t output */
+/* Define the printf format specifier to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
# define PRIuZ "Iu"
# define PRIxZ "Ix"
#endif
#ifndef PRIuZ
-/* Define the printf format specifer to use for size_t output */
+/* Define the printf format specifier to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
# define PRIuZ "Iu"
#else
}
puts(entry->value);
+
+ /* Free the git_config_entry after use with `git_config_entry_free()`. */
+ git_config_entry_free(entry);
+
return 0;
}
error = 1;
}
+ /**
+ * The configuration file must be freed once it's no longer
+ * being used by the user.
+ */
+ git_config_free(cfg);
out:
return error;
}
printf("\n*Raw to Hex*\n");
out[GIT_OID_HEXSZ] = '\0';
- /**
- * If you have a oid, you can easily get the hex value of the SHA as well.
- */
- git_oid_fmt(out, oid);
-
/**
* If you have a oid, you can easily get the hex value of the SHA as well.
*/
/**
* ### Config Files
*
- * The [config API][config] allows you to list and updatee config values
+ * 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
}
if (*revstr == '^') {
- revs.flags = GIT_REVPARSE_SINGLE;
+ revs.flags = GIT_REVSPEC_SINGLE;
hide = !hide;
if (git_revparse_single(&revs.from, s->repo, revstr + 1) < 0)
} else if (git_revparse(&revs, s->repo, revstr) < 0)
return -1;
- if ((revs.flags & GIT_REVPARSE_SINGLE) != 0)
+ if ((revs.flags & GIT_REVSPEC_SINGLE) != 0)
push_rev(s, revs.from, hide);
else {
push_rev(s, revs.to, hide);
- if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
+ if ((revs.flags & GIT_REVSPEC_MERGE_BASE) != 0) {
git_oid base;
check_lg2(git_merge_base(&base, s->repo,
git_object_id(revs.from), git_object_id(revs.to)),
if ((error = git_revparse(&revspec, repo, range)))
return error;
- if (revspec.flags & GIT_REVPARSE_MERGE_BASE) {
+ if (revspec.flags & GIT_REVSPEC_MERGE_BASE) {
/* TODO: support "<commit>...<commit>" */
return GIT_EINVALIDSPEC;
}
check_lg2(git_revparse(&rs, repo, ps->spec), "Could not parse", ps->spec);
- if ((rs.flags & GIT_REVPARSE_SINGLE) != 0) {
+ if ((rs.flags & GIT_REVSPEC_SINGLE) != 0) {
git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
printf("%s\n", str);
git_object_free(rs.from);
}
- else if ((rs.flags & GIT_REVPARSE_RANGE) != 0) {
+ else if ((rs.flags & GIT_REVSPEC_RANGE) != 0) {
git_oid_tostr(str, sizeof(str), git_object_id(rs.to));
printf("%s\n", str);
git_object_free(rs.to);
- if ((rs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
+ if ((rs.flags & GIT_REVSPEC_MERGE_BASE) != 0) {
git_oid base;
check_lg2(git_merge_base(&base, repo,
git_object_id(rs.from), git_object_id(rs.to)),
git_object_free(obj);
}
-static void action_create_lighweight_tag(tag_state *state)
+static void action_create_lightweight_tag(tag_state *state)
{
git_repository *repo = state->repo;
struct tag_options *opts = state->opts;
print_usage();
if (*action != &action_create_tag)
- *action = &action_create_lighweight_tag;
+ *action = &action_create_lightweight_tag;
} else if (!strcmp(curr, "-n")) {
opts->num_lines = 1;
*action = &action_list_tags;
--- /dev/null
+/*
+ * libgit2 commit-graph fuzzer target.
+ *
+ * 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 <stdio.h>
+
+#include "git2.h"
+
+#include "buffer.h"
+#include "common.h"
+#include "futils.h"
+#include "hash.h"
+#include "commit_graph.h"
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ GIT_UNUSED(argc);
+ GIT_UNUSED(argv);
+
+ if (git_libgit2_init() < 0) {
+ fprintf(stderr, "Failed to initialize libgit2\n");
+ abort();
+ }
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ git_commit_graph_file file = {{0}};
+ git_commit_graph_entry e;
+ git_buf commit_graph_buf = GIT_BUF_INIT;
+ git_oid oid = {{0}};
+ bool append_hash = false;
+
+ if (size < 4)
+ return 0;
+
+ /*
+ * If the first byte in the stream has the high bit set, append the
+ * SHA1 hash so that the file is somewhat valid.
+ */
+ append_hash = *data & 0x80;
+ /* Keep a 4-byte alignment to avoid unaligned accesses. */
+ data += 4;
+ size -= 4;
+
+ if (append_hash) {
+ if (git_buf_init(&commit_graph_buf, size + sizeof(oid)) < 0)
+ goto cleanup;
+ if (git_hash_buf(&oid, data, size) < 0) {
+ fprintf(stderr, "Failed to compute the SHA1 hash\n");
+ abort();
+ }
+ memcpy(commit_graph_buf.ptr, data, size);
+ memcpy(commit_graph_buf.ptr + size, &oid, sizeof(oid));
+ } else {
+ git_buf_attach_notowned(&commit_graph_buf, (char *)data, size);
+ }
+
+ if (git_commit_graph_file_parse(
+ &file,
+ (const unsigned char *)git_buf_cstr(&commit_graph_buf),
+ git_buf_len(&commit_graph_buf))
+ < 0)
+ goto cleanup;
+
+ /* Search for any oid, just to exercise that codepath. */
+ if (git_commit_graph_entry_find(&e, &file, &oid, GIT_OID_HEXSZ) < 0)
+ goto cleanup;
+
+cleanup:
+ git_commit_graph_file_close(&file);
+ git_buf_dispose(&commit_graph_buf);
+ return 0;
+}
--- /dev/null
+ÿúÿ¦
\ No newline at end of file
--- /dev/null
+\eØúö
\ No newline at end of file
--- /dev/null
+ïïï\9cïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï@ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïë
\ No newline at end of file
--- /dev/null
+ïïï\9cïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï@ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï
\ No newline at end of file
--- /dev/null
+Ëó@ÿ~
\ No newline at end of file
# contributed code (possibly with some exceptions)
# "no" means the author does not consent
# "ask" means that the contributor wants to give/withhold
-# his/her consent on a patch-by-patch basis.
+# their consent on a patch-by-patch basis.
# "???" means the person is a prominent contributor who has
-# not yet made his/her standpoint clear.
+# not yet made their standpoint clear.
#
# Please try to keep the list alphabetically ordered. It will
# help in case we get all 600-ish git.git authors on it.
#include "git2/deprecated.h"
#include "git2/describe.h"
#include "git2/diff.h"
+#include "git2/email.h"
#include "git2/errors.h"
#include "git2/filter.h"
#include "git2/global.h"
* @param preimage the tree to apply the diff to
* @param diff the diff to apply
* @param options the options for the apply (or null for defaults)
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_apply_to_tree(
git_index **out,
* @param diff the diff to apply
* @param location the location to apply (workdir, index or both)
* @param options the options for the apply (or null for defaults)
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_apply(
git_repository *repo,
*
* Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes
* from a `.gitattributes` file in the repository at the HEAD revision.
+ *
+ * Passing the `GIT_ATTR_CHECK_INCLUDE_COMMIT` flag will use attributes
+ * from a `.gitattributes` file in a specific commit.
*/
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
+#define GIT_ATTR_CHECK_INCLUDE_COMMIT (1 << 4)
+
+/**
+* An options structure for querying attributes.
+*/
+typedef struct {
+ unsigned int version;
+
+ /** A combination of GIT_ATTR_CHECK flags */
+ unsigned int flags;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ git_oid *commit_id;
+#endif
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_ATTR_CHECK_INCLUDE_COMMIT` is specified.
+ */
+ git_oid attr_commit_id;
+} git_attr_options;
+
+#define GIT_ATTR_OPTIONS_VERSION 1
+#define GIT_ATTR_OPTIONS_INIT {GIT_ATTR_OPTIONS_VERSION}
/**
* Look up the value of one git attribute for path.
const char *path,
const char *name);
+/**
+ * Look up the value of one git attribute for path with extended options.
+ *
+ * @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
+ * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
+ * use the string value for attributes set to a value. You
+ * should NOT modify or free this value.
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path The path to check for attributes. Relative paths are
+ * interpreted relative to the repo root. The file does
+ * not have to exist, but if it does not, then it will be
+ * treated as a plain file (not a directory).
+ * @param name The name of the attribute to look up.
+ */
+GIT_EXTERN(int) git_attr_get_ext(
+ const char **value_out,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ const char *name);
+
/**
* Look up a list of git attributes for path.
*
size_t num_attr,
const char **names);
+/**
+ * Look up a list of git attributes for path with extended options.
+ *
+ * @param values_out An array of num_attr entries that will have string
+ * pointers written into it for the values of the attributes.
+ * You should not modify or free the values that are written
+ * into this array (although of course, you should free the
+ * array itself if you allocated it).
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path The path inside the repo to check attributes. This
+ * does not have to exist, but if it does not, then
+ * it will be treated as a plain file (i.e. not a directory).
+ * @param num_attr The number of attributes being looked up
+ * @param names An array of num_attr strings containing attribute names.
+ */
+GIT_EXTERN(int) git_attr_get_many_ext(
+ const char **values_out,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ size_t num_attr,
+ const char **names);
+
/**
* The callback used with git_attr_foreach.
*
git_attr_foreach_cb callback,
void *payload);
+/**
+ * Loop over all the git attributes for a path with extended options.
+ *
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path Path inside the repo to check attributes. This does not have
+ * to exist, but if it does not, then it will be treated as a
+ * plain file (i.e. not a directory).
+ * @param callback Function to invoke on each attribute name and value.
+ * See git_attr_foreach_cb.
+ * @param payload Passed on as extra parameter to callback function.
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_attr_foreach_ext(
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ git_attr_foreach_cb callback,
+ void *payload);
+
/**
* Flush the gitattributes cache.
*
typedef enum {
/** Normal blame, the default */
GIT_BLAME_NORMAL = 0,
- /** Track lines that have moved within a file (like `git blame -M`).
- * NOT IMPLEMENTED. */
+
+ /**
+ * Track lines that have moved within a file (like `git blame -M`).
+ *
+ * This is not yet implemented and reserved for future use.
+ */
GIT_BLAME_TRACK_COPIES_SAME_FILE = (1<<0),
- /** Track lines that have moved across files in the same commit (like `git blame -C`).
- * NOT IMPLEMENTED. */
+
+ /**
+ * Track lines that have moved across files in the same commit
+ * (like `git blame -C`).
+ *
+ * This is not yet implemented and reserved for future use.
+ */
GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = (1<<1),
- /** Track lines that have been copied from another file that exists in the
- * same commit (like `git blame -CC`). Implies SAME_FILE.
- * NOT IMPLEMENTED. */
+
+ /**
+ * Track lines that have been copied from another file that exists
+ * in the same commit (like `git blame -CC`). Implies SAME_FILE.
+ *
+ * This is not yet implemented and reserved for future use.
+ */
GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = (1<<2),
- /** Track lines that have been copied from another file that exists in *any*
- * commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
- * NOT IMPLEMENTED. */
+
+ /**
+ * Track lines that have been copied from another file that exists in
+ * *any* commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
+ *
+ * This is not yet implemented and reserved for future use.
+ */
GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
- /** Restrict the search of commits to those reachable following only the
- * first parents. */
+
+ /**
+ * Restrict the search of commits to those reachable following only
+ * the first parents.
+ */
GIT_BLAME_FIRST_PARENT = (1<<4),
- /** Use mailmap file to map author and committer names and email addresses
- * to canonical real names and email addresses. The mailmap will be read
- * from the working directory, or HEAD in a bare repository. */
+
+ /**
+ * Use mailmap file to map author and committer names and email
+ * addresses to canonical real names and email addresses. The
+ * mailmap will be read from the working directory, or HEAD in a
+ * bare repository.
+ */
GIT_BLAME_USE_MAILMAP = (1<<5),
+
/** Ignore whitespace differences */
GIT_BLAME_IGNORE_WHITESPACE = (1<<6),
} git_blame_flag_t;
/** A combination of `git_blame_flag_t` */
uint32_t flags;
- /** The lower bound on the number of alphanumeric
- * characters that must be detected as moving/copying within a file for it to
- * associate those lines with the parent commit. The default value is 20.
- * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
- * flags are specified.
+
+ /**
+ * The lower bound on the number of alphanumeric characters that
+ * must be detected as moving/copying within a file for it to
+ * associate those lines with the parent commit. The default value
+ * is 20.
+ *
+ * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
+ * flags are specified.
*/
uint16_t min_match_characters;
+
/** The id of the newest commit to consider. The default is HEAD. */
git_oid newest_commit;
+
/**
* The id of the oldest commit to consider.
* The default is the first commit encountered with a NULL parent.
*/
git_oid oldest_commit;
+
/**
* The first line in the file to blame.
* The default is 1 (line numbers start with 1).
*/
size_t min_line;
+
/**
* The last line in the file to blame.
* The default is the last line of the file.
/**
* Structure that represents a blame hunk.
- *
- * - `lines_in_hunk` is the number of lines in this hunk
- * - `final_commit_id` is the OID of the commit where this line was last
- * changed.
- * - `final_start_line_number` is the 1-based line number where this hunk
- * begins, in the final version of the file
- * - `final_signature` is the author of `final_commit_id`. If
- * `GIT_BLAME_USE_MAILMAP` has been specified, it will contain the canonical
- * real name and email address.
- * - `orig_commit_id` is the OID of the commit where this hunk was found. This
- * will usually be the same as `final_commit_id`, except when
- * `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
- * - `orig_path` is the path to the file where this hunk originated, as of the
- * commit specified by `orig_commit_id`.
- * - `orig_start_line_number` is the 1-based line number where this hunk begins
- * in the file named by `orig_path` in the commit specified by
- * `orig_commit_id`.
- * - `orig_signature` is the author of `orig_commit_id`. If
- * `GIT_BLAME_USE_MAILMAP` has been specified, it will contain the canonical
- * real name and email address.
- * - `boundary` is 1 iff the hunk has been tracked to a boundary commit (the
- * root, or the commit specified in git_blame_options.oldest_commit)
*/
typedef struct git_blame_hunk {
+ /**
+ * The number of lines in this hunk.
+ */
size_t lines_in_hunk;
+ /**
+ * The OID of the commit where this line was last changed.
+ */
git_oid final_commit_id;
+
+ /**
+ * The 1-based line number where this hunk begins, in the final version
+ * of the file.
+ */
size_t final_start_line_number;
+
+ /**
+ * The author of `final_commit_id`. If `GIT_BLAME_USE_MAILMAP` has been
+ * specified, it will contain the canonical real name and email address.
+ */
git_signature *final_signature;
+ /**
+ * The OID of the commit where this hunk was found.
+ * This will usually be the same as `final_commit_id`, except when
+ * `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
+ */
git_oid orig_commit_id;
+
+ /**
+ * The path to the file where this hunk originated, as of the commit
+ * specified by `orig_commit_id`.
+ */
const char *orig_path;
+
+ /**
+ * The 1-based line number where this hunk begins in the file named by
+ * `orig_path` in the commit specified by `orig_commit_id`.
+ */
size_t orig_start_line_number;
+
+ /**
+ * The author of `orig_commit_id`. If `GIT_BLAME_USE_MAILMAP` has been
+ * specified, it will contain the canonical real name and email address.
+ */
git_signature *orig_signature;
+ /**
+ * The 1 iff the hunk has been tracked to a boundary commit (the root,
+ * or the commit specified in git_blame_options.oldest_commit)
+ */
char boundary;
} git_blame_hunk;
* time.
*
* @param blob pointer to the blob
- * @return the pointer
+ * @return the pointer, or NULL on error
*/
GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob);
* When set, filters will be loaded from a `.gitattributes` file
* in the HEAD commit.
*/
- GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD = (1 << 2),
+ GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD = (1 << 2),
+
+ /**
+ * When set, filters will be loaded from a `.gitattributes` file
+ * in the specified commit.
+ */
+ GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT = (1 << 3),
} git_blob_filter_flag_t;
/**
* The options used when applying filter options to a file.
+ *
+ * Initialize with `GIT_BLOB_FILTER_OPTIONS_INIT`. Alternatively, you can
+ * use `git_blob_filter_options_init`.
+ *
*/
typedef struct {
int version;
/** Flags to control the filtering process, see `git_blob_filter_flag_t` above */
uint32_t flags;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ git_oid *commit_id;
+#endif
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT` is specified.
+ */
+ git_oid attr_commit_id;
} git_blob_filter_options;
#define GIT_BLOB_FILTER_OPTIONS_VERSION 1
#define GIT_BLOB_FILTER_OPTIONS_INIT {GIT_BLOB_FILTER_OPTIONS_VERSION, GIT_BLOB_FILTER_CHECK_FOR_BINARY}
+/**
+ * Initialize git_blob_filter_options structure
+ *
+ * Initializes a `git_blob_filter_options` with default values. Equivalent
+ * to creating an instance with `GIT_BLOB_FILTER_OPTIONS_INIT`.
+ *
+ * @param opts The `git_blob_filter_options` struct to initialize.
+ * @param version The struct version; pass `GIT_BLOB_FILTER_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_blob_filter_options_init(git_blob_filter_options *opts, unsigned int version);
+
/**
* Get a buffer with the filtered content of a blob.
*
* Write an in-memory buffer to the ODB as a blob
*
* @param id return the id of the written blob
- * @param repo repository where to blob will be written
+ * @param repo repository where the blob will be written
* @param buffer data to be written into the blob
* @param len length of the data
* @return 0 or an error code
*/
GIT_EXTERN(int) git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname);
+/**
+ * Retrieve the upstream merge of a local branch
+ *
+ * This will return the currently configured "branch.*.merge" for a given
+ * branch. This branch must be local.
+ *
+ * @param buf the buffer into which to write the name
+ * @param repo the repository in which to look
+ * @param refname the full name of the branch
+ * @return 0 or an error code
+ */
+ GIT_EXTERN(int) git_branch_upstream_merge(git_buf *buf, git_repository *repo, const char *refname);
+
+/**
+ * Determine whether a branch name is valid, meaning that (when prefixed
+ * with `refs/heads/`) that it is a valid reference name, and that any
+ * additional branch name restrictions are imposed (eg, it cannot start
+ * with a `-`).
+ *
+ * @param valid output pointer to set with validity of given branch name
+ * @param name a branch name to test
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_branch_name_is_valid(int *valid, const char *name);
+
/** @} */
GIT_END_DECL
#endif
#define INCLUDE_git_cert_h__
#include "common.h"
+#include "types.h"
/**
* @file git2/cert.h
GIT_CERT_SSH_SHA1 = (1 << 1),
/** SHA-256 is available */
GIT_CERT_SSH_SHA256 = (1 << 2),
+ /** Raw hostkey is available */
+ GIT_CERT_SSH_RAW = (1 << 3),
} git_cert_ssh_t;
+typedef enum {
+ /** The raw key is of an unknown type. */
+ GIT_CERT_SSH_RAW_TYPE_UNKNOWN = 0,
+ /** The raw key is an RSA key. */
+ GIT_CERT_SSH_RAW_TYPE_RSA = 1,
+ /** The raw key is a DSS key. */
+ GIT_CERT_SSH_RAW_TYPE_DSS = 2,
+ /** The raw key is a ECDSA 256 key. */
+ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256 = 3,
+ /** The raw key is a ECDSA 384 key. */
+ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384 = 4,
+ /** The raw key is a ECDSA 521 key. */
+ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521 = 5,
+ /** The raw key is a ED25519 key. */
+ GIT_CERT_SSH_RAW_TYPE_KEY_ED25519 = 6
+} git_cert_ssh_raw_type_t;
+
/**
* Hostkey information taken from libssh2
*/
git_cert parent; /**< The parent cert */
/**
- * A hostkey type from libssh2, either
- * `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1`
+ * A bitmask containing the available fields.
*/
git_cert_ssh_t type;
/**
- * Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will
+ * Hostkey hash. If `type` has `GIT_CERT_SSH_MD5` set, this will
* have the MD5 hash of the hostkey.
*/
unsigned char hash_md5[16];
/**
- * Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will
+ * Hostkey hash. If `type` has `GIT_CERT_SSH_SHA1` set, this will
* have the SHA-1 hash of the hostkey.
*/
unsigned char hash_sha1[20];
/**
- * Hostkey hash. If type has `GIT_CERT_SSH_SHA256` set, this will
+ * Hostkey hash. If `type` has `GIT_CERT_SSH_SHA256` set, this will
* have the SHA-256 hash of the hostkey.
*/
unsigned char hash_sha256[32];
+
+ /**
+ * Raw hostkey type. If `type` has `GIT_CERT_SSH_RAW` set, this will
+ * have the type of the raw hostkey.
+ */
+ git_cert_ssh_raw_type_t raw_type;
+
+ /**
+ * Pointer to the raw hostkey. If `type` has `GIT_CERT_SSH_RAW` set,
+ * this will have the raw contents of the hostkey.
+ */
+ const char *hostkey;
+
+ /**
+ * Raw hostkey length. If `type` has `GIT_CERT_SSH_RAW` set, this will
+ * have the length of the raw contents of the hostkey.
+ */
+ size_t hostkey_len;
} git_cert_hostkey;
/**
/** Normally checkout writes the index upon completion; this prevents that. */
GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23),
+ /**
+ * Show what would be done by a checkout. Stop after sending
+ * notifications; don't update the working directory or index.
+ */
+ GIT_CHECKOUT_DRY_RUN = (1u << 24),
+
/**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/
* Checkout will invoke an options notification callback (`notify_cb`) for
* certain cases - you pick which ones via `notify_flags`:
*
- * - GIT_CHECKOUT_NOTIFY_CONFLICT invokes checkout on conflicting paths.
- *
- * - GIT_CHECKOUT_NOTIFY_DIRTY notifies about "dirty" files, i.e. those that
- * do not need an update but no longer match the baseline. Core git
- * displays these files when checkout runs, but won't stop the checkout.
- *
- * - GIT_CHECKOUT_NOTIFY_UPDATED sends notification for any file changed.
- *
- * - GIT_CHECKOUT_NOTIFY_UNTRACKED notifies about untracked files.
- *
- * - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files.
- *
* Returning a non-zero value from this callback will cancel the checkout.
* The non-zero return value will be propagated back and returned by the
* git_checkout_... call.
*/
typedef enum {
GIT_CHECKOUT_NOTIFY_NONE = 0,
+
+ /**
+ * Invokes checkout on conflicting paths.
+ */
GIT_CHECKOUT_NOTIFY_CONFLICT = (1u << 0),
+
+ /**
+ * Notifies about "dirty" files, i.e. those that do not need an update
+ * but no longer match the baseline. Core git displays these files when
+ * checkout runs, but won't stop the checkout.
+ */
GIT_CHECKOUT_NOTIFY_DIRTY = (1u << 1),
+
+ /**
+ * Sends notification for any file changed.
+ */
GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2),
+
+ /**
+ * Notifies about untracked files.
+ */
GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
+
+ /**
+ * Notifies about ignored files.
+ */
GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4),
GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu
* The name of the branch to checkout. NULL means use the
* remote's default branch.
*/
- const char* checkout_branch;
+ const char *checkout_branch;
/**
* A callback used to create the new repository into which to
GIT_EXTERN(int) git_commit_dup(git_commit **out, git_commit *source);
/**
- * Commit signing callback.
- *
- * The callback will be called with the commit content, giving a user an
- * opportunity to sign the commit content. The signature_field
- * buf may be left empty to specify the default field "gpgsig".
- *
- * Signatures can take the form of any string, and can be created on an arbitrary
- * header field. Signatures are most commonly used for verifying authorship of a
- * commit using GPG or a similar cryptographically secure signing algorithm.
- * See https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work for more
- * details.
- *
- * When the callback:
- * - returns GIT_PASSTHROUGH, no signature will be added to the commit.
- * - returns < 0, commit creation will be aborted.
- * - returns GIT_OK, the signature parameter is expected to be filled.
- */
-typedef int (*git_commit_signing_cb)(
- git_buf *signature, git_buf *signature_field, const char *commit_content, void *payload);
+ * Commit creation callback: used when a function is going to create
+ * commits (for example, in `git_rebase_commit`) to allow callers to
+ * override the commit creation behavior. For example, users may
+ * wish to sign commits by providing this information to
+ * `git_commit_create_buffer`, signing that buffer, then calling
+ * `git_commit_create_with_signature`. The resultant commit id
+ * should be set in the `out` object id parameter.
+ *
+ * @param out pointer that this callback will populate with the object
+ * id of the commit that is created
+ * @param author the author name and time of the commit
+ * @param committer the committer name and time of the commit
+ * @param message_encoding the encoding of the given message, or NULL
+ * to assume UTF8
+ * @param message the commit message
+ * @param tree the tree to be committed
+ * @param parent_count the number of parents for this commit
+ * @param parents the commit parents
+ * @param payload the payload pointer in the rebase options
+ * @return 0 if this callback has created the commit and populated the out
+ * parameter, GIT_PASSTHROUGH if the callback has not created a
+ * commit and wants the calling function to create the commit as
+ * if no callback had been specified, any other value to stop
+ * and return a failure
+ */
+typedef int (*git_commit_create_cb)(
+ git_oid *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[],
+ void *payload);
/** @} */
GIT_END_DECL
/**
* The separator used in path list strings (ie like in the PATH
- * environment variable). A semi-colon ";" is used on Windows, and
- * a colon ":" for all other systems.
+ * environment variable). A semi-colon ";" is used on Windows and
+ * AmigaOS, and a colon ":" for all other systems.
*/
-#ifdef GIT_WIN32
+#if defined(GIT_WIN32) || defined(AMIGA)
#define GIT_PATH_LIST_SEPARATOR ';'
#else
#define GIT_PATH_LIST_SEPARATOR ':'
GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS,
GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE,
GIT_OPT_GET_MWINDOW_FILE_LIMIT,
- GIT_OPT_SET_MWINDOW_FILE_LIMIT
+ GIT_OPT_SET_MWINDOW_FILE_LIMIT,
+ GIT_OPT_SET_ODB_PACKED_PRIORITY,
+ GIT_OPT_SET_ODB_LOOSE_PRIORITY,
+ GIT_OPT_GET_EXTENSIONS,
+ GIT_OPT_SET_EXTENSIONS
} git_libgit2_opt_t;
/**
* >
* > - `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,
* > authentication, use expect/continue when POSTing data.
* > This option is not available on Windows.
*
+ * opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, int priority)
+ * > Override the default priority of the packed ODB backend which
+ * > is added when default backends are assigned to a repository
+ *
+ * opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, int priority)
+ * > Override the default priority of the loose ODB backend which
+ * > is added when default backends are assigned to a repository
+ *
+ * opts(GIT_OPT_GET_EXTENSIONS, git_strarray *out)
+ * > Returns the list of git extensions that are supported. This
+ * > is the list of built-in extensions supported by libgit2 and
+ * > custom extensions that have been added with
+ * > `GIT_OPT_SET_EXTENSIONS`. Extensions that have been negated
+ * > will not be returned. The returned list should be released
+ * > with `git_strarray_dispose`.
+ *
+ * opts(GIT_OPT_SET_EXTENSIONS, const char **extensions, size_t len)
+ * > Set that the given git extensions are supported by the caller.
+ * > Extensions supported by libgit2 may be negated by prefixing
+ * > them with a `!`. For example: setting extensions to
+ * > { "!noop", "newext" } indicates that the caller does not want
+ * > to support repositories with the `noop` extension but does want
+ * > to support repositories with the `newext` extension.
+ *
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure
*
* Git allows you to store your global configuration at
* `$HOME/.gitconfig` or `$XDG_CONFIG_HOME/git/config`. For backwards
- * compatability, the XDG file shouldn't be used unless the use has
+ * compatibility, the XDG file shouldn't be used unless the use has
* created it explicitly. With this function you'll open the correct
* one to write to.
*
#include "describe.h"
#include "diff.h"
#include "errors.h"
+#include "filter.h"
#include "index.h"
#include "indexer.h"
#include "merge.h"
#include "trace.h"
#include "repository.h"
#include "revert.h"
+#include "revparse.h"
#include "stash.h"
#include "status.h"
#include "submodule.h"
/**@}*/
-/** @name Deprecated Blob Functions
+/** @name Deprecated Blob Functions and Constants
*
- * These functions are retained for backward compatibility. The newer
- * versions of these functions should be preferred in all new code.
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these functions and values
+ * should be preferred in all new code.
*
* There is no plan to remove these backward compatibility values at
* this time.
*/
/**@{*/
+#define GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD
+
GIT_EXTERN(int) git_blob_create_fromworkdir(git_oid *id, git_repository *repo, const char *relative_path);
GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path);
GIT_EXTERN(int) git_blob_create_fromstream(
/**@}*/
+/** @name Deprecated Filter Functions
+ *
+ * These functions are retained for backward compatibility. The
+ * newer versions of these functions should be preferred in all
+ * new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/** Deprecated in favor of `git_filter_list_stream_buffer`.
+ *
+ * @deprecated Use git_filter_list_stream_buffer
+ * @see Use git_filter_list_stream_buffer
+ */
+GIT_EXTERN(int) git_filter_list_stream_data(
+ git_filter_list *filters,
+ git_buf *data,
+ git_writestream *target);
+
+/** Deprecated in favor of `git_filter_list_apply_to_buffer`.
+ *
+ * @deprecated Use git_filter_list_apply_to_buffer
+ * @see Use git_filter_list_apply_to_buffer
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_data(
+ git_buf *out,
+ git_filter_list *filters,
+ git_buf *in);
+
+/**@}*/
+
+/** @name Deprecated Tree Functions
+ *
+ * These functions are retained for backward compatibility. The
+ * newer versions of these functions and values should be preferred
+ * in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Write the contents of the tree builder as a tree object.
+ * This is an alias of `git_treebuilder_write` and is preserved
+ * for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_treebuilder_write
+ * @see git_treebuilder_write
+ */
+GIT_EXTERN(int) git_treebuilder_write_with_buffer(
+ git_oid *oid, git_treebuilder *bld, git_buf *tree);
+
+/**@}*/
+
/** @name Deprecated Buffer Functions
*
* These functions and enumeration values are retained for backward
*/
/**@{*/
+/**
+ * Static initializer for git_buf from static buffer
+ */
+#define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) }
+
+/**
+ * Resize the buffer allocation to make more space.
+ *
+ * This will attempt to grow the buffer to accommodate the target size.
+ *
+ * If the buffer refers to memory that was not allocated by libgit2 (i.e.
+ * the `asize` field is zero), then `ptr` will be replaced with a newly
+ * allocated block of data. Be careful so that memory allocated by the
+ * caller is not lost. As a special variant, if you pass `target_size` as
+ * 0 and the memory is not allocated by libgit2, this will allocate a new
+ * buffer of size `size` and copy the external data into it.
+ *
+ * Currently, this will never shrink a buffer, only expand it.
+ *
+ * If the allocation fails, this will return an error and the buffer will be
+ * marked as invalid for future operations, invaliding the contents.
+ *
+ * @param buffer The buffer to be resized; may or may not be allocated yet
+ * @param target_size The desired available size
+ * @return 0 on success, -1 on allocation failure
+ */
+GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size);
+
+/**
+ * Set buffer to a copy of some raw data.
+ *
+ * @param buffer The buffer to set
+ * @param data The data to copy into the buffer
+ * @param datalen The length of the data to copy into the buffer
+ * @return 0 on success, -1 on allocation failure
+ */
+GIT_EXTERN(int) git_buf_set(
+ git_buf *buffer, const void *data, size_t datalen);
+
+/**
+* Check quickly if buffer looks like it contains binary data
+*
+* @param buf Buffer to check
+* @return 1 if buffer looks like non-text data
+*/
+GIT_EXTERN(int) git_buf_is_binary(const git_buf *buf);
+
+/**
+* Check quickly if buffer contains a NUL byte
+*
+* @param buf Buffer to check
+* @return 1 if buffer contains a NUL byte
+*/
+GIT_EXTERN(int) git_buf_contains_nul(const git_buf *buf);
+
/**
* Free the memory referred to by the git_buf. This is an alias of
* `git_buf_dispose` and is preserved for backward compatibility.
/**@}*/
+/** @name Deprecated Commit Definitions
+ */
+/**@{*/
+
+/**
+ * Provide a commit signature during commit creation.
+ *
+ * Callers should instead define a `git_commit_create_cb` that
+ * generates a commit buffer using `git_commit_create_buffer`, sign
+ * that buffer and call `git_commit_create_with_signature`.
+ *
+ * @deprecated use a `git_commit_create_cb` instead
+ */
+typedef int (*git_commit_signing_cb)(
+ git_buf *signature,
+ git_buf *signature_field,
+ const char *commit_content,
+ void *payload);
+
+/**@}*/
+
/** @name Deprecated Config Functions and Constants
*/
/**@{*/
/**@}*/
+/** @name Deprecated Diff Functions and Constants
+ *
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these functions and values
+ * should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Formatting options for diff e-mail generation
+ */
+typedef enum {
+ /** Normal patch, the default */
+ GIT_DIFF_FORMAT_EMAIL_NONE = 0,
+
+ /** Don't insert "[PATCH]" in the subject header*/
+ GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0),
+
+} git_diff_format_email_flags_t;
+
+/**
+ * Options for controlling the formatting of the generated e-mail.
+ */
+typedef struct {
+ unsigned int version;
+
+ /** see `git_diff_format_email_flags_t` above */
+ uint32_t flags;
+
+ /** This patch number */
+ size_t patch_no;
+
+ /** Total number of patches in this series */
+ size_t total_patches;
+
+ /** id to use for the commit */
+ const git_oid *id;
+
+ /** Summary of the change */
+ const char *summary;
+
+ /** Commit message's body */
+ const char *body;
+
+ /** Author of the change */
+ const git_signature *author;
+} git_diff_format_email_options;
+
+#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1
+#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL}
+
+/**
+ * Create an e-mail ready patch from a diff.
+ *
+ * @deprecated git_email_create_from_diff
+ * @see git_email_create_from_diff
+ */
+GIT_EXTERN(int) git_diff_format_email(
+ git_buf *out,
+ git_diff *diff,
+ const git_diff_format_email_options *opts);
+
+/**
+ * Create an e-mail ready patch for a commit.
+ *
+ * @deprecated git_email_create_from_commit
+ * @see git_email_create_from_commit
+ */
+GIT_EXTERN(int) git_diff_commit_as_email(
+ git_buf *out,
+ git_repository *repo,
+ git_commit *commit,
+ size_t patch_no,
+ size_t total_patches,
+ uint32_t flags,
+ const git_diff_options *diff_opts);
+
+/**
+ * Initialize git_diff_format_email_options structure
+ *
+ * Initializes a `git_diff_format_email_options` with default values. Equivalent
+ * to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
+ *
+ * @param opts The `git_blame_options` struct to initialize.
+ * @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_diff_format_email_options_init(
+ git_diff_format_email_options *opts,
+ unsigned int version);
+
+/**@}*/
+
/** @name Deprecated Error Functions and Constants
*
* These functions and enumeration values are retained for backward
/**@}*/
-/** @name Deprecated Reference Constants
+/** @name Deprecated Remote Functions
*
- * These enumeration values are retained for backward compatibility. The
- * newer versions of these values should be preferred in all new code.
+ * These functions are retained for backward compatibility. The newer
+ * versions of these functions should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility functions at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Ensure the remote name is well-formed.
+ *
+ * @deprecated Use git_remote_name_is_valid
+ * @param remote_name name to be checked.
+ * @return 1 if the reference name is acceptable; 0 if it isn't
+ */
+GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
+
+/**@}*/
+
+/** @name Deprecated Reference Functions and Constants
+ *
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these values should be
+ * preferred in all new code.
*
* There is no plan to remove these backward compatibility values at
* this time.
#define GIT_REF_FORMAT_REFSPEC_PATTERN GIT_REFERENCE_FORMAT_REFSPEC_PATTERN
#define GIT_REF_FORMAT_REFSPEC_SHORTHAND GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND
+/**
+ * Ensure the reference name is well-formed.
+ *
+ * Valid reference names must follow one of two patterns:
+ *
+ * 1. Top-level names must contain only capital letters and underscores,
+ * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * @deprecated Use git_reference_name_is_valid
+ * @param refname name to be checked.
+ * @return 1 if the reference name is acceptable; 0 if it isn't
+ */
+GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
+
GIT_EXTERN(int) git_tag_create_frombuffer(
git_oid *oid,
git_repository *repo,
/**@}*/
+/** @name Deprecated Revspec Constants
+ *
+ * These enumeration values are retained for backward compatibility.
+ * The newer versions of these values should be preferred in all new
+ * code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+typedef git_revspec_t git_revparse_mode_t;
+
+#define GIT_REVPARSE_SINGLE GIT_REVSPEC_SINGLE
+#define GIT_REVPARSE_RANGE GIT_REVSPEC_RANGE
+#define GIT_REVPARSE_MERGE_BASE GIT_REVSPEC_MERGE_BASE
+
+/**@}*/
+
/** @name Deprecated Credential Types
*
* These types are retained for backward compatibility. The newer
* There is no plan to remove these backward compatibility values at
* this time.
*/
+/**@{*/
typedef git_credential git_cred;
typedef git_credential_userpass_plaintext git_cred_userpass_plaintext;
/**@}*/
+/** @name Deprecated OID Array Functions
+ *
+ * These types are retained for backward compatibility. The newer
+ * versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Free the memory referred to by the git_oidarray. This is an alias of
+ * `git_oidarray_dispose` and is preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_oidarray_dispose
+ * @see git_oidarray_dispose
+ */
+GIT_EXTERN(void) git_oidarray_free(git_oidarray *array);
+
+/**@}*/
+
/** @name Deprecated Transfer Progress Types
*
* These types are retained for backward compatibility. The newer
*/
GIT_DIFF_INDENT_HEURISTIC = (1u << 18),
+ /** Ignore blank lines */
+ GIT_DIFF_IGNORE_BLANK_LINES = (1u << 19),
+
/** Treat all files as text, disabling binary attributes & detection */
GIT_DIFF_FORCE_TEXT = (1u << 20),
/** Treat all files as binary, disabling text diffs */
* Although this is called a "file", it could represent a file, a symbolic
* link, a submodule commit id, or even a tree (although that only if you
* are tracking type changes or ignored/untracked directories).
- *
- * The `id` is the `git_oid` of the item. If the entry represents an
- * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
- * then the oid will be zeroes.
- *
- * `path` is the NUL-terminated path to the entry relative to the working
- * directory of the repository.
- *
- * `size` is the size of the entry in bytes.
- *
- * `flags` is a combination of the `git_diff_flag_t` types
- *
- * `mode` is, roughly, the stat() `st_mode` value for the item. This will
- * be restricted to one of the `git_filemode_t` values.
- *
- * The `id_abbrev` represents the known length of the `id` field, when
- * converted to a hex string. It is generally `GIT_OID_HEXSZ`, unless this
- * delta was created from reading a patch file, in which case it may be
- * abbreviated to something reasonable, like 7 characters.
*/
typedef struct {
+ /**
+ * The `git_oid` of the item. If the entry represents an
+ * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
+ * then the oid will be zeroes.
+ */
git_oid id;
+
+ /**
+ * The NUL-terminated path to the entry relative to the working
+ * directory of the repository.
+ */
const char *path;
+
+ /**
+ * The size of the entry in bytes.
+ */
git_object_size_t size;
+
+ /**
+ * A combination of the `git_diff_flag_t` types
+ */
uint32_t flags;
+
+ /**
+ * Roughly, the stat() `st_mode` value for the item. This will
+ * be restricted to one of the `git_filemode_t` values.
+ */
uint16_t mode;
+
+ /**
+ * Represents the known length of the `id` field, when
+ * converted to a hex string. It is generally `GIT_OID_HEXSZ`, unless this
+ * delta was created from reading a patch file, in which case it may be
+ * abbreviated to something reasonable, like 7 characters.
+ */
uint16_t id_abbrev;
} git_diff_file;
/**
* Query how many diff deltas are there in a diff filtered by type.
*
- * This works just like `git_diff_entrycount()` with an extra parameter
+ * This works just like `git_diff_num_deltas()` with an extra parameter
* that is a `git_delta_t` and returns just the count of how many deltas
* match that particular type.
*
*/
GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats);
-/**
- * Formatting options for diff e-mail generation
- */
-typedef enum {
- /** Normal patch, the default */
- GIT_DIFF_FORMAT_EMAIL_NONE = 0,
-
- /** Don't insert "[PATCH]" in the subject header*/
- GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0),
-
-} git_diff_format_email_flags_t;
-
-/**
- * Options for controlling the formatting of the generated e-mail.
- */
-typedef struct {
- unsigned int version;
-
- /** see `git_diff_format_email_flags_t` above */
- uint32_t flags;
-
- /** This patch number */
- size_t patch_no;
-
- /** Total number of patches in this series */
- size_t total_patches;
-
- /** id to use for the commit */
- const git_oid *id;
-
- /** Summary of the change */
- const char *summary;
-
- /** Commit message's body */
- const char *body;
-
- /** Author of the change */
- const git_signature *author;
-} git_diff_format_email_options;
-
-#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1
-#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL}
-
-/**
- * Create an e-mail ready patch from a diff.
- *
- * @param out buffer to store the e-mail patch in
- * @param diff containing the commit
- * @param opts structure with options to influence content and formatting.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_diff_format_email(
- git_buf *out,
- git_diff *diff,
- const git_diff_format_email_options *opts);
-
-/**
- * Create an e-mail ready patch for a commit.
- *
- * Does not support creating patches for merge commits (yet).
- *
- * @param out buffer to store the e-mail patch in
- * @param repo containing the commit
- * @param commit pointer to up commit
- * @param patch_no patch number of the commit
- * @param total_patches total number of patches in the patch set
- * @param flags determines the formatting of the e-mail
- * @param diff_opts structure with options to influence diff or NULL for defaults.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_diff_commit_as_email(
- git_buf *out,
- git_repository *repo,
- git_commit *commit,
- size_t patch_no,
- size_t total_patches,
- uint32_t flags,
- const git_diff_options *diff_opts);
-
-/**
- * Initialize git_diff_format_email_options structure
- *
- * Initializes a `git_diff_format_email_options` with default values. Equivalent
- * to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
- *
- * @param opts The `git_blame_options` struct to initialize.
- * @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_diff_format_email_options_init(
- git_diff_format_email_options *opts,
- unsigned int version);
-
/**
* Patch ID options structure
*
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_email_h__
+#define INCLUDE_git_email_h__
+
+#include "common.h"
+
+/**
+ * @file git2/email.h
+ * @brief Git email formatting and application routines.
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Formatting options for diff e-mail generation
+ */
+typedef enum {
+ /** Normal patch, the default */
+ GIT_EMAIL_CREATE_DEFAULT = 0,
+
+ /** Do not include patch numbers in the subject prefix. */
+ GIT_EMAIL_CREATE_OMIT_NUMBERS = (1u << 0),
+
+ /**
+ * Include numbers in the subject prefix even when the
+ * patch is for a single commit (1/1).
+ */
+ GIT_EMAIL_CREATE_ALWAYS_NUMBER = (1u << 1),
+
+ /** Do not perform rename or similarity detection. */
+ GIT_EMAIL_CREATE_NO_RENAMES = (1u << 2),
+} git_email_create_flags_t;
+
+/**
+ * Options for controlling the formatting of the generated e-mail.
+ */
+typedef struct {
+ unsigned int version;
+
+ /** see `git_email_create_flags_t` above */
+ uint32_t flags;
+
+ /** Options to use when creating diffs */
+ git_diff_options diff_opts;
+
+ /** Options for finding similarities within diffs */
+ git_diff_find_options diff_find_opts;
+
+ /**
+ * The subject prefix, by default "PATCH". If set to an empty
+ * string ("") then only the patch numbers will be shown in the
+ * prefix. If the subject_prefix is empty and patch numbers
+ * are not being shown, the prefix will be omitted entirely.
+ */
+ const char *subject_prefix;
+
+ /**
+ * The starting patch number; this cannot be 0. By default,
+ * this is 1.
+ */
+ size_t start_number;
+
+ /** The "re-roll" number. By default, there is no re-roll. */
+ size_t reroll_number;
+} git_email_create_options;
+
+/*
+ * By default, our options include rename detection and binary
+ * diffs to match `git format-patch`.
+ */
+#define GIT_EMAIL_CREATE_OPTIONS_VERSION 1
+#define GIT_EMAIL_CREATE_OPTIONS_INIT \
+{ \
+ GIT_EMAIL_CREATE_OPTIONS_VERSION, \
+ GIT_EMAIL_CREATE_DEFAULT, \
+ { GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_SHOW_BINARY, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3 }, \
+ GIT_DIFF_FIND_OPTIONS_INIT \
+}
+
+/**
+ * Create a diff for a commit in mbox format for sending via email.
+ *
+ * @param out buffer to store the e-mail patch in
+ * @param diff the changes to include in the email
+ * @param patch_idx the patch index
+ * @param patch_count the total number of patches that will be included
+ * @param commit_id the commit id for this change
+ * @param summary the commit message for this change
+ * @param body optional text to include above the diffstat
+ * @param author the person who authored this commit
+ * @param opts email creation options
+ */
+GIT_EXTERN(int) git_email_create_from_diff(
+ git_buf *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *opts);
+
+/**
+ * Create a diff for a commit in mbox format for sending via email.
+ * The commit must not be a merge commit.
+ *
+ * @param out buffer to store the e-mail patch in
+ * @param commit commit to create a patch for
+ * @param opts email creation options
+ */
+GIT_EXTERN(int) git_email_create_from_commit(
+ git_buf *out,
+ git_commit *commit,
+ const git_email_create_options *opts);
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */
GIT_ELOCKED = -14, /**< Lock file prevented operation */
GIT_EMODIFIED = -15, /**< Reference value does not match expected */
- GIT_EAUTH = -16, /**< Authentication error */
- GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */
+ GIT_EAUTH = -16, /**< Authentication error */
+ GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */
GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */
- GIT_EPEEL = -19, /**< The requested peel operation is not possible */
- GIT_EEOF = -20, /**< Unexpected EOF */
- GIT_EINVALID = -21, /**< Invalid operation or input */
+ GIT_EPEEL = -19, /**< The requested peel operation is not possible */
+ GIT_EEOF = -20, /**< Unexpected EOF */
+ GIT_EINVALID = -21, /**< Invalid operation or input */
GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */
- GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */
+ GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */
GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */
GIT_PASSTHROUGH = -30, /**< A user-configured callback refused to act */
/** Load attributes from `.gitattributes` in the root of HEAD */
GIT_FILTER_ATTRIBUTES_FROM_HEAD = (1u << 2),
+
+ /**
+ * Load attributes from `.gitattributes` in a given commit.
+ * This can only be specified in a `git_filter_options`.
+ */
+ GIT_FILTER_ATTRIBUTES_FROM_COMMIT = (1u << 3),
} git_filter_flag_t;
+/**
+ * Filtering options
+ */
+typedef struct {
+ unsigned int version;
+
+ /** See `git_filter_flag_t` above */
+ uint32_t flags;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ git_oid *commit_id;
+#endif
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_FILTER_ATTRIBUTES_FROM_COMMIT` is specified.
+ */
+ git_oid attr_commit_id;
+} git_filter_options;
+
+ #define GIT_FILTER_OPTIONS_VERSION 1
+ #define GIT_FILTER_OPTIONS_INIT {GIT_FILTER_OPTIONS_VERSION}
+
/**
* A filter that can transform file data
*
git_filter_mode_t mode,
uint32_t flags);
+/**
+ * Load the filter list for a given path.
+ *
+ * This will return 0 (success) but set the output git_filter_list to NULL
+ * if no filters are requested for the given file.
+ *
+ * @param filters Output newly created git_filter_list (or NULL)
+ * @param repo Repository object that contains `path`
+ * @param blob The blob to which the filter will be applied (if known)
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @param opts The `git_filter_options` to use when loading filters
+ * @return 0 on success (which could still return NULL if no filters are
+ * needed for the requested file), <0 on error
+ */
+GIT_EXTERN(int) git_filter_list_load_ext(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob,
+ const char *path,
+ git_filter_mode_t mode,
+ git_filter_options *opts);
+
/**
* Query the filter list to see if a given filter (by name) will run.
* The built-in filters "crlf" and "ident" can be queried, otherwise this
/**
* Apply filter list to a data buffer.
*
- * See `git2/buffer.h` for background on `git_buf` objects.
- *
- * If the `in` buffer holds data allocated by libgit2 (i.e. `in->asize` is
- * not zero), then it will be overwritten when applying the filters. If
- * not, then it will be left untouched.
- *
- * If there are no filters to apply (or `filters` is NULL), then the `out`
- * buffer will reference the `in` buffer data (with `asize` set to zero)
- * instead of allocating data. This keeps allocations to a minimum, but
- * it means you have to be careful about freeing the `in` data since `out`
- * may be pointing to it!
- *
* @param out Buffer to store the result of the filtering
* @param filters A loaded git_filter_list (or NULL)
* @param in Buffer containing the data to filter
+ * @param in_len The length of the input buffer
* @return 0 on success, an error code otherwise
*/
-GIT_EXTERN(int) git_filter_list_apply_to_data(
+GIT_EXTERN(int) git_filter_list_apply_to_buffer(
git_buf *out,
git_filter_list *filters,
- git_buf *in);
+ const char *in,
+ size_t in_len);
/**
* Apply a filter list to the contents of a file on disk
* Apply a filter list to an arbitrary buffer as a stream
*
* @param filters the list of filters to apply
- * @param data the buffer to filter
+ * @param buffer the buffer to filter
+ * @param len the size of the buffer
* @param target the stream into which the data will be written
*/
-GIT_EXTERN(int) git_filter_list_stream_data(
+GIT_EXTERN(int) git_filter_list_stream_buffer(
git_filter_list *filters,
- git_buf *data,
+ const char *buffer,
+ size_t len,
git_writestream *target);
/**
* Note that a commit is not considered a descendant of itself, in contrast
* to `git merge-base --is-ancestor`.
*
- * @param commit a previously loaded commit.
- * @param ancestor a potential ancestor commit.
+ * @param repo the repository where the commits exist
+ * @param commit a previously loaded commit
+ * @param ancestor a potential ancestor commit
* @return 1 if the given commit is a descendant of the potential ancestor,
* 0 if not, error code otherwise.
*/
const git_oid *commit,
const git_oid *ancestor);
+/**
+ * Determine if a commit is reachable from any of a list of commits by
+ * following parent edges.
+ *
+ * @param repo the repository where the commits exist
+ * @param commit a previously loaded commit
+ * @param length the number of commits in the provided `descendant_array`
+ * @param descendant_array oids of the commits
+ * @return 1 if the given commit is an ancestor of any of the given potential
+ * descendants, 0 if not, error code otherwise.
+ */
+GIT_EXTERN(int) git_graph_reachable_from_any(
+ git_repository *repo,
+ const git_oid *commit,
+ const git_oid descendant_array[],
+ size_t length);
+
/** @} */
GIT_END_DECL
#endif
*
* The index must not contain any file in conflict.
*
- * @param out Pointer where to store OID of the the written tree
+ * @param out Pointer where to store OID of the written tree
* @param index Index to write
* @param repo Repository where to write the tree
* @return 0 on success, GIT_EUNMERGED when the index is not clean
* @param at_pos the address to which the position of the index entry is written (optional)
* @param index an existing index object
* @param path path to search
- * @return a zero-based position in the index if found; GIT_ENOTFOUND otherwise
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path);
* @param at_pos the address to which the position of the index entry is written (optional)
* @param index an existing index object
* @param prefix the prefix to search for
- * @return 0 with valid value in at_pos; an error code otherwise
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix);
* Type for progress callbacks during indexing. Return a value less
* than zero to cancel the indexing or download.
*
- * @param stats Structure containing information about the state of the tran sfer
+ * @param stats Structure containing information about the state of the transfer
* @param payload Payload provided by caller
*/
typedef int GIT_CALLBACK(git_indexer_progress_cb)(const git_indexer_progress *stats, void *payload);
/** progress_cb function to call with progress information */
git_indexer_progress_cb progress_cb;
+
/** progress_cb_payload payload for the progress callback */
void *progress_cb_payload;
* (negative value)
*/
GIT_EXTERN(int) git_note_next(
- git_oid* note_id,
- git_oid* annotated_id,
+ git_oid *note_id,
+ git_oid *annotated_id,
git_note_iterator *it);
*
* @param odb database to add the backend to
* @param path path to the objects folder for the alternate
- * @return 0 on success; error code otherwise
+ * @return 0 on success, error code otherwise
*/
GIT_EXTERN(int) git_odb_add_disk_alternate(git_odb *odb, const char *path);
* @param out pointer where to store the read object
* @param db database to search for the object in.
* @param id identity of the object to read.
- * @return
- * - 0 if the object was read;
- * - GIT_ENOTFOUND if the object is not in the database.
+ * @return 0 if the object was read, GIT_ENOTFOUND if the object is
+ * not in the database.
*/
GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id);
* @param db database to search for the object in.
* @param short_id a prefix of the id of the object to read.
* @param len the length of the prefix
- * @return
- * - 0 if the object was read;
- * - GIT_ENOTFOUND if the object is not in the database.
- * - GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
+ * @return 0 if the object was read, GIT_ENOTFOUND if the object is not in the
+ * database. GIT_EAMBIGUOUS if the prefix is ambiguous
+ * (several objects match the prefix)
*/
GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len);
* @param type_out pointer where to store the type
* @param db database to search for the object in.
* @param id identity of the object to read.
- * @return
- * - 0 if the object was read;
- * - GIT_ENOTFOUND if the object is not in the database.
+ * @return 0 if the object was read, GIT_ENOTFOUND if the object is not
+ * in the database.
*/
GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_object_t *type_out, git_odb *db, const git_oid *id);
*
* @param db database to be searched for the given object.
* @param id the object to search for.
- * @return
- * - 1, if the object was found
- * - 0, otherwise
+ * @return 1 if the object was found, 0 otherwise
*/
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
* @param stream the stream
* @param buffer the data to write
* @param len the buffer's length
- * @return 0 if the write succeeded; error code otherwise
+ * @return 0 if the write succeeded, error code otherwise
*/
GIT_EXTERN(int) git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len);
*
* @param out pointer to store the resulting object's id
* @param stream the stream
- * @return 0 on success; an error code otherwise
+ * @return 0 on success, an error code otherwise
*/
GIT_EXTERN(int) git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream);
* @param type pointer where to store the type of the object
* @param db object database where the stream will read from
* @param oid oid of the object the stream will read from
- * @return 0 if the stream was created; error code otherwise
+ * @return 0 if the stream was created, error code otherwise
*/
GIT_EXTERN(int) git_odb_open_rstream(
git_odb_stream **out,
git_indexer_progress_cb progress_cb,
void *progress_payload);
+/**
+ * Write a `multi-pack-index` file from all the `.pack` files in the ODB.
+ *
+ * If the ODB layer understands pack files, then this will create a file called
+ * `multi-pack-index` next to the `.pack` and `.idx` files, which will contain
+ * an index of all objects stored in `.pack` files. This will allow for
+ * O(log n) lookup for n objects (regardless of how many packfiles there
+ * exist).
+ *
+ * @param db object database where the `multi-pack-index` file will be written.
+ */
+GIT_EXTERN(int) git_odb_write_multi_pack_index(
+ git_odb *db);
+
/**
* Determine the object-ID (sha1 hash) of a data buffer
*
* @param odb database to add the backend to
* @param backend pointer to a git_odb_backend instance
* @param priority Value for ordering the backends queue
- * @return 0 on success; error code otherwise
+ * @return 0 on success, error code otherwise
*/
GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
* @param odb database to add the backend to
* @param backend pointer to a git_odb_backend instance
* @param priority Value for ordering the backends queue
- * @return 0 on success; error code otherwise
+ * @return 0 on success, error code otherwise
*/
GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
* @param out output pointer to ODB backend at pos
* @param odb object database
* @param pos index into object database backend list
- * @return 0 on success; GIT_ENOTFOUND if pos is invalid; other errors < 0
+ * @return 0 on success, GIT_ENOTFOUND if pos is invalid, other errors < 0
*/
GIT_EXTERN(int) git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos);
+/**
+ * Set the git commit-graph for the ODB.
+ *
+ * After a successfull call, the ownership of the cgraph parameter will be
+ * transferred to libgit2, and the caller should not free it.
+ *
+ * The commit-graph can also be unset by explicitly passing NULL as the cgraph
+ * parameter.
+ *
+ * @param odb object database
+ * @param cgraph the git commit-graph
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_odb_set_commit_graph(git_odb *odb, git_commit_graph *cgraph);
+
/** @} */
GIT_END_DECL
#endif
} git_oidarray;
/**
- * Free the OID array
- *
- * This method must (and must only) be called on `git_oidarray`
- * objects where the array is allocated by the library. Not doing so,
- * will result in a memory leak.
+ * Free the object IDs contained in an oid_array. This method should
+ * be called on `git_oidarray` objects that were provided by the
+ * library. Not doing so will result in a memory leak.
*
* This does not free the `git_oidarray` itself, since the library will
- * never allocate that object directly itself (it is more commonly embedded
- * inside another struct or created on the stack).
+ * never allocate that object directly itself.
*
* @param array git_oidarray from which to free oid data
*/
-GIT_EXTERN(void) git_oidarray_free(git_oidarray *array);
+GIT_EXTERN(void) git_oidarray_dispose(git_oidarray *array);
/** @} */
GIT_END_DECL
*/
typedef struct git_patch git_patch;
+/**
+ * Get the repository associated with this patch. May be NULL.
+ *
+ * @param patch the patch
+ * @return a pointer to the repository
+ */
+GIT_EXTERN(git_repository *) git_patch_owner(const git_patch *patch);
+
/**
* Return a patch for an entry in the diff list.
*
*/
git_checkout_options checkout_options;
+ /**
+ * Optional callback that allows users to override commit
+ * creation in `git_rebase_commit`. If specified, users can
+ * create their own commit and provide the commit ID, which
+ * may be useful for signing commits or otherwise customizing
+ * the commit creation.
+ *
+ * If this callback returns `GIT_PASSTHROUGH`, then
+ * `git_rebase_commit` will continue to create the commit.
+ */
+ git_commit_create_cb commit_create_cb;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
/**
* If provided, this will be called with the commit content, allowing
* a signature to be added to the rebase commit. Can be skipped with
* GIT_PASSTHROUGH. If GIT_PASSTHROUGH is returned, a commit will be made
* without a signature.
+ *
* This field is only used when performing git_rebase_commit.
+ *
+ * This callback is not invoked if a `git_commit_create_cb` is
+ * specified.
+ *
+ * This callback is deprecated; users should provide a
+ * creation callback as `commit_create_cb` that produces a
+ * commit buffer, signs it, and commits it.
*/
- git_commit_signing_cb signing_cb;
+ int (*signing_cb)(git_buf *, git_buf *, const char *, void *);
+#endif
/**
* This will be passed to each of the callbacks in this struct
* of updating does not match the one passed through `current_value`
* (i.e. if the ref has changed since the user read it).
*
+ * If `current_value` is all zeros, this function will return GIT_EMODIFIED
+ * if the ref already exists.
+ *
* @param out Pointer to the newly created reference
* @param repo Repository where that reference will live
* @param name The name of the reference
*
* The message for the reflog will be ignored if the reference does
* not belong in the standard set (HEAD, branches and remote-tracking
- * branches) and and it does not have a reflog.
+ * branches) and it does not have a reflog.
*
* @param out Pointer to the newly created reference
* @param repo Repository where that reference will live
*
* The message for the reflog will be ignored if the reference does
* not belong in the standard set (HEAD, branches and remote-tracking
- * branches) and and it does not have a reflog.
+ * branches) and it does not have a reflog.
*
* It will return GIT_EMODIFIED if the reference's value at the time
* of updating does not match the one passed through `current_id`
*
* The message for the reflog will be ignored if the reference does
* not belong in the standard set (HEAD, branches and remote-tracking
- * branches) and and it does not have a reflog.
+ * branches) and it does not have a reflog.
*
* @param out Pointer to the newly created reference
* @param ref The reference
* the characters '~', '^', ':', '\\', '?', '[', and '*', and the
* sequences ".." and "@{" which have special meaning to revparse.
*
+ * @param valid output pointer to set with validity of given reference name
* @param refname name to be checked.
- * @return 1 if the reference name is acceptable; 0 if it isn't
+ * @return 0 on success or an error code
*/
-GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
+GIT_EXTERN(int) git_reference_name_is_valid(int *valid, const char *refname);
/**
* Get the reference's short name
* Get the remote's url
*
* If url.*.insteadOf has been configured for this URL, it will
- * return the modified URL.
+ * return the modified URL. If `git_remote_set_instance_pushurl`
+ * has been called for this remote, then that URL will be returned.
*
* @param remote the remote
* @return a pointer to the url
GIT_EXTERN(const char *) git_remote_url(const git_remote *remote);
/**
- * Get the remote's url for pushing
+ * Get the remote's url for pushing.
*
* If url.*.pushInsteadOf has been configured for this URL, it
- * will return the modified URL.
+ * will return the modified URL. If `git_remote_set_instance_pushurl`
+ * has been called for this remote, then that URL will be returned.
*
* @param remote the remote
* @return a pointer to the url or NULL if no special url for pushing is set
* @param url the url to set
* @return 0 or an error value
*/
-GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, const char* url);
+GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, const char *url);
/**
* Set the remote's url for pushing in the configuration.
* @param repo the repository in which to perform the change
* @param remote the remote's name
* @param url the url to set
+ * @return 0, or an error code
*/
-GIT_EXTERN(int) git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url);
+GIT_EXTERN(int) git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url);
+
+/**
+ * Set the url for this particular url instance. The URL in the
+ * configuration will be ignored, and will not be changed.
+ *
+ * @param remote the remote's name
+ * @param url the url to set
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_instance_url(git_remote *remote, const char *url);
+
+/**
+ * Set the push url for this particular url instance. The URL in the
+ * configuration will be ignored, and will not be changed.
+ *
+ * @param remote the remote's name
+ * @param url the url to set
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_instance_pushurl(git_remote *remote, const char *url);
/**
* Add a fetch refspec to the remote's configuration
unsigned int current,
unsigned int total,
size_t bytes,
- void* payload);
+ void *payload);
/**
* Represents an update which will be performed on the remote during push
*/
typedef int GIT_CALLBACK(git_push_update_reference_cb)(const char *refname, const char *status, void *data);
+#ifndef GIT_DEPRECATE_HARD
/**
* Callback to resolve URLs before connecting to remote
*
* @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
* @param payload Payload provided by the caller
* @return 0 on success, GIT_PASSTHROUGH or an error
+ * @deprecated Use `git_remote_set_instance_url`
*/
typedef int GIT_CALLBACK(git_url_resolve_cb)(git_buf *url_resolved, const char *url, int direction, void *payload);
+#endif
+
+/**
+ * Callback invoked immediately before we attempt to connect to the
+ * given url. Callers may change the URL before the connection by
+ * calling `git_remote_set_instance_url` in the callback.
+ *
+ * @param remote The remote to be connected
+ * @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
+ * @param payload Payload provided by the caller
+ * @return 0 on success, or an error
+ */
+typedef int GIT_CALLBACK(git_remote_ready_cb)(git_remote *remote, int direction, void *payload);
/**
* The callback settings structure
*/
git_transport_cb transport;
+ /**
+ * Callback when the remote is ready to connect.
+ */
+ git_remote_ready_cb remote_ready;
+
/**
* This will be passed to each of the callbacks in this struct
* as the last parameter.
*/
void *payload;
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
/**
* Resolve URL before connecting to remote.
* The returned URL will be used to connect to the remote instead.
+ *
+ * This callback is deprecated; users should use
+ * git_remote_ready_cb and configure the instance URL instead.
*/
git_url_resolve_cb resolve_url;
+#endif
};
#define GIT_REMOTE_CALLBACKS_VERSION 1
* @param repo the repository in which to make the change
* @param remote the name of the remote
* @param value the new value to take.
+ * @return 0, or an error code.
*/
GIT_EXTERN(int) git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value);
+
/**
* Retrieve the ref-prune setting
*
/**
* Ensure the remote name is well-formed.
*
+ * @param valid output pointer to set with validity of given remote name
* @param remote_name name to be checked.
- * @return 1 if the reference name is acceptable; 0 if it isn't
+ * @return 0 on success or an error code
*/
-GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
+GIT_EXTERN(int) git_remote_name_is_valid(int *valid, const char *remote_name);
/**
* Delete an existing persisted remote.
*
* This function must only be called after connecting.
*
- * @param out the buffern in which to store the reference name
+ * @param out the buffer in which to store the reference name
* @param remote the remote
* @return 0, GIT_ENOTFOUND if the remote does not have any references
* or none of them point to HEAD's commit, or an error message.
*
* These flags configure extra behaviors to `git_repository_init_ext`.
* In every case, the default behavior is the zero value (i.e. flag is
- * not set). Just OR the flag values together for the `flags` parameter
- * when initializing a new repo. Details of individual values are:
- *
- * * BARE - Create a bare repository with no working directory.
- * * NO_REINIT - Return an GIT_EEXISTS error if the repo_path appears to
- * already be an git repository.
- * * NO_DOTGIT_DIR - Normally a "/.git/" will be appended to the repo
- * path for non-bare repos (if it is not already there), but
- * passing this flag prevents that behavior.
- * * MKDIR - Make the repo_path (and workdir_path) as needed. Init is
- * always willing to create the ".git" directory even without this
- * flag. This flag tells init to create the trailing component of
- * the repo and workdir paths as needed.
- * * MKPATH - Recursively make all components of the repo and workdir
- * paths as necessary.
- * * EXTERNAL_TEMPLATE - libgit2 normally uses internal templates to
- * initialize a new repo. This flags enables external templates,
- * looking the "template_path" from the options if set, or the
- * `init.templatedir` global config if not, or falling back on
- * "/usr/share/git-core/templates" if it exists.
- * * GIT_REPOSITORY_INIT_RELATIVE_GITLINK - If an alternate workdir is
- * specified, use relative paths for the gitdir and core.worktree.
+ * not set). Just OR the flag values together for the `flags` parameter
+ * when initializing a new repo.
*/
typedef enum {
+ /**
+ * Create a bare repository with no working directory.
+ */
GIT_REPOSITORY_INIT_BARE = (1u << 0),
+
+ /**
+ * Return an GIT_EEXISTS error if the repo_path appears to already be
+ * an git repository.
+ */
GIT_REPOSITORY_INIT_NO_REINIT = (1u << 1),
+
+ /**
+ * Normally a "/.git/" will be appended to the repo path for
+ * non-bare repos (if it is not already there), but passing this flag
+ * prevents that behavior.
+ */
GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = (1u << 2),
+
+ /**
+ * Make the repo_path (and workdir_path) as needed. Init is always willing
+ * to create the ".git" directory even without this flag. This flag tells
+ * init to create the trailing component of the repo and workdir paths
+ * as needed.
+ */
GIT_REPOSITORY_INIT_MKDIR = (1u << 3),
+
+ /**
+ * Recursively make all components of the repo and workdir paths as
+ * necessary.
+ */
GIT_REPOSITORY_INIT_MKPATH = (1u << 4),
+
+ /**
+ * libgit2 normally uses internal templates to initialize a new repo.
+ * This flags enables external templates, looking the "template_path" from
+ * the options if set, or the `init.templatedir` global config if not,
+ * or falling back on "/usr/share/git-core/templates" if it exists.
+ */
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
+
+ /**
+ * If an alternate workdir is specified, use relative paths for the gitdir
+ * and core.worktree.
+ */
GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6),
} git_repository_init_flag_t;
*
* Set the mode field of the `git_repository_init_options` structure
* either to the custom mode that you would like, or to one of the
- * following modes:
- *
- * * SHARED_UMASK - Use permissions configured by umask - the default.
- * * SHARED_GROUP - Use "--shared=group" behavior, chmod'ing the new repo
- * to be group writable and "g+sx" for sticky group assignment.
- * * SHARED_ALL - Use "--shared=all" behavior, adding world readability.
- * * Anything else - Set to custom value.
+ * defined modes.
*/
typedef enum {
+ /**
+ * Use permissions configured by umask - the default.
+ */
GIT_REPOSITORY_INIT_SHARED_UMASK = 0,
+
+ /**
+ * Use "--shared=group" behavior, chmod'ing the new repo to be group
+ * writable and "g+sx" for sticky group assignment.
+ */
GIT_REPOSITORY_INIT_SHARED_GROUP = 0002775,
+
+ /**
+ * Use "--shared=all" behavior, adding world readability.
+ */
GIT_REPOSITORY_INIT_SHARED_ALL = 0002777,
} git_repository_init_mode_t;
* Extended options structure for `git_repository_init_ext`.
*
* This contains extra options for `git_repository_init_ext` that enable
- * additional initialization features. The fields are:
- *
- * * flags - Combination of GIT_REPOSITORY_INIT flags above.
- * * mode - Set to one of the standard GIT_REPOSITORY_INIT_SHARED_...
- * constants above, or to a custom value that you would like.
- * * workdir_path - The path to the working dir or NULL for default (i.e.
- * repo_path parent on non-bare repos). IF THIS IS RELATIVE PATH,
- * IT WILL BE EVALUATED RELATIVE TO THE REPO_PATH. If this is not
- * the "natural" working directory, a .git gitlink file will be
- * created here linking to the repo_path.
- * * description - If set, this will be used to initialize the "description"
- * file in the repository, instead of using the template content.
- * * template_path - When GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE is set,
- * this contains the path to use for the template directory. If
- * this is NULL, the config or default directory options will be
- * used instead.
- * * initial_head - The name of the head to point HEAD at. If NULL, then
- * this will be treated as "master" and the HEAD ref will be set
- * to "refs/heads/master". If this begins with "refs/" it will be
- * used verbatim; otherwise "refs/heads/" will be prefixed.
- * * origin_url - If this is non-NULL, then after the rest of the
- * repository initialization is completed, an "origin" remote
- * will be added pointing to this URL.
+ * additional initialization features.
*/
typedef struct {
unsigned int version;
+
+ /**
+ * Combination of GIT_REPOSITORY_INIT flags above.
+ */
uint32_t flags;
+
+ /**
+ * Set to one of the standard GIT_REPOSITORY_INIT_SHARED_... constants
+ * above, or to a custom value that you would like.
+ */
uint32_t mode;
+
+ /**
+ * The path to the working dir or NULL for default (i.e. repo_path parent
+ * on non-bare repos). IF THIS IS RELATIVE PATH, IT WILL BE EVALUATED
+ * RELATIVE TO THE REPO_PATH. If this is not the "natural" working
+ * directory, a .git gitlink file will be created here linking to the
+ * repo_path.
+ */
const char *workdir_path;
+
+ /**
+ * If set, this will be used to initialize the "description" file in the
+ * repository, instead of using the template content.
+ */
const char *description;
+
+ /**
+ * When GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE is set, this contains
+ * the path to use for the template directory. If this is NULL, the config
+ * or default directory options will be used instead.
+ */
const char *template_path;
+
+ /**
+ * The name of the head to point HEAD at. If NULL, then this will be
+ * treated as "master" and the HEAD ref will be set to "refs/heads/master".
+ * If this begins with "refs/" it will be used verbatim;
+ * otherwise "refs/heads/" will be prefixed.
+ */
const char *initial_head;
+
+ /**
+ * If this is non-NULL, then after the rest of the repository
+ * initialization is completed, an "origin" remote will be added
+ * pointing to this URL.
+ */
const char *origin_url;
} git_repository_init_options;
*
* @param out Output value of calculated SHA
* @param repo Repository pointer
- * @param path Path to file on disk whose contents should be hashed. If the
- * repository is not NULL, this can be a relative path.
+ * @param path Path to file on disk whose contents should be hashed. This
+ * may be an absolute path or a relative path, in which case it
+ * will be treated as a path within the working directory.
* @param type The object type to hash as (e.g. GIT_OBJECT_BLOB)
* @param as_path The path to use to look up filtering rules. If this is
- * NULL, then the `path` parameter will be used instead. If
- * this is passed as the empty string, then no filters will be
- * applied when calculating the hash.
+ * an empty string then no filters will be applied when
+ * calculating the hash. If this is `NULL` and the `path`
+ * parameter is a file within the repository's working
+ * directory, then the `path` will be used.
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_repository_hashfile(
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_repository_set_head(
- git_repository* repo,
- const char* refname);
+ git_repository *repo,
+ const char *refname);
/**
* Make the repository HEAD directly point to the Commit.
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_repository_set_head_detached(
- git_repository* repo,
- const git_oid* commitish);
+ git_repository *repo,
+ const git_oid *commitish);
/**
* Make the repository HEAD directly point to the Commit.
* branch or an error code
*/
GIT_EXTERN(int) git_repository_detach_head(
- git_repository* repo);
+ git_repository *repo);
/**
* Repository state
*/
typedef enum {
/** The spec targeted a single object. */
- GIT_REVPARSE_SINGLE = 1 << 0,
+ GIT_REVSPEC_SINGLE = 1 << 0,
/** The spec targeted a range of commits. */
- GIT_REVPARSE_RANGE = 1 << 1,
+ GIT_REVSPEC_RANGE = 1 << 1,
/** The spec used the '...' operator, which invokes special semantics. */
- GIT_REVPARSE_MERGE_BASE = 1 << 2,
-} git_revparse_mode_t;
+ GIT_REVSPEC_MERGE_BASE = 1 << 2,
+} git_revspec_t;
/**
* Git Revision Spec: output of a `git_revparse` operation
git_object *from;
/** The right element of the revspec; must be freed by the user */
git_object *to;
- /** The intent of the revspec (i.e. `git_revparse_mode_t` flags) */
+ /** The intent of the revspec (i.e. `git_revspec_mode_t` flags) */
unsigned int flags;
} git_revspec;
*/
typedef int GIT_CALLBACK(git_stash_cb)(
size_t index,
- const char* message,
+ const char *message,
const git_oid *stash_id,
void *payload);
* With `git_status_foreach_ext`, this will control which changes get
* callbacks. With `git_status_list_new`, these will control which
* changes are included in the list.
- *
- * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly
- * matches `git status --porcelain` regarding which files are
- * included and in what order.
- * - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
- * comparison, not looking at working directory changes.
- * - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
- * working directory comparison, not comparing the index to the HEAD.
*/
typedef enum {
+ /**
+ * The default. This roughly matches `git status --porcelain` regarding
+ * which files are included and in what order.
+ */
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
+
+ /**
+ * Only gives status based on HEAD to index comparison, not looking at
+ * working directory changes.
+ */
GIT_STATUS_SHOW_INDEX_ONLY = 1,
+
+ /**
+ * Only gives status based on index to working directory comparison,
+ * not comparing the index to the HEAD.
+ */
GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
} git_status_show_t;
/**
* Flags to control status callbacks
*
- * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
- * on untracked files. These will only be made if the workdir files are
- * included in the status "show" option.
- * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks.
- * Again, these callbacks will only be made if the workdir files are
- * included in the status "show" option.
- * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
- * made even on unmodified files.
- * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be
- * skipped. This only applies if there are no pending typechanges to
- * the submodule (either from or to another type).
- * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in
- * untracked directories should be included. Normally if an entire
- * directory is new, then just the top-level directory is included (with
- * a trailing slash on the entry name). This flag says to include all
- * of the individual files in the directory instead.
- * - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
- * should be treated as a literal path, and not as a pathspec pattern.
- * - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
- * ignored directories should be included in the status. This is like
- * doing `git ls-files -o -i --exclude-standard` with core git.
- * - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
- * should be processed between the head and the index and enables
- * the GIT_STATUS_INDEX_RENAMED as a possible status flag.
- * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that rename
- * detection should be run between the index and the working directory
- * and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
- * - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
- * sensitivity for the file system and forces the output to be in
- * case-sensitive order
- * - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
- * sensitivity for the file system and forces the output to be in
- * case-insensitive order
- * - GIT_STATUS_OPT_RENAMES_FROM_REWRITES indicates that rename detection
- * should include rewritten files
- * - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
- * doing a "soft" index reload (i.e. reloading the index data if the
- * file on disk has been modified outside libgit2).
- * - GIT_STATUS_OPT_UPDATE_INDEX tells libgit2 to refresh the stat cache
- * in the index for files that are unchanged but have out of date stat
- * information in the index. It will result in less work being done on
- * subsequent calls to get status. This is mutually exclusive with the
- * NO_REFRESH option.
- *
* Calling `git_status_foreach()` is like calling the extended version
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
* and GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS. Those options are bundled
* together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
*/
typedef enum {
+ /**
+ * Says that callbacks should be made on untracked files.
+ * These will only be made if the workdir files are included in the status
+ * "show" option.
+ */
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
+
+ /**
+ * Says that ignored files get callbacks.
+ * Again, these callbacks will only be made if the workdir files are
+ * included in the status "show" option.
+ */
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
+
+ /**
+ * Indicates that callback should be made even on unmodified files.
+ */
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
+
+ /**
+ * Indicates that submodules should be skipped.
+ * This only applies if there are no pending typechanges to the submodule
+ * (either from or to another type).
+ */
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
+
+ /**
+ * Indicates that all files in untracked directories should be included.
+ * Normally if an entire directory is new, then just the top-level
+ * directory is included (with a trailing slash on the entry name).
+ * This flag says to include all of the individual files in the directory
+ * instead.
+ */
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
+
+ /**
+ * Indicates that the given path should be treated as a literal path,
+ * and not as a pathspec pattern.
+ */
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
+
+ /**
+ * Indicates that the contents of ignored directories should be included
+ * in the status. This is like doing `git ls-files -o -i --exclude-standard`
+ * with core git.
+ */
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
+
+ /**
+ * Indicates that rename detection should be processed between the head and
+ * the index and enables the GIT_STATUS_INDEX_RENAMED as a possible status
+ * flag.
+ */
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
+
+ /**
+ * Indicates that rename detection should be run between the index and the
+ * working directory and enabled GIT_STATUS_WT_RENAMED as a possible status
+ * flag.
+ */
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
+
+ /**
+ * Overrides the native case sensitivity for the file system and forces
+ * the output to be in case-sensitive order.
+ */
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
+
+ /**
+ * Overrides the native case sensitivity for the file system and forces
+ * the output to be in case-insensitive order.
+ */
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
+
+ /**
+ * Iindicates that rename detection should include rewritten files.
+ */
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
+
+ /**
+ * Bypasses the default status behavior of doing a "soft" index reload
+ * (i.e. reloading the index data if the file on disk has been modified
+ * outside libgit2).
+ */
GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
+
+ /**
+ * Tells libgit2 to refresh the stat cache in the index for files that are
+ * unchanged but have out of date stat einformation in the index.
+ * It will result in less work being done on subsequent calls to get status.
+ * This is mutually exclusive with the NO_REFRESH option.
+ */
GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
+
+ /**
+ * Normally files that cannot be opened or read are ignored as
+ * these are often transient files; this option will return
+ * unreadable files as `GIT_STATUS_WT_UNREADABLE`.
+ */
GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1u << 14),
+
+ /**
+ * Unreadable files will be detected and given the status
+ * untracked instead of unreadable.
+ */
GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 15),
} git_status_opt_t;
*
*/
typedef struct {
- unsigned int version; /**< The version */
+ /**
+ * The struct version; pass `GIT_STATUS_OPTIONS_VERSION`.
+ */
+ unsigned int version;
/**
* The `show` value is one of the `git_status_show_t` constants that
git_status_show_t show;
/**
- * The `flags` value is an OR'ed combination of the `git_status_opt_t`
- * values above.
+ * The `flags` value is an OR'ed combination of the
+ * `git_status_opt_t` values above.
*/
unsigned int flags;
/**
* The `pathspec` is an array of path patterns to match (using
- * fnmatch-style matching), or just an array of paths to match exactly if
- * `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified in the flags.
+ * fnmatch-style matching), or just an array of paths to match
+ * exactly if `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified
+ * in the flags.
*/
git_strarray pathspec;
/**
- * The `baseline` is the tree to be used for comparison to the working directory
- * and index; defaults to HEAD.
+ * The `baseline` is the tree to be used for comparison to the
+ * working directory and index; defaults to HEAD.
*/
git_tree *baseline;
} git_status_options;
//
///////////////////////////////////////////////////////////////////////////////
-#ifndef _MSC_VER // [
-#error "Use this header only with Microsoft Visual C++ compilers!"
-#endif // _MSC_VER ]
+#ifdef _MSC_VER // [
#ifndef _MSC_STDINT_H_ // [
#define _MSC_STDINT_H_
#endif // _MSC_STDINT_H_ ]
+
+#endif // _MSC_VER ]
\ No newline at end of file
git_repository *repo,
const char *name);
+/**
+ * Create an in-memory copy of a submodule. The copy must be explicitly
+ * free'd or it will leak.
+ *
+ * @param out Pointer to store the copy of the submodule.
+ * @param source Original submodule to copy.
+ */
+GIT_EXTERN(int) git_submodule_dup(git_submodule **out, git_submodule *source);
+
/**
* Release a submodule
*
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_commit_graph_h__
+#define INCLUDE_sys_git_commit_graph_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/sys/commit_graph.h
+ * @brief Git commit-graph
+ * @defgroup git_commit_graph Git commit-graph APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Opens a `git_commit_graph` from a path to an objects directory.
+ *
+ * This finds, opens, and validates the `commit-graph` file.
+ *
+ * @param cgraph_out the `git_commit_graph` struct to initialize.
+ * @param objects_dir the path to a git objects directory.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir);
+
+/**
+ * Frees commit-graph data. This should only be called when memory allocated
+ * using `git_commit_graph_open` is not returned to libgit2 because it was not
+ * associated with the ODB through a successful call to
+ * `git_odb_set_commit_graph`.
+ *
+ * @param cgraph the commit-graph object to free. If NULL, no action is taken.
+ */
+GIT_EXTERN(void) git_commit_graph_free(git_commit_graph *cgraph);
+
+/**
+ * Create a new writer for `commit-graph` files.
+ *
+ * @param out Location to store the writer pointer.
+ * @param objects_info_dir The `objects/info` directory.
+ * The `commit-graph` file will be written in this directory.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_new(
+ git_commit_graph_writer **out,
+ const char *objects_info_dir);
+
+/**
+ * Free the commit-graph writer and its resources.
+ *
+ * @param w The writer to free. If NULL no action is taken.
+ */
+GIT_EXTERN(void) git_commit_graph_writer_free(git_commit_graph_writer *w);
+
+/**
+ * Add an `.idx` file (associated to a packfile) to the writer.
+ *
+ * @param w The writer.
+ * @param repo The repository that owns the `.idx` file.
+ * @param idx_path The path of an `.idx` file.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_add_index_file(
+ git_commit_graph_writer *w,
+ git_repository *repo,
+ const char *idx_path);
+
+/**
+ * Add a revwalk to the writer. This will add all the commits from the revwalk
+ * to the commit-graph.
+ *
+ * @param w The writer.
+ * @param walk The git_revwalk.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_add_revwalk(
+ git_commit_graph_writer *w,
+ git_revwalk *walk);
+
+
+/**
+ * The strategy to use when adding a new set of commits to a pre-existing
+ * commit-graph chain.
+ */
+typedef enum {
+ /**
+ * Do not split commit-graph files. The other split strategy-related option
+ * fields are ignored.
+ */
+ GIT_COMMIT_GRAPH_SPLIT_STRATEGY_SINGLE_FILE = 0,
+} git_commit_graph_split_strategy_t;
+
+/**
+ * Options structure for
+ * `git_commit_graph_writer_commit`/`git_commit_graph_writer_dump`.
+ *
+ * Initialize with `GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT`. Alternatively, you
+ * can use `git_commit_graph_writer_options_init`.
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * The strategy to use when adding new commits to a pre-existing commit-graph
+ * chain.
+ */
+ git_commit_graph_split_strategy_t split_strategy;
+
+ /**
+ * The number of commits in level N is less than X times the number of
+ * commits in level N + 1. Default is 2.
+ */
+ float size_multiple;
+
+ /**
+ * The number of commits in level N + 1 is more than C commits.
+ * Default is 64000.
+ */
+ size_t max_commits;
+} git_commit_graph_writer_options;
+
+#define GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION 1
+#define GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT { \
+ GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION \
+ }
+
+/**
+ * Initialize git_commit_graph_writer_options structure
+ *
+ * Initializes a `git_commit_graph_writer_options` with default values. Equivalent to
+ * creating an instance with `GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT`.
+ *
+ * @param opts The `git_commit_graph_writer_options` struct to initialize.
+ * @param version The struct version; pass `GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_commit_graph_writer_options_init(
+ git_commit_graph_writer_options *opts,
+ unsigned int version);
+
+/**
+ * Write a `commit-graph` file to a file.
+ *
+ * @param w The writer
+ * @param opts Pointer to git_commit_graph_writer_options struct.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_commit(
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts);
+
+/**
+ * Dump the contents of the `commit-graph` to an in-memory buffer.
+ *
+ * @param buffer Buffer where to store the contents of the `commit-graph`.
+ * @param w The writer.
+ * @param opts Pointer to git_commit_graph_writer_options struct.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_dump(
+ git_buf *buffer,
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_email_h__
+#define INCLUDE_sys_git_email_h__
+
+/**
+ * @file git2/sys/email.h
+ * @brief Advanced git email creation routines
+ * @defgroup git_email Advanced git email creation routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a diff for a commit in mbox format for sending via email.
+ *
+ * @param out buffer to store the e-mail patch in
+ * @param diff the changes to include in the email
+ * @param patch_idx the patch index
+ * @param patch_count the total number of patches that will be included
+ * @param commit_id the commit id for this change
+ * @param summary the commit message for this change
+ * @param body optional text to include above the diffstat
+ * @param author the person who authored this commit
+ * @param opts email creation options
+ */
+GIT_EXTERN(int) git_email_create_from_diff(
+ git_buf *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
*
* The `payload` will be a pointer to a reference payload for the filter.
* This will start as NULL, but `check` can assign to this pointer for
- * later use by the `apply` callback. Note that the value should be heap
- * allocated (not stack), so that it doesn't go away before the `apply`
+ * later use by the `stream` callback. Note that the value should be heap
+ * allocated (not stack), so that it doesn't go away before the `stream`
* callback can use it. If a filter allocates and assigns a value to the
* `payload`, it will need a `cleanup` callback to free the payload.
*/
typedef int GIT_CALLBACK(git_filter_check_fn)(
- git_filter *self,
- void **payload, /* points to NULL ptr on entry, may be set */
+ git_filter *self,
+ void **payload, /* NULL on entry, may be set */
const git_filter_source *src,
- const char **attr_values);
+ const char **attr_values);
+#ifndef GIT_DEPRECATE_HARD
/**
* Callback to actually perform the data filtering
*
*
* The `payload` value will refer to any payload that was set by the
* `check` callback. It may be read from or written to as needed.
+ *
+ * @deprecated use git_filter_stream_fn
*/
typedef int GIT_CALLBACK(git_filter_apply_fn)(
- git_filter *self,
- void **payload, /* may be read and/or set */
- git_buf *to,
- const git_buf *from,
+ git_filter *self,
+ void **payload, /* may be read and/or set */
+ git_buf *to,
+ const git_buf *from,
const git_filter_source *src);
+#endif
+/**
+ * Callback to perform the data filtering.
+ *
+ * Specified as `filter.stream`, this is a callback that filters data
+ * in a streaming manner. This function will provide a
+ * `git_writestream` that will the original data will be written to;
+ * with that data, the `git_writestream` will then perform the filter
+ * translation and stream the filtered data out to the `next` location.
+ */
typedef int GIT_CALLBACK(git_filter_stream_fn)(
- git_writestream **out,
- git_filter *self,
- void **payload,
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
const git_filter_source *src,
- git_writestream *next);
+ git_writestream *next);
/**
* Callback to clean up after filtering has been applied
*
* Specified as `filter.cleanup`, this is an optional callback invoked
- * after the filter has been applied. If the `check` or `apply` callbacks
- * allocated a `payload` to keep per-source filter state, use this
- * callback to free that payload and release resources as required.
+ * after the filter has been applied. If the `check`, `apply`, or
+ * `stream` callbacks allocated a `payload` to keep per-source filter
+ * state, use this callback to free that payload and release resources
+ * as required.
*/
typedef void GIT_CALLBACK(git_filter_cleanup_fn)(
- git_filter *self,
- void *payload);
+ git_filter *self,
+ void *payload);
/**
* Filter structure used to register custom filters.
/**
* Called to determine whether the filter should be invoked for a
* given file. If this function returns `GIT_PASSTHROUGH` then the
- * `apply` function will not be invoked and the contents will be passed
- * through unmodified.
+ * `stream` or `apply` functions will not be invoked and the
+ * contents will be passed through unmodified.
*/
git_filter_check_fn check;
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
/**
- * Called to actually apply the filter to file contents. If this
- * function returns `GIT_PASSTHROUGH` then the contents will be passed
- * through unmodified.
+ * Provided for backward compatibility; this will apply the
+ * filter to the given contents in a `git_buf`. Callers should
+ * provide a `stream` function instead.
*/
git_filter_apply_fn apply;
+#endif
/**
- * Called to apply the filter in a streaming manner. If this is not
- * specified then the system will call `apply` with the whole buffer.
+ * Called to apply the filter, this function will provide a
+ * `git_writestream` that will the original data will be
+ * written to; with that data, the `git_writestream` will then
+ * perform the filter translation and stream the filtered data
+ * out to the `next` location.
*/
git_filter_stream_fn stream;
* As mentioned elsewhere, the initialize callback will not be invoked
* immediately. It is deferred until the filter is used in some way.
*
- * A filter's attribute checks and `check` and `apply` callbacks will be
- * issued in order of `priority` on smudge (to workdir), and in reverse
- * order of `priority` on clean (to odb).
+ * A filter's attribute checks and `check` and `stream` (or `apply`)
+ * callbacks will be issued in order of `priority` on smudge (to
+ * workdir), and in reverse order of `priority` on clean (to odb).
*
* Two filters are preregistered with libgit2:
* - GIT_FILTER_CRLF with priority 0
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_midx_h__
+#define INCLUDE_sys_git_midx_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/midx.h
+ * @brief Git multi-pack-index routines
+ * @defgroup git_midx Git multi-pack-index routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new writer for `multi-pack-index` files.
+ *
+ * @param out location to store the writer pointer.
+ * @param pack_dir the directory where the `.pack` and `.idx` files are. The
+ * `multi-pack-index` file will be written in this directory, too.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_new(
+ git_midx_writer **out,
+ const char *pack_dir);
+
+/**
+ * Free the multi-pack-index writer and its resources.
+ *
+ * @param w the writer to free. If NULL no action is taken.
+ */
+GIT_EXTERN(void) git_midx_writer_free(git_midx_writer *w);
+
+/**
+ * Add an `.idx` file to the writer.
+ *
+ * @param w the writer
+ * @param idx_path the path of an `.idx` file.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_add(
+ git_midx_writer *w,
+ const char *idx_path);
+
+/**
+ * Write a `multi-pack-index` file to a file.
+ *
+ * @param w the writer
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_commit(
+ git_midx_writer *w);
+
+/**
+ * Dump the contents of the `multi-pack-index` to an in-memory buffer.
+ *
+ * @param midx Buffer where to store the contents of the `multi-pack-index`.
+ * @param w the writer
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_dump(
+ git_buf *midx,
+ git_midx_writer *w);
+
+/** @} */
+GIT_END_DECL
+#endif
git_odb_writepack **, git_odb_backend *, git_odb *odb,
git_indexer_progress_cb progress_cb, void *progress_payload);
+ /**
+ * If the backend supports pack files, this will create a
+ * `multi-pack-index` file which will contain an index of all objects
+ * across all the `.pack` files.
+ */
+ int GIT_CALLBACK(writemidx)(git_odb_backend *);
+
/**
* "Freshens" an already existing object, updating its last-used
* time. This occurs when `git_odb_write` was called, but the
#define INCLUDE_sys_git_transport_h
#include "git2/net.h"
+#include "git2/transport.h"
#include "git2/types.h"
#include "git2/strarray.h"
#include "git2/proxy.h"
*/
GIT_EXTERN(int) git_tag_dup(git_tag **out, git_tag *source);
+/**
+ * Determine whether a tag name is valid, meaning that (when prefixed
+ * with `refs/tags/`) that it is a valid reference name, and that any
+ * additional tag name restrictions are imposed (eg, it cannot start
+ * with a `-`).
+ *
+ * @param valid output pointer to set with validity of given tag name
+ * @param name a tag name to test
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_tag_name_is_valid(int *valid, const char *name);
+
/** @} */
GIT_END_DECL
#endif
GIT_BEGIN_DECL
/**
- * Callback for messages recieved by the transport.
+ * Callback for messages received by the transport.
*
* Return a negative value to cancel the network operation.
*
*
* @param bld Tree builder
* @param filename Filename of the entry to remove
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_treebuilder_remove(
git_treebuilder *bld, const char *filename);
GIT_EXTERN(int) git_treebuilder_write(
git_oid *id, git_treebuilder *bld);
-/**
- * Write the contents of the tree builder as a tree object
- * using a shared git_buf.
- *
- * @see git_treebuilder_write
- *
- * @param oid Pointer to store the OID of the newly written tree
- * @param bld Tree builder to write
- * @param tree Shared buffer for writing the tree. Will be grown as necessary.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_treebuilder_write_with_buffer(
- git_oid *oid, git_treebuilder *bld, git_buf *tree);
-
/** Callback for the tree traversal method */
typedef int GIT_CALLBACK(git_treewalk_cb)(
const char *root, const git_tree_entry *entry, void *payload);
* @param baseline the tree to base these changes on
* @param nupdates the number of elements in the update list
* @param updates the list of updates to perform
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates);
/** A stream to write a packfile to the ODB */
typedef struct git_odb_writepack git_odb_writepack;
+/** a writer for multi-pack-index files. */
+typedef struct git_midx_writer git_midx_writer;
+
/** An open refs database handle. */
typedef struct git_refdb git_refdb;
/** A custom backend for refs */
typedef struct git_refdb_backend git_refdb_backend;
+/** A git commit-graph */
+typedef struct git_commit_graph git_commit_graph;
+
+/** a writer for commit-graph files. */
+typedef struct git_commit_graph_writer git_commit_graph_writer;
+
/**
* Representation of an existing git repository,
* including all its object contents
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
-#define LIBGIT2_VERSION "1.1.0"
+#define LIBGIT2_VERSION "1.3.0"
#define LIBGIT2_VER_MAJOR 1
-#define LIBGIT2_VER_MINOR 1
+#define LIBGIT2_VER_MINOR 3
#define LIBGIT2_VER_REVISION 0
#define LIBGIT2_VER_PATCH 0
-#define LIBGIT2_SOVERSION "1.1"
+#define LIBGIT2_SOVERSION "1.3"
#endif
typedef struct git_worktree_prune_options {
unsigned int version;
+ /** A combination of `git_worktree_prune_t` */
uint32_t flags;
} git_worktree_prune_options;
{
"name": "libgit2",
- "version": "1.1.0",
+ "version": "1.3.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 ."
--- /dev/null
+# In attr_file_free, the locks are acquired in the opposite order in which they
+# are normally acquired. This is probably something worth fixing to have a
+# consistent lock hierarchy that is easy to understand.
+deadlock:attr_cache_lock
+
+# git_mwindow_file_register has the possibility of evicting some files from the
+# global cache. In order to avoid races and closing files that are currently
+# being accessed, before evicting any file it will attempt to acquire that
+# file's lock. Finally, git_mwindow_file_register is typically called with a
+# file lock held, because the caller will use the fd in the mwf immediately
+# after registering it. This causes ThreadSanitizer to observe different orders
+# of acquisition of the mutex (which implies a possibility of a deadlock),
+# _but_ since the files are added to the cache after other files have been
+# evicted, there cannot be a case where mwf A is trying to be registered while
+# evicting mwf B concurrently and viceversa: at most one of them can be present
+# in the cache.
+deadlock:git_mwindow_file_register
+
+# When invoking the time/timezone functions from git_signature_now(), they
+# access libc methods that need to be instrumented to correctly analyze the
+# data races.
+called_from_lib:libc.so.6
+
+# TODO(#5592): Investigate and fix this. It can be triggered by the `thread`
+# test suite.
+race:git_filter_list__load_ext
...
}
+{
+ ignore-openssl-init-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_stream_global_init
+ ...
+}
+
+{
+ ignore-openssl-legacy-init-leak
+ Memcheck:Leak
+ ...
+ fun:OPENSSL_init_ssl__legacy
+ ...
+}
+
+{
+ ignore-openssl-malloc-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_malloc
+ ...
+}
+
+{
+ ignore-openssl-realloc-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_realloc
+ ...
+}
+
{
ignore-glibc-getaddrinfo-cache
Memcheck:Leak
...
}
+{
+ ignore-libssh2-session-create
+ Memcheck:Leak
+ ...
+ fun:_git_ssh_session_create
+ ...
+}
+
+{
+ ignore-libssh2-setup-conn
+ Memcheck:Leak
+ ...
+ fun:_git_ssh_setup_conn
+ ...
+}
+
{
ignore-libssh2-gcrypt-control-leak
Memcheck:Leak
obj:*libcrypto.so*
...
}
+
+{
+ ignore-dlopen-leak
+ Memcheck:Leak
+ ...
+ fun:dlopen
+ ...
+}
+
+{
+ ignore-dlopen-leak
+ Memcheck:Leak
+ ...
+ fun:_dlerror_run
+ ...
+}
ENDIF()
ADD_FEATURE_INFO(debugpool GIT_DEBUG_POOL "debug pool allocator")
+IF(DEBUG_STRICT_ALLOC)
+ SET(GIT_DEBUG_STRICT_ALLOC 1)
+ENDIF()
+ADD_FEATURE_INFO(debugalloc GIT_DEBUG_STRICT_ALLOC "debug strict allocators")
+
+IF(DEBUG_STRICT_OPEN)
+ SET(GIT_DEBUG_STRICT_OPEN 1)
+ENDIF()
+ADD_FEATURE_INFO(debugopen GIT_DEBUG_STRICT_OPEN "path validation in open")
+
INCLUDE(PkgBuildConfig)
+INCLUDE(SanitizeBool)
# This variable will contain the libraries we need to put into
# libgit2.pc's Requires.private. That is, what we're linking to or
ENDIF()
ADD_FEATURE_INFO(tracing GIT_TRACE "tracing support")
-CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
IF (HAVE_FUTIMENS)
SET(GIT_USE_FUTIMENS 1)
ENDIF ()
+ADD_FEATURE_INFO(futimens GIT_USE_FUTIMENS "futimens support")
CHECK_PROTOTYPE_DEFINITION(qsort_r
"void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))"
ENDIF()
# Optional external dependency: zlib
-IF(NOT USE_BUNDLED_ZLIB)
+SanitizeBool(USE_BUNDLED_ZLIB)
+IF(USE_BUNDLED_ZLIB STREQUAL ON)
+ SET(USE_BUNDLED_ZLIB "Bundled")
+ENDIF()
+
+IF(USE_BUNDLED_ZLIB STREQUAL "OFF")
FIND_PACKAGE(ZLIB)
IF(ZLIB_FOUND)
LIST(APPEND LIBGIT2_SYSTEM_INCLUDES ${ZLIB_INCLUDE_DIRS})
MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." )
ENDIF()
ENDIF()
-IF(USE_BUNDLED_ZLIB OR NOT ZLIB_FOUND)
+IF(USE_BUNDLED_ZLIB STREQUAL "Chromium")
+ ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/chromium-zlib" "${libgit2_BINARY_DIR}/deps/chromium-zlib")
+ LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/chromium-zlib")
+ LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:chromium_zlib>)
+ ADD_FEATURE_INFO(zlib ON "using (Chromium) bundled zlib")
+ELSEIF(USE_BUNDLED_ZLIB OR NOT ZLIB_FOUND)
ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/zlib" "${libgit2_BINARY_DIR}/deps/zlib")
LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/zlib")
LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:zlib>)
# Optional external dependency: libssh2
IF (USE_SSH)
FIND_PKGLIBRARIES(LIBSSH2 libssh2)
+ IF (NOT LIBSSH2_FOUND)
+ FIND_PACKAGE(LibSSH2)
+ SET(LIBSSH2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR})
+ GET_FILENAME_COMPONENT(LIBSSH2_LIBRARY_DIRS "${LIBSSH2_LIBRARY}" DIRECTORY)
+ SET(LIBSSH2_LIBRARIES ${LIBSSH2_LIBRARY})
+ SET(LIBSSH2_LDFLAGS "-lssh2")
+ ENDIF()
ENDIF()
IF (LIBSSH2_FOUND)
SET(GIT_SSH 1)
set_target_properties(git2 PROPERTIES VERSION ${libgit2_VERSION})
set_target_properties(git2 PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}")
if(LIBGIT2_FILENAME)
- target_compile_definitions(git2internal PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
+ target_compile_definitions(git2 PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
set_target_properties(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
elseif(DEFINED LIBGIT2_PREFIX)
set_target_properties(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
*/
#include "alloc.h"
+#include "runtime.h"
+#include "allocators/failalloc.h"
#include "allocators/stdalloc.h"
-#include "allocators/win32_crtdbg.h"
-
-git_allocator git__allocator;
+#include "allocators/win32_leakcheck.h"
+
+/* Fail any allocation until git_libgit2_init is called. */
+git_allocator git__allocator = {
+ git_failalloc_malloc,
+ git_failalloc_calloc,
+ git_failalloc_strdup,
+ git_failalloc_strndup,
+ git_failalloc_substrdup,
+ git_failalloc_realloc,
+ git_failalloc_reallocarray,
+ git_failalloc_mallocarray,
+ git_failalloc_free
+};
static int setup_default_allocator(void)
{
-#if defined(GIT_MSVC_CRTDBG)
- return git_win32_crtdbg_init_allocator(&git__allocator);
+#if defined(GIT_WIN32_LEAKCHECK)
+ return git_win32_leakcheck_init_allocator(&git__allocator);
#else
return git_stdalloc_init_allocator(&git__allocator);
#endif
int git_allocator_global_init(void)
{
/*
- * We don't want to overwrite any allocator which has been set before
- * the init function is called.
+ * We don't want to overwrite any allocator which has been set
+ * before the init function is called.
*/
- if (git__allocator.gmalloc != NULL)
+ if (git__allocator.gmalloc != git_failalloc_malloc)
return 0;
return setup_default_allocator();
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "failalloc.h"
+
+void *git_failalloc_malloc(size_t len, const char *file, int line)
+{
+ GIT_UNUSED(len);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line)
+{
+ GIT_UNUSED(nelem);
+ GIT_UNUSED(elsize);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+char *git_failalloc_strdup(const char *str, const char *file, int line)
+{
+ GIT_UNUSED(str);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+char *git_failalloc_strndup(const char *str, size_t n, const char *file, int line)
+{
+ GIT_UNUSED(str);
+ GIT_UNUSED(n);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+char *git_failalloc_substrdup(const char *start, size_t n, const char *file, int line)
+{
+ GIT_UNUSED(start);
+ GIT_UNUSED(n);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line)
+{
+ GIT_UNUSED(ptr);
+ GIT_UNUSED(size);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+void *git_failalloc_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
+{
+ GIT_UNUSED(ptr);
+ GIT_UNUSED(nelem);
+ GIT_UNUSED(elsize);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+void *git_failalloc_mallocarray(size_t nelem, size_t elsize, const char *file, int line)
+{
+ GIT_UNUSED(nelem);
+ GIT_UNUSED(elsize);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+void git_failalloc_free(void *ptr)
+{
+ GIT_UNUSED(ptr);
+}
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_allocators_failalloc_h__
+#define INCLUDE_allocators_failalloc_h__
+
+#include "common.h"
+
+extern void *git_failalloc_malloc(size_t len, const char *file, int line);
+extern void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line);
+extern char *git_failalloc_strdup(const char *str, const char *file, int line);
+extern char *git_failalloc_strndup(const char *str, size_t n, const char *file, int line);
+extern char *git_failalloc_substrdup(const char *start, size_t n, const char *file, int line);
+extern void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line);
+extern void *git_failalloc_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line);
+extern void *git_failalloc_mallocarray(size_t nelem, size_t elsize, const char *file, int line);
+extern void git_failalloc_free(void *ptr);
+
+#endif
static void *stdalloc__malloc(size_t len, const char *file, int line)
{
- void *ptr = malloc(len);
+ void *ptr;
GIT_UNUSED(file);
GIT_UNUSED(line);
- if (!ptr) git_error_set_oom();
+#ifdef GIT_DEBUG_STRICT_ALLOC
+ if (!len)
+ return NULL;
+#endif
+
+ ptr = malloc(len);
+
+ if (!ptr)
+ git_error_set_oom();
+
return ptr;
}
static void *stdalloc__calloc(size_t nelem, size_t elsize, const char *file, int line)
{
- void *ptr = calloc(nelem, elsize);
+ void *ptr;
GIT_UNUSED(file);
GIT_UNUSED(line);
- if (!ptr) git_error_set_oom();
+#ifdef GIT_DEBUG_STRICT_ALLOC
+ if (!elsize || !nelem)
+ return NULL;
+#endif
+
+ ptr = calloc(nelem, elsize);
+
+ if (!ptr)
+ git_error_set_oom();
+
return ptr;
}
static char *stdalloc__strdup(const char *str, const char *file, int line)
{
- char *ptr = strdup(str);
+ char *ptr;
GIT_UNUSED(file);
GIT_UNUSED(line);
- if (!ptr) git_error_set_oom();
+ ptr = strdup(str);
+
+ if (!ptr)
+ git_error_set_oom();
+
return ptr;
}
length = p_strnlen(str, n);
if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
- !(ptr = stdalloc__malloc(alloclength, file, line)))
+ !(ptr = stdalloc__malloc(alloclength, file, line)))
return NULL;
if (length)
size_t alloclen;
if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
- !(ptr = stdalloc__malloc(alloclen, file, line)))
+ !(ptr = stdalloc__malloc(alloclen, file, line)))
return NULL;
memcpy(ptr, start, n);
static void *stdalloc__realloc(void *ptr, size_t size, const char *file, int line)
{
- void *new_ptr = realloc(ptr, size);
+ void *new_ptr;
GIT_UNUSED(file);
GIT_UNUSED(line);
- if (!new_ptr) git_error_set_oom();
+#ifdef GIT_DEBUG_STRICT_ALLOC
+ if (!size)
+ return NULL;
+#endif
+
+ new_ptr = realloc(ptr, size);
+
+ if (!new_ptr)
+ git_error_set_oom();
+
return new_ptr;
}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "win32_crtdbg.h"
-
-#if defined(GIT_MSVC_CRTDBG)
-
-#include "win32/w32_crtdbg_stacktrace.h"
-
-static void *crtdbg__malloc(size_t len, const char *file, int line)
-{
- void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) git_error_set_oom();
- return ptr;
-}
-
-static void *crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
-{
- void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) git_error_set_oom();
- return ptr;
-}
-
-static char *crtdbg__strdup(const char *str, const char *file, int line)
-{
- char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) git_error_set_oom();
- return ptr;
-}
-
-static char *crtdbg__strndup(const char *str, size_t n, const char *file, int line)
-{
- size_t length = 0, alloclength;
- char *ptr;
-
- length = p_strnlen(str, n);
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
- !(ptr = crtdbg__malloc(alloclength, file, line)))
- return NULL;
-
- if (length)
- memcpy(ptr, str, length);
-
- ptr[length] = '\0';
-
- return ptr;
-}
-
-static char *crtdbg__substrdup(const char *start, size_t n, const char *file, int line)
-{
- char *ptr;
- size_t alloclen;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
- !(ptr = crtdbg__malloc(alloclen, file, line)))
- return NULL;
-
- memcpy(ptr, start, n);
- ptr[n] = '\0';
- return ptr;
-}
-
-static void *crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
-{
- void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!new_ptr) git_error_set_oom();
- return new_ptr;
-}
-
-static void *crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
-{
- size_t newsize;
-
- if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize))
- return NULL;
-
- return crtdbg__realloc(ptr, newsize, file, line);
-}
-
-static void *crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
-{
- return crtdbg__reallocarray(NULL, nelem, elsize, file, line);
-}
-
-static void crtdbg__free(void *ptr)
-{
- free(ptr);
-}
-
-int git_win32_crtdbg_init_allocator(git_allocator *allocator)
-{
- allocator->gmalloc = crtdbg__malloc;
- allocator->gcalloc = crtdbg__calloc;
- allocator->gstrdup = crtdbg__strdup;
- allocator->gstrndup = crtdbg__strndup;
- allocator->gsubstrdup = crtdbg__substrdup;
- allocator->grealloc = crtdbg__realloc;
- allocator->greallocarray = crtdbg__reallocarray;
- allocator->gmallocarray = crtdbg__mallocarray;
- allocator->gfree = crtdbg__free;
- return 0;
-}
-
-#else
-
-int git_win32_crtdbg_init_allocator(git_allocator *allocator)
-{
- GIT_UNUSED(allocator);
- git_error_set(GIT_EINVALID, "crtdbg memory allocator not available");
- return -1;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_allocators_crtdbg_h
-#define INCLUDE_allocators_crtdbg_h
-
-#include "common.h"
-
-#include "alloc.h"
-
-int git_win32_crtdbg_init_allocator(git_allocator *allocator);
-
-#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "win32_leakcheck.h"
+
+#if defined(GIT_WIN32_LEAKCHECK)
+
+#include "win32/w32_leakcheck.h"
+
+static void *leakcheck_malloc(size_t len, const char *file, int line)
+{
+ void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
+ if (!ptr) git_error_set_oom();
+ return ptr;
+}
+
+static void *leakcheck_calloc(size_t nelem, size_t elsize, const char *file, int line)
+{
+ void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
+ if (!ptr) git_error_set_oom();
+ return ptr;
+}
+
+static char *leakcheck_strdup(const char *str, const char *file, int line)
+{
+ char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
+ if (!ptr) git_error_set_oom();
+ return ptr;
+}
+
+static char *leakcheck_strndup(const char *str, size_t n, const char *file, int line)
+{
+ size_t length = 0, alloclength;
+ char *ptr;
+
+ length = p_strnlen(str, n);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
+ !(ptr = leakcheck_malloc(alloclength, file, line)))
+ return NULL;
+
+ if (length)
+ memcpy(ptr, str, length);
+
+ ptr[length] = '\0';
+
+ return ptr;
+}
+
+static char *leakcheck_substrdup(const char *start, size_t n, const char *file, int line)
+{
+ char *ptr;
+ size_t alloclen;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
+ !(ptr = leakcheck_malloc(alloclen, file, line)))
+ return NULL;
+
+ memcpy(ptr, start, n);
+ ptr[n] = '\0';
+ return ptr;
+}
+
+static void *leakcheck_realloc(void *ptr, size_t size, const char *file, int line)
+{
+ void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
+ if (!new_ptr) git_error_set_oom();
+ return new_ptr;
+}
+
+static void *leakcheck_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
+{
+ size_t newsize;
+
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize))
+ return NULL;
+
+ return leakcheck_realloc(ptr, newsize, file, line);
+}
+
+static void *leakcheck_mallocarray(size_t nelem, size_t elsize, const char *file, int line)
+{
+ return leakcheck_reallocarray(NULL, nelem, elsize, file, line);
+}
+
+static void leakcheck_free(void *ptr)
+{
+ free(ptr);
+}
+
+int git_win32_leakcheck_init_allocator(git_allocator *allocator)
+{
+ allocator->gmalloc = leakcheck_malloc;
+ allocator->gcalloc = leakcheck_calloc;
+ allocator->gstrdup = leakcheck_strdup;
+ allocator->gstrndup = leakcheck_strndup;
+ allocator->gsubstrdup = leakcheck_substrdup;
+ allocator->grealloc = leakcheck_realloc;
+ allocator->greallocarray = leakcheck_reallocarray;
+ allocator->gmallocarray = leakcheck_mallocarray;
+ allocator->gfree = leakcheck_free;
+ return 0;
+}
+
+#else
+
+int git_win32_leakcheck_init_allocator(git_allocator *allocator)
+{
+ GIT_UNUSED(allocator);
+ git_error_set(GIT_EINVALID, "leakcheck memory allocator not available");
+ return -1;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_allocators_win32_leakcheck_h
+#define INCLUDE_allocators_win32_leakcheck_h
+
+#include "common.h"
+
+#include "alloc.h"
+
+int git_win32_leakcheck_init_allocator(git_allocator *allocator);
+
+#endif
git_annotated_commit *annotated_commit;
int error = 0;
- assert(out && commit);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(commit);
*out = NULL;
git_commit *commit = NULL;
int error = 0;
- assert(out && repo && id);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(id);
*out = NULL;
git_object *obj, *commit;
int error;
- assert(out && repo && revspec);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(revspec);
if ((error = git_revparse_single(&obj, repo, revspec)) < 0)
return error;
git_object *peeled;
int error = 0;
- assert(out && repo && ref);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(ref);
*out = NULL;
git_reference *head;
int error;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
*out = NULL;
- if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
+ if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
return -1;
error = git_annotated_commit_from_ref(out, repo, head);
const char *remote_url,
const git_oid *id)
{
- assert(repo && id && branch_name && remote_url);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(branch_name);
+ GIT_ASSERT_ARG(remote_url);
+ GIT_ASSERT_ARG(id);
if (annotated_commit_init_from_id(out, repo, id, branch_name) < 0)
return -1;
const git_oid *git_annotated_commit_id(
const git_annotated_commit *annotated_commit)
{
- assert(annotated_commit);
+ GIT_ASSERT_ARG_WITH_RETVAL(annotated_commit, NULL);
return git_commit_id(annotated_commit->commit);
}
const char *git_annotated_commit_ref(
const git_annotated_commit *annotated_commit)
{
- assert(annotated_commit);
+ GIT_ASSERT_ARG_WITH_RETVAL(annotated_commit, NULL);
return annotated_commit->ref_name;
}
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "apply.h"
-
#include "git2/apply.h"
#include "git2/patch.h"
#include "git2/filter.h"
#include "zstream.h"
#include "reader.h"
#include "index.h"
+#include "apply.h"
typedef struct {
/* The lines that we allocate ourself are allocated out of the pool.
unsigned int mode = 0;
int error = 0;
- assert(contents_out && filename_out && mode_out && (source || !source_len) && patch);
+ GIT_ASSERT_ARG(contents_out);
+ GIT_ASSERT_ARG(filename_out);
+ GIT_ASSERT_ARG(mode_out);
+ GIT_ASSERT_ARG(source || !source_len);
+ GIT_ASSERT_ARG(patch);
if (given_opts)
memcpy(&ctx.opts, given_opts, sizeof(git_apply_options));
size_t i;
int error = 0;
- assert(out && repo && preimage && diff);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(preimage);
+ GIT_ASSERT_ARG(diff);
*out = NULL;
int git_apply_options_init(git_apply_options *opts, unsigned int version)
{
+ GIT_ASSERT_ARG(opts);
+
GIT_INIT_STRUCTURE_FROM_TEMPLATE(
opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT);
return 0;
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
int error = GIT_EINVALID;
- assert(repo && diff);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(diff);
GIT_ERROR_CHECK_VERSION(
given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options");
error = git_reader_for_workdir(&pre_reader, repo, false);
break;
default:
- assert(false);
+ GIT_ASSERT(false);
}
if (error < 0)
error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
break;
default:
- assert(false);
+ GIT_ASSERT(false);
}
if (error < 0)
typedef git_array_t(char) git_array_generic_t;
-/* use a generic array for growth so this can return the new item */
-GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
+/* use a generic array for growth, return 0 on success */
+GIT_INLINE(int) git_array_grow(void *_a, size_t item_size)
{
volatile git_array_generic_t *a = _a;
size_t new_size;
if ((new_array = git__reallocarray(a->ptr, new_size, item_size)) == NULL)
goto on_oom;
- a->ptr = new_array; a->asize = new_size; a->size++;
- return a->ptr + (a->size - 1) * item_size;
+ a->ptr = new_array;
+ a->asize = new_size;
+ return 0;
on_oom:
git_array_clear(*a);
- return NULL;
+ return -1;
}
#define git_array_alloc(a) \
- (((a).size >= (a).asize) ? \
- git_array_grow(&(a), sizeof(*(a).ptr)) : \
- ((a).ptr ? &(a).ptr[(a).size++] : NULL))
+ (((a).size < (a).asize || git_array_grow(&(a), sizeof(*(a).ptr)) == 0) ? \
+ &(a).ptr[(a).size++] : (void *)NULL)
-#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
+#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : (void *)NULL)
-#define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : NULL)
+#define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : (void *)NULL)
-#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : NULL)
+#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : (void *)NULL)
#define git_array_size(a) (a).size
static int collect_attr_files(
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags,
+ git_attr_options *opts,
const char *path,
git_vector *files);
static void release_attr_files(git_vector *files);
-int git_attr_get(
+int git_attr_get_ext(
const char **value,
git_repository *repo,
- uint32_t flags,
+ git_attr_options *opts,
const char *pathname,
const char *name)
{
git_attr_rule *rule;
git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
- assert(value && repo && name);
+ GIT_ASSERT_ARG(value);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
*value = NULL;
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0)
goto cleanup;
memset(&attr, 0, sizeof(attr));
return error;
}
+int git_attr_get(
+ const char **value,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ const char *name)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_get_ext(value, repo, &opts, pathname, name);
+}
+
typedef struct {
git_attr_name name;
const char **values,
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags,
+ git_attr_options *opts,
const char *pathname,
size_t num_attr,
const char **names)
if (!num_attr)
return 0;
- assert(values && repo && names);
+ GIT_ASSERT_ARG(values);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(pathname);
+ GIT_ASSERT_ARG(names);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE;
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0)
+ if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0)
goto cleanup;
info = git__calloc(num_attr, sizeof(attr_get_many_info));
size_t num_attr,
const char **names)
{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
return git_attr_get_many_with_session(
- values, repo, NULL, flags, pathname, num_attr, names);
+ values, repo, NULL, &opts, pathname, num_attr, names);
+}
+
+int git_attr_get_many_ext(
+ const char **values,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ size_t num_attr,
+ const char **names)
+{
+ return git_attr_get_many_with_session(
+ values, repo, NULL, opts, pathname, num_attr, names);
}
int git_attr_foreach(
const char *pathname,
int (*callback)(const char *name, const char *value, void *payload),
void *payload)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_foreach_ext(repo, &opts, pathname, callback, payload);
+}
+
+int git_attr_foreach_ext(
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ int (*callback)(const char *name, const char *value, void *payload),
+ void *payload)
{
int error;
git_attr_path path;
git_strmap *seen = NULL;
git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
- assert(repo && callback);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(callback);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE;
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
- if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 ||
+ if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 ||
(error = git_strmap_new(&seen)) < 0)
goto cleanup;
return error;
}
-static int preload_attr_file(
+static int preload_attr_source(
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *file,
- bool allow_macros)
+ git_attr_file_source *source)
{
int error;
git_attr_file *preload = NULL;
- if (!file)
+ if (!source)
return 0;
- if (!(error = git_attr_cache__get(&preload, repo, attr_session, source, base, file,
- git_attr_file__parse_buffer, allow_macros)))
+
+ error = git_attr_cache__get(&preload, repo, attr_session, source,
+ git_attr_file__parse_buffer, true);
+
+ if (!error)
git_attr_file__free(preload);
return error;
}
+GIT_INLINE(int) preload_attr_file(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ const char *base,
+ const char *filename)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
+
+ if (!filename)
+ return 0;
+
+ source.base = base;
+ source.filename = filename;
+
+ return preload_attr_source(repo, attr_session, &source);
+}
+
static int system_attr_file(
git_buf *out,
git_attr_session *attr_session)
static int attr_setup(
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags)
+ git_attr_options *opts)
{
- git_buf path = GIT_BUF_INIT;
+ git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT;
+ git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE, NULL };
+ git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_HEAD, NULL, GIT_ATTR_FILE, NULL };
+ git_attr_file_source commit_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL };
git_index *idx = NULL;
const char *workdir;
int error = 0;
* definitions will be available for later file parsing.
*/
- if ((error = system_attr_file(&path, attr_session)) < 0 ||
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- NULL, path.ptr, true)) < 0) {
+ if ((error = system_attr_file(&system, attr_session)) < 0 ||
+ (error = preload_attr_file(repo, attr_session, NULL, system.ptr)) < 0) {
if (error != GIT_ENOTFOUND)
goto out;
+
+ error = 0;
}
- if ((error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- NULL, git_repository_attr_cache(repo)->cfg_attr_file, true)) < 0)
+ if ((error = preload_attr_file(repo, attr_session, NULL,
+ git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
goto out;
- git_buf_clear(&path); /* git_repository_item_path expects an empty buffer, because it uses git_buf_set */
- if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- path.ptr, GIT_ATTR_FILE_INREPO, true)) < 0) {
+ if ((error = git_repository_item_path(&info, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
+ (error = preload_attr_file(repo, attr_session, info.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
if (error != GIT_ENOTFOUND)
goto out;
+
+ error = 0;
}
if ((workdir = git_repository_workdir(repo)) != NULL &&
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- workdir, GIT_ATTR_FILE, true)) < 0)
+ (error = preload_attr_file(repo, attr_session, workdir, GIT_ATTR_FILE)) < 0)
goto out;
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_INDEX,
- NULL, GIT_ATTR_FILE, true)) < 0)
+ (error = preload_attr_source(repo, attr_session, &index_source)) < 0)
goto out;
- if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 &&
- (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_HEAD,
- NULL, GIT_ATTR_FILE, true)) < 0)
+ if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
+ (error = preload_attr_source(repo, attr_session, &head_source)) < 0)
goto out;
+ if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) {
+#ifndef GIT_DEPRECATE_HARD
+ if (opts->commit_id)
+ commit_source.commit_id = opts->commit_id;
+ else
+#endif
+ commit_source.commit_id = &opts->attr_commit_id;
+
+ if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0)
+ goto out;
+ }
+
if (attr_session)
attr_session->init_setup = 1;
out:
- git_buf_dispose(&path);
+ git_buf_dispose(&system);
+ git_buf_dispose(&info);
return error;
}
git_attr_rule *macro = NULL;
git_pool *pool;
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
if ((error = git_attr_cache__init(repo)) < 0)
return error;
typedef struct {
git_repository *repo;
git_attr_session *attr_session;
- uint32_t flags;
+ git_attr_options *opts;
const char *workdir;
git_index *index;
git_vector *files;
} attr_walk_up_info;
static int attr_decide_sources(
- uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
+ uint32_t flags,
+ bool has_wd,
+ bool has_index,
+ git_attr_file_source_t *srcs)
{
int count = 0;
switch (flags & 0x03) {
case GIT_ATTR_CHECK_FILE_THEN_INDEX:
if (has_wd)
- srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
break;
case GIT_ATTR_CHECK_INDEX_THEN_FILE:
if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
if (has_wd)
- srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
break;
case GIT_ATTR_CHECK_INDEX_ONLY:
if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
break;
}
if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
- srcs[count++] = GIT_ATTR_FILE__FROM_HEAD;
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_HEAD;
+
+ if ((flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT;
return count;
}
-static int push_attr_file(
+static int push_attr_source(
git_repository *repo,
git_attr_session *attr_session,
git_vector *list,
- git_attr_file_source source,
- const char *base,
- const char *filename,
+ git_attr_file_source *source,
bool allow_macros)
{
int error = 0;
git_attr_file *file = NULL;
error = git_attr_cache__get(&file, repo, attr_session,
- source, base, filename, git_attr_file__parse_buffer, allow_macros);
+ source,
+ git_attr_file__parse_buffer,
+ allow_macros);
if (error < 0)
return error;
return error;
}
+GIT_INLINE(int) push_attr_file(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_vector *list,
+ const char *base,
+ const char *filename)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
+ return push_attr_source(repo, attr_session, list, &source, true);
+}
+
static int push_one_attr(void *ref, const char *path)
{
attr_walk_up_info *info = (attr_walk_up_info *)ref;
- git_attr_file_source src[GIT_ATTR_FILE_NUM_SOURCES];
+ git_attr_file_source_t src[GIT_ATTR_FILE_NUM_SOURCES];
int error = 0, n_src, i;
bool allow_macros;
- n_src = attr_decide_sources(
- info->flags, info->workdir != NULL, info->index != NULL, src);
+ n_src = attr_decide_sources(info->opts ? info->opts->flags : 0,
+ info->workdir != NULL,
+ info->index != NULL,
+ src);
+
allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
- for (i = 0; !error && i < n_src; ++i)
- error = push_attr_file(info->repo, info->attr_session, info->files,
- src[i], path, GIT_ATTR_FILE, allow_macros);
+ for (i = 0; !error && i < n_src; ++i) {
+ git_attr_file_source source = { src[i], path, GIT_ATTR_FILE };
+
+ if (src[i] == GIT_ATTR_FILE_SOURCE_COMMIT && info->opts) {
+#ifndef GIT_DEPRECATE_HARD
+ if (info->opts->commit_id)
+ source.commit_id = info->opts->commit_id;
+ else
+#endif
+ source.commit_id = &info->opts->attr_commit_id;
+ }
+
+ error = push_attr_source(info->repo, info->attr_session, info->files,
+ &source, allow_macros);
+ }
return error;
}
static int collect_attr_files(
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags,
+ git_attr_options *opts,
const char *path,
git_vector *files)
{
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL };
- if ((error = attr_setup(repo, attr_session, flags)) < 0)
+ GIT_ASSERT(!git_path_is_absolute(path));
+
+ if ((error = attr_setup(repo, attr_session, opts)) < 0)
return error;
/* Resolve path in a non-bare repo */
- if (workdir != NULL)
- error = git_path_find_dir(&dir, path, workdir);
- else
+ if (workdir != NULL) {
+ if (!(error = git_repository_workdir_path(&dir, repo, path)))
+ error = git_path_find_dir(&dir);
+ }
+ else {
error = git_path_dirname_r(&dir, path);
+ }
+
if (error < 0)
goto cleanup;
*/
if ((error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
- (error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- attrfile.ptr, GIT_ATTR_FILE_INREPO, true)) < 0) {
+ (error = push_attr_file(repo, attr_session, files, attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
if (error != GIT_ENOTFOUND)
goto cleanup;
}
info.repo = repo;
info.attr_session = attr_session;
- info.flags = flags;
+ info.opts = opts;
info.workdir = workdir;
if (git_repository_index__weakptr(&info.index, repo) < 0)
git_error_clear(); /* no error even if there is no index */
goto cleanup;
if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
- error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- NULL, git_repository_attr_cache(repo)->cfg_attr_file, true);
+ error = push_attr_file(repo, attr_session, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
if (error < 0)
goto cleanup;
}
- if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
+ if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
error = system_attr_file(&dir, attr_session);
if (!error)
- error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- NULL, dir.ptr, true);
+ error = push_attr_file(repo, attr_session, files, NULL, dir.ptr);
else if (error == GIT_ENOTFOUND)
error = 0;
}
#include "repository.h"
#include "filebuf.h"
#include "attrcache.h"
-#include "buf_text.h"
#include "git2/blob.h"
#include "git2/tree.h"
#include "blob.h"
int git_attr_file__new(
git_attr_file **out,
git_attr_file_entry *entry,
- git_attr_file_source source)
+ git_attr_file_source *source)
{
git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
GIT_ERROR_CHECK_ALLOC(attrs);
goto on_error;
GIT_REFCOUNT_INC(attrs);
- attrs->entry = entry;
- attrs->source = source;
+ attrs->entry = entry;
+ memcpy(&attrs->source, source, sizeof(git_attr_file_source));
*out = attrs;
return 0;
git_repository *repo,
git_attr_session *attr_session,
git_attr_file_entry *entry,
- git_attr_file_source source,
+ git_attr_file_source *source,
git_attr_file_parser parser,
bool allow_macros)
{
int error = 0;
+ git_commit *commit = NULL;
git_tree *tree = NULL;
git_tree_entry *tree_entry = NULL;
git_blob *blob = NULL;
struct stat st;
bool nonexistent = false;
int bom_offset;
- git_bom_t bom;
+ git_buf_bom_t bom;
git_oid id;
git_object_size_t blobsize;
*out = NULL;
- switch (source) {
- case GIT_ATTR_FILE__IN_MEMORY:
+ switch (source->type) {
+ case GIT_ATTR_FILE_SOURCE_MEMORY:
/* in-memory attribute file doesn't need data */
break;
- case GIT_ATTR_FILE__FROM_INDEX: {
+ case GIT_ATTR_FILE_SOURCE_INDEX: {
if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
(error = git_blob_lookup(&blob, repo, &id)) < 0)
return error;
git_buf_put(&content, git_blob_rawcontent(blob), (size_t)blobsize);
break;
}
- case GIT_ATTR_FILE__FROM_FILE: {
+ case GIT_ATTR_FILE_SOURCE_FILE: {
int fd = -1;
/* For open or read errors, pretend that we got ENOTFOUND. */
break;
}
- case GIT_ATTR_FILE__FROM_HEAD: {
- if ((error = git_repository_head_tree(&tree, repo)) < 0 ||
- (error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0 ||
- (error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0)
+ case GIT_ATTR_FILE_SOURCE_HEAD:
+ case GIT_ATTR_FILE_SOURCE_COMMIT: {
+ if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT) {
+ if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0 ||
+ (error = git_commit_tree(&tree, commit)) < 0)
+ goto cleanup;
+ } else {
+ if ((error = git_repository_head_tree(&tree, repo)) < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0) {
+ /*
+ * If the attributes file does not exist, we can
+ * cache an empty file for this commit to prevent
+ * needless future lookups.
+ */
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ break;
+ }
+
+ goto cleanup;
+ }
+
+ if ((error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0)
goto cleanup;
/*
break;
}
default:
- git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source);
+ git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source->type);
return -1;
}
/* advance over a UTF8 BOM */
content_str = git_buf_cstr(&content);
- bom_offset = git_buf_text_detect_bom(&bom, &content);
+ bom_offset = git_buf_detect_bom(&bom, &content);
- if (bom == GIT_BOM_UTF8)
+ if (bom == GIT_BUF_BOM_UTF8)
content_str += bom_offset;
/* store the key of the attr_reader; don't bother with cache
/* write cache breakers */
if (nonexistent)
file->nonexistent = 1;
- else if (source == GIT_ATTR_FILE__FROM_INDEX)
+ else if (source->type == GIT_ATTR_FILE_SOURCE_INDEX)
git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
- else if (source == GIT_ATTR_FILE__FROM_HEAD)
+ else if (source->type == GIT_ATTR_FILE_SOURCE_HEAD)
git_oid_cpy(&file->cache_data.oid, git_tree_id(tree));
- else if (source == GIT_ATTR_FILE__FROM_FILE)
+ else if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT)
+ git_oid_cpy(&file->cache_data.oid, git_tree_id(tree));
+ else if (source->type == GIT_ATTR_FILE_SOURCE_FILE)
git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
/* else always cacheable */
git_blob_free(blob);
git_tree_entry_free(tree_entry);
git_tree_free(tree);
+ git_commit_free(commit);
git_buf_dispose(&content);
return error;
int git_attr_file__out_of_date(
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file *file)
+ git_attr_file *file,
+ git_attr_file_source *source)
{
if (!file)
return 1;
else if (file->nonexistent)
return 1;
- switch (file->source) {
- case GIT_ATTR_FILE__IN_MEMORY:
+ switch (file->source.type) {
+ case GIT_ATTR_FILE_SOURCE_MEMORY:
return 0;
- case GIT_ATTR_FILE__FROM_FILE:
+ case GIT_ATTR_FILE_SOURCE_FILE:
return git_futils_filestamp_check(
&file->cache_data.stamp, file->entry->fullpath);
- case GIT_ATTR_FILE__FROM_INDEX: {
+ case GIT_ATTR_FILE_SOURCE_INDEX: {
int error;
git_oid id;
return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
}
- case GIT_ATTR_FILE__FROM_HEAD: {
- git_tree *tree;
+ case GIT_ATTR_FILE_SOURCE_HEAD: {
+ git_tree *tree = NULL;
+ int error = git_repository_head_tree(&tree, repo);
+
+ if (error < 0)
+ return error;
+
+ error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0);
+
+ git_tree_free(tree);
+ return error;
+ }
+
+ case GIT_ATTR_FILE_SOURCE_COMMIT: {
+ git_commit *commit = NULL;
+ git_tree *tree = NULL;
int error;
- if ((error = git_repository_head_tree(&tree, repo)) < 0)
+ if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0)
+ return error;
+
+ error = git_commit_tree(&tree, commit);
+ git_commit_free(commit);
+
+ if (error < 0)
return error;
- error = git_oid__cmp(&file->cache_data.oid, git_tree_id(tree));
+ error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0);
git_tree_free(tree);
return error;
}
default:
- git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source);
+ git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source.type);
return -1;
}
}
{
uint32_t h = 5381;
int c;
- assert(name);
+
+ GIT_ASSERT_ARG(name);
+
while ((c = (int)*name++) != 0)
h = ((h << 5) + h) + c;
return h;
int git_attr_file__load_standalone(git_attr_file **out, const char *path)
{
git_buf content = GIT_BUF_INIT;
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
git_attr_file *file = NULL;
int error;
* don't have to free it - freeing file+pool will free cache entry, too.
*/
- if ((error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE)) < 0 ||
+ if ((error = git_attr_file__new(&file, NULL, &source)) < 0 ||
(error = git_attr_file__parse_buffer(NULL, file, content.ptr, true)) < 0 ||
- (error = git_attr_cache__alloc_file_entry(&file->entry, NULL, path, &file->pool)) < 0)
+ (error = git_attr_cache__alloc_file_entry(&file->entry, NULL, NULL, path, &file->pool)) < 0)
goto out;
*out = file;
}
int git_attr_path__init(
- git_attr_path *info, const char *path, const char *base, git_dir_flag dir_flag)
+ git_attr_path *info,
+ const char *path,
+ const char *base,
+ git_dir_flag dir_flag)
{
ssize_t root;
int slash_count, allow_space;
bool escaped;
- assert(spec && base && *base);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(base && *base);
if (parse_optimized_patterns(spec, pool, *base))
return 0;
const char *scan = *base;
git_attr_assignment *assign = NULL;
- assert(assigns && !assigns->length);
+ GIT_ASSERT_ARG(assigns && !assigns->length);
git_vector_set_cmp(assigns, sort_by_hash_and_name);
int git_attr_session__init(git_attr_session *session, git_repository *repo)
{
- assert(repo);
+ GIT_ASSERT_ARG(repo);
memset(session, 0, sizeof(*session));
- session->key = git_atomic_inc(&repo->attr_session_key);
+ session->key = git_atomic32_inc(&repo->attr_session_key);
return 0;
}
(GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
typedef enum {
- GIT_ATTR_FILE__IN_MEMORY = 0,
- GIT_ATTR_FILE__FROM_FILE = 1,
- GIT_ATTR_FILE__FROM_INDEX = 2,
- GIT_ATTR_FILE__FROM_HEAD = 3,
+ GIT_ATTR_FILE_SOURCE_MEMORY = 0,
+ GIT_ATTR_FILE_SOURCE_FILE = 1,
+ GIT_ATTR_FILE_SOURCE_INDEX = 2,
+ GIT_ATTR_FILE_SOURCE_HEAD = 3,
+ GIT_ATTR_FILE_SOURCE_COMMIT = 4,
- GIT_ATTR_FILE_NUM_SOURCES = 4
+ GIT_ATTR_FILE_NUM_SOURCES = 5
+} git_attr_file_source_t;
+
+typedef struct {
+ /* The source location for the attribute file. */
+ git_attr_file_source_t type;
+
+ /*
+ * The filename of the attribute file to read (relative to the
+ * given base path).
+ */
+ const char *base;
+ const char *filename;
+
+ /*
+ * The commit ID when the given source type is a commit (or NULL
+ * for the repository's HEAD commit.)
+ */
+ git_oid *commit_id;
} git_attr_file_source;
extern const char *git_attr__true;
const char **values_out,
git_repository *repo,
git_attr_session *attr_session,
- uint32_t flags,
+ git_attr_options *opts,
const char *path,
size_t num_attr,
const char **names);
int git_attr_file__new(
git_attr_file **out,
git_attr_file_entry *entry,
- git_attr_file_source source);
+ git_attr_file_source *source);
void git_attr_file__free(git_attr_file *file);
git_repository *repo,
git_attr_session *attr_session,
git_attr_file_entry *ce,
- git_attr_file_source source,
+ git_attr_file_source *source,
git_attr_file_parser parser,
bool allow_macros);
git_attr_file **out, const char *path);
int git_attr_file__out_of_date(
- git_repository *repo, git_attr_session *session, git_attr_file *file);
+ git_repository *repo, git_attr_session *session, git_attr_file *file, git_attr_file_source *source);
int git_attr_file__parse_buffer(
git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros);
typedef enum { GIT_DIR_FLAG_TRUE = 1, GIT_DIR_FLAG_FALSE = 0, GIT_DIR_FLAG_UNKNOWN = -1 } git_dir_flag;
extern int git_attr_path__init(
- git_attr_path *info, const char *path, const char *base, git_dir_flag is_dir);
-
+ git_attr_path *out,
+ const char *path,
+ const char *base,
+ git_dir_flag is_dir);
extern void git_attr_path__free(git_attr_path *info);
extern int git_attr_assignment__parse(
int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
+ git_repository *repo,
const char *base,
const char *path,
git_pool *pool)
}
memcpy(&ce->fullpath[baselen], path, pathlen);
+ if (git_path_validate_workdir_with_len(repo, ce->fullpath, pathlen + baselen) < 0)
+ return -1;
+
ce->path = &ce->fullpath[baselen];
*out = ce;
git_attr_file_entry *entry = NULL;
int error;
- if ((error = git_attr_cache__alloc_file_entry(&entry, git_repository_workdir(repo),
- path, &cache->pool)) < 0)
+ if ((error = git_attr_cache__alloc_file_entry(&entry, repo,
+ git_repository_workdir(repo), path, &cache->pool)) < 0)
return error;
if ((error = git_strmap_set(cache->files, entry->path, entry)) < 0)
* Replace the existing value if another thread has
* created it in the meantime.
*/
- old = git__swap(entry->file[file->source], file);
+ old = git_atomic_swap(entry->file[file->source.type], file);
if (old) {
GIT_REFCOUNT_OWN(old, NULL);
{
int error = 0;
git_attr_file_entry *entry;
- git_attr_file *old = NULL;
+ git_attr_file *oldfile = NULL;
if (!file)
return 0;
return error;
if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
- old = git__compare_and_swap(&entry->file[file->source], file, NULL);
+ oldfile = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL);
attr_cache_unlock(cache);
- if (old) {
- GIT_REFCOUNT_OWN(old, NULL);
- git_attr_file__free(old);
+ if (oldfile == file) {
+ GIT_REFCOUNT_OWN(file, NULL);
+ git_attr_file__free(file);
}
return error;
git_attr_file_entry **out_entry,
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename)
+ git_attr_file_source *source)
{
int error = 0;
git_buf path = GIT_BUF_INIT;
- const char *wd = git_repository_workdir(repo), *relfile;
+ const char *wd = git_repository_workdir(repo);
+ const char *filename;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL;
/* join base and path as needed */
- if (base != NULL && git_path_root(filename) < 0) {
+ if (source->base != NULL && git_path_root(source->filename) < 0) {
git_buf *p = attr_session ? &attr_session->tmp : &path;
- if (git_buf_joinpath(p, base, filename) < 0)
+ if (git_buf_joinpath(p, source->base, source->filename) < 0 ||
+ git_path_validate_workdir_buf(repo, p) < 0)
return -1;
filename = p->ptr;
+ } else {
+ filename = source->filename;
}
- relfile = filename;
- if (wd && !git__prefixcmp(relfile, wd))
- relfile += strlen(wd);
+ if (wd && !git__prefixcmp(filename, wd))
+ filename += strlen(wd);
/* check cache for existing entry */
if ((error = attr_cache_lock(cache)) < 0)
goto cleanup;
- entry = attr_cache_lookup_entry(cache, relfile);
- if (!entry)
- error = attr_cache_make_entry(&entry, repo, relfile);
- else if (entry->file[source] != NULL) {
- file = entry->file[source];
+ entry = attr_cache_lookup_entry(cache, filename);
+
+ if (!entry) {
+ error = attr_cache_make_entry(&entry, repo, filename);
+ } else if (entry->file[source->type] != NULL) {
+ file = entry->file[source->type];
GIT_REFCOUNT_INC(file);
}
git_attr_file **out,
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename,
+ git_attr_file_source *source,
git_attr_file_parser parser,
bool allow_macros)
{
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL, *updated = NULL;
- if ((error = attr_cache_lookup(
- &file, &entry, repo, attr_session, source, base, filename)) < 0)
+ if ((error = attr_cache_lookup(&file, &entry, repo, attr_session, source)) < 0)
return error;
/* load file if we don't have one or if existing one is out of date */
- if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0)
- error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser, allow_macros);
+ if (!file ||
+ (error = git_attr_file__out_of_date(repo, attr_session, file, source)) > 0)
+ error = git_attr_file__load(&updated, repo, attr_session,
+ entry, source, parser,
+ allow_macros);
/* if we loaded the file, insert into and/or update cache */
if (updated) {
- if ((error = attr_cache_upsert(cache, updated)) < 0)
+ if ((error = attr_cache_upsert(cache, updated)) < 0) {
git_attr_file__free(updated);
- else {
+ } else {
git_attr_file__free(file); /* offset incref from lookup */
file = updated;
}
bool git_attr_cache__is_cached(
git_repository *repo,
- git_attr_file_source source,
+ git_attr_file_source_t source_type,
const char *filename)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
if ((entry = git_strmap_get(files, filename)) == NULL)
return false;
- return entry && (entry->file[source] != NULL);
+ return entry && (entry->file[source_type] != NULL);
}
git_strmap_foreach_value(cache->files, entry, {
for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) {
- if ((file = git__swap(entry->file[i], NULL)) != NULL) {
+ if ((file = git_atomic_swap(entry->file[i], NULL)) != NULL) {
GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(file);
}
(ret = git_pool_init(&cache->pool, 1)) < 0)
goto cancel;
- cache = git__compare_and_swap(&repo->attrcache, NULL, cache);
- if (cache)
+ if (git_atomic_compare_and_swap(&repo->attrcache, NULL, cache) != NULL)
goto cancel; /* raced with another thread, free this but no error */
git_config_free(cfg);
/* this could be done less expensively, but for now, we'll just free
* the entire attrcache and let the next use reinitialize it...
*/
- if (repo && (cache = git__swap(repo->attrcache, NULL)) != NULL)
+ if (repo && (cache = git_atomic_swap(repo->attrcache, NULL)) != NULL)
attr_cache__free(cache);
return 0;
git_attr_file **file,
git_repository *repo,
git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename,
+ git_attr_file_source *source,
git_attr_file_parser parser,
bool allow_macros);
extern bool git_attr_cache__is_cached(
git_repository *repo,
- git_attr_file_source source,
- const char *path);
+ git_attr_file_source_t source_type,
+ const char *filename);
extern int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
+ git_repository *repo,
const char *base,
const char *path,
git_pool *pool);
return line <= hunk->final_start_line_number;
}
-static git_blame_hunk* new_hunk(
+static git_blame_hunk *new_hunk(
size_t start,
size_t lines,
size_t orig_start,
return hunk;
}
-static git_blame_hunk* dup_hunk(git_blame_hunk *hunk)
+static void free_hunk(git_blame_hunk *hunk)
+{
+ git__free((void*)hunk->orig_path);
+ git_signature_free(hunk->final_signature);
+ git_signature_free(hunk->orig_signature);
+ git__free(hunk);
+}
+
+static git_blame_hunk *dup_hunk(git_blame_hunk *hunk)
{
git_blame_hunk *newhunk = new_hunk(
hunk->final_start_line_number,
git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id);
git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id);
newhunk->boundary = hunk->boundary;
- git_signature_dup(&newhunk->final_signature, hunk->final_signature);
- git_signature_dup(&newhunk->orig_signature, hunk->orig_signature);
- return newhunk;
-}
-static void free_hunk(git_blame_hunk *hunk)
-{
- git__free((void*)hunk->orig_path);
- git_signature_free(hunk->final_signature);
- git_signature_free(hunk->orig_signature);
- git__free(hunk);
+ if (git_signature_dup(&newhunk->final_signature, hunk->final_signature) < 0 ||
+ git_signature_dup(&newhunk->orig_signature, hunk->orig_signature) < 0) {
+ free_hunk(newhunk);
+ return NULL;
+ }
+
+ return newhunk;
}
/* Starting with the hunk that includes start_line, shift all following hunks'
}
}
-git_blame* git_blame__alloc(
+git_blame *git_blame__alloc(
git_repository *repo,
git_blame_options opts,
const char *path)
uint32_t git_blame_get_hunk_count(git_blame *blame)
{
- assert(blame);
+ GIT_ASSERT_ARG(blame);
return (uint32_t)blame->hunks.length;
}
const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t index)
{
- assert(blame);
+ GIT_ASSERT_ARG_WITH_RETVAL(blame, NULL);
return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
}
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno)
{
size_t i, new_lineno = lineno;
- assert(blame);
+
+ GIT_ASSERT_ARG_WITH_RETVAL(blame, NULL);
if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) {
return git_blame_get_hunk_byindex(blame, (uint32_t)i);
return blame->num_lines;
}
-static git_blame_hunk* hunk_from_entry(git_blame__entry *e, git_blame *blame)
+static git_blame_hunk *hunk_from_entry(git_blame__entry *e, git_blame *blame)
{
git_blame_hunk *h = new_hunk(
e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path);
git_blame_options normOptions = GIT_BLAME_OPTIONS_INIT;
git_blame *blame = NULL;
- assert(out && repo && path);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(path);
+
if ((error = normalize_options(&normOptions, options, repo)) < 0)
goto on_error;
diffopts.context_lines = 0;
- assert(out && reference && buffer && buffer_len);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(reference);
+ GIT_ASSERT_ARG(buffer && buffer_len);
blame = git_blame__alloc(reference->repository, reference->options, reference->path);
GIT_ERROR_CHECK_ALLOC(blame);
return -1;
}
-static git_blame__origin* find_origin(
+static git_blame__origin *find_origin(
git_blame *blame,
git_commit *parent,
git_blame__origin *origin)
#include "filebuf.h"
#include "filter.h"
-#include "buf_text.h"
const void *git_blob_rawcontent(const git_blob *blob)
{
- assert(blob);
+ GIT_ASSERT_ARG_WITH_RETVAL(blob, NULL);
+
if (blob->raw)
return blob->data.raw.data;
else
git_object_size_t git_blob_rawsize(const git_blob *blob)
{
- assert(blob);
+ GIT_ASSERT_ARG(blob);
+
if (blob->raw)
return blob->data.raw.size;
else
int git_blob__parse_raw(void *_blob, const char *data, size_t size)
{
git_blob *blob = (git_blob *) _blob;
- assert(blob);
+
+ GIT_ASSERT_ARG(blob);
+
blob->raw = 1;
blob->data.raw.data = data;
blob->data.raw.size = size;
int git_blob__parse(void *_blob, git_odb_object *odb_obj)
{
git_blob *blob = (git_blob *) _blob;
- assert(blob);
+
+ GIT_ASSERT_ARG(blob);
+
git_cached_obj_incref((git_cached_obj *)odb_obj);
blob->raw = 0;
blob->data.odb = odb_obj;
git_odb *odb;
git_odb_stream *stream;
- assert(id && repo);
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(repo);
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJECT_BLOB)) < 0)
git_object_size_t *size,
git_odb *odb,
const char *full_path,
- git_filter_list *fl)
+ git_filter_list *fl,
+ git_repository* repo)
{
int error;
git_buf tgt = GIT_BUF_INIT;
- error = git_filter_list_apply_to_file(&tgt, fl, NULL, full_path);
+ error = git_filter_list_apply_to_file(&tgt, fl, repo, full_path);
/* Write the file to disk if it was properly filtered */
if (!error) {
mode_t mode;
git_buf path = GIT_BUF_INIT;
- assert(hint_path || !try_load_filters);
+ GIT_ASSERT_ARG(hint_path || !try_load_filters);
if (!content_path) {
- if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
- return GIT_EBAREREPO;
-
- if (git_buf_joinpath(
- &path, git_repository_workdir(repo), hint_path) < 0)
+ if (git_repository_workdir_path(&path, repo, hint_path) < 0)
return -1;
content_path = path.ptr;
error = write_file_stream(id, odb, content_path, size);
else {
/* We need to apply one or more filters */
- error = write_file_filtered(id, &size, odb, content_path, fl);
+ error = write_file_filtered(id, &size, odb, content_path, fl, repo);
git_filter_list_free(fl);
}
{
int error;
git_buf full_path = GIT_BUF_INIT;
- const char *workdir, *hintpath;
+ const char *workdir, *hintpath = NULL;
if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
git_buf_dispose(&full_path);
return error;
}
- hintpath = git_buf_cstr(&full_path);
workdir = git_repository_workdir(repo);
- if (workdir && !git__prefixcmp(hintpath, workdir))
- hintpath += strlen(workdir);
+ if (workdir && !git__prefixcmp(full_path.ptr, workdir))
+ hintpath = full_path.ptr + strlen(workdir);
error = git_blob__create_from_paths(
- id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
+ id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, !!hintpath);
git_buf_dispose(&full_path);
return error;
git_buf path = GIT_BUF_INIT;
blob_writestream *stream;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
stream = git__calloc(1, sizeof(blob_writestream));
GIT_ERROR_CHECK_ALLOC(stream);
git_buf content = GIT_BUF_INIT;
git_object_size_t size;
- assert(blob);
+ GIT_ASSERT_ARG(blob);
size = git_blob_rawsize(blob);
git_buf_attach_notowned(&content, git_blob_rawcontent(blob),
(size_t)min(size, GIT_FILTER_BYTES_TO_CHECK_NUL));
- return git_buf_text_is_binary(&content);
+ return git_buf_is_binary(&content);
+}
+
+int git_blob_filter_options_init(
+ git_blob_filter_options *opts,
+ unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version,
+ git_blob_filter_options, GIT_BLOB_FILTER_OPTIONS_INIT);
+ return 0;
}
int git_blob_filter(
int error = 0;
git_filter_list *fl = NULL;
git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
- git_filter_flag_t flags = GIT_FILTER_DEFAULT;
-
- assert(blob && path && out);
+ git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
- git_buf_sanitize(out);
+ GIT_ASSERT_ARG(blob);
+ GIT_ASSERT_ARG(path);
+ GIT_ASSERT_ARG(out);
GIT_ERROR_CHECK_VERSION(
given_opts, GIT_BLOB_FILTER_OPTIONS_VERSION, "git_blob_filter_options");
+ if (git_buf_sanitize(out) < 0)
+ return -1;
+
if (given_opts != NULL)
memcpy(&opts, given_opts, sizeof(git_blob_filter_options));
return 0;
if ((opts.flags & GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
- flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES;
+ filter_opts.flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES;
+
+ if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
+ filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD;
- if ((opts.flags & GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD) != 0)
- flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD;
+ if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) {
+ filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_COMMIT;
+
+#ifndef GIT_DEPRECATE_HARD
+ if (opts.commit_id)
+ git_oid_cpy(&filter_opts.attr_commit_id, opts.commit_id);
+ else
+#endif
+ git_oid_cpy(&filter_opts.attr_commit_id, &opts.attr_commit_id);
+ }
- if (!(error = git_filter_list_load(
+ if (!(error = git_filter_list_load_ext(
&fl, git_blob_owner(blob), blob, path,
- GIT_FILTER_TO_WORKTREE, flags))) {
+ GIT_FILTER_TO_WORKTREE, &filter_opts))) {
error = git_filter_list_apply_to_blob(out, fl, blob);
int error = -1;
int bare = git_repository_is_bare(repository);
- assert(branch_name && commit && ref_out);
- assert(git_object_owner((const git_object *)commit) == repository);
+ GIT_ASSERT_ARG(branch_name);
+ GIT_ASSERT_ARG(commit);
+ GIT_ASSERT_ARG(ref_out);
+ GIT_ASSERT_ARG(git_commit_owner(commit) == repository);
if (!git__strcmp(branch_name, "HEAD")) {
git_error_set(GIT_ERROR_REFERENCE, "'HEAD' is not a valid branch name");
int git_branch_is_checked_out(const git_reference *branch)
{
+ GIT_ASSERT_ARG(branch);
+
if (!git_reference_is_branch(branch))
return 0;
return git_repository_foreach_worktree(git_reference_owner(branch),
git_buf config_section = GIT_BUF_INIT;
int error = -1;
- assert(branch);
+ GIT_ASSERT_ARG(branch);
if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
git_error_set(GIT_ERROR_INVALID, "reference '%s' is not a valid branch.",
log_message = GIT_BUF_INIT;
int error;
- assert(branch && new_branch_name);
+ GIT_ASSERT_ARG(branch);
+ GIT_ASSERT_ARG(new_branch_name);
if (!git_reference_is_branch(branch))
return not_a_local_branch(git_reference_name(branch));
git_branch_t branch_type)
{
int error = -1;
- assert(ref_out && repo && branch_name);
+
+ GIT_ASSERT_ARG(ref_out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(branch_name);
switch (branch_type) {
case GIT_BRANCH_LOCAL:
error = retrieve_branch_reference(ref_out, repo, branch_name, true);
break;
default:
- assert(false);
+ GIT_ASSERT(false);
}
return error;
}
{
const char *branch_name;
- assert(out && ref);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref);
branch_name = ref->name;
const git_refspec *refspec;
git_config *config;
- assert(out && refname);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(refname);
- git_buf_sanitize(out);
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
if (!git_reference__is_branch(refname))
return not_a_local_branch(refname);
return error;
}
-int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
+static int git_branch_upstream_with_format(git_buf *buf, git_repository *repo, const char *refname, const char *format, const char *format_name)
{
int error;
git_config *cfg;
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
return error;
- git_buf_sanitize(buf);
-
- if ((error = retrieve_upstream_configuration(buf, cfg, refname, "branch.%s.remote")) < 0)
+ if ((error = git_buf_sanitize(buf)) < 0 ||
+ (error = retrieve_upstream_configuration(buf, cfg, refname, format)) < 0)
return error;
if (git_buf_len(buf) == 0) {
- git_error_set(GIT_ERROR_REFERENCE, "branch '%s' does not have an upstream remote", refname);
+ git_error_set(GIT_ERROR_REFERENCE, "branch '%s' does not have an upstream %s", refname, format_name);
error = GIT_ENOTFOUND;
git_buf_clear(buf);
}
return error;
}
+int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
+{
+ return git_branch_upstream_with_format(buf, repo, refname, "branch.%s.remote", "remote");
+}
+
+int git_branch_upstream_merge(git_buf *buf, git_repository *repo, const char *refname)
+{
+ return git_branch_upstream_with_format(buf, repo, refname, "branch.%s.merge", "merge");
+}
+
int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname)
{
git_strarray remote_list = {0};
int error = 0;
char *remote_name = NULL;
- assert(buf && repo && refname);
+ GIT_ASSERT_ARG(buf);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
- git_buf_sanitize(buf);
+ if ((error = git_buf_sanitize(buf)) < 0)
+ return error;
/* Verify that this is a remote branch */
if (!git_reference__is_remote(refname)) {
bool is_same = false;
int error;
- assert(branch);
+ GIT_ASSERT_ARG(branch);
if (!git_reference_is_branch(branch))
return false;
return is_same;
}
+
+int git_branch_name_is_valid(int *valid, const char *name)
+{
+ git_buf ref_name = GIT_BUF_INIT;
+ int error = 0;
+
+ GIT_ASSERT(valid);
+
+ *valid = 0;
+
+ /*
+ * Discourage branch name starting with dash,
+ * https://github.com/git/git/commit/6348624010888b
+ * and discourage HEAD as branch name,
+ * https://github.com/git/git/commit/a625b092cc5994
+ */
+ if (!name || name[0] == '-' || !git__strcmp(name, "HEAD"))
+ goto done;
+
+ if ((error = git_buf_puts(&ref_name, GIT_REFS_HEADS_DIR)) < 0 ||
+ (error = git_buf_puts(&ref_name, name)) < 0)
+ goto done;
+
+ error = git_reference_name_is_valid(valid, ref_name.ptr);
+
+done:
+ git_buf_dispose(&ref_name);
+ return error;
+}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "buf_text.h"
-
-int git_buf_text_puts_escaped(
- git_buf *buf,
- const char *string,
- const char *esc_chars,
- const char *esc_with)
-{
- const char *scan;
- size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
-
- if (!string)
- return 0;
-
- for (scan = string; *scan; ) {
- /* count run of non-escaped characters */
- count = strcspn(scan, esc_chars);
- total += count;
- scan += count;
- /* count run of escaped characters */
- count = strspn(scan, esc_chars);
- total += count * (esc_len + 1);
- scan += count;
- }
-
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, total, 1);
- if (git_buf_grow_by(buf, alloclen) < 0)
- return -1;
-
- for (scan = string; *scan; ) {
- count = strcspn(scan, esc_chars);
-
- memmove(buf->ptr + buf->size, scan, count);
- scan += count;
- buf->size += count;
-
- for (count = strspn(scan, esc_chars); count > 0; --count) {
- /* copy escape sequence */
- memmove(buf->ptr + buf->size, esc_with, esc_len);
- buf->size += esc_len;
- /* copy character to be escaped */
- buf->ptr[buf->size] = *scan;
- buf->size++;
- scan++;
- }
- }
-
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-void git_buf_text_unescape(git_buf *buf)
-{
- buf->size = git__unescape(buf->ptr);
-}
-
-int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
-{
- const char *scan = src->ptr;
- const char *scan_end = src->ptr + src->size;
- const char *next = memchr(scan, '\r', src->size);
- size_t new_size;
- char *out;
-
- assert(tgt != src);
-
- if (!next)
- return git_buf_set(tgt, src->ptr, src->size);
-
- /* reduce reallocs while in the loop */
- GIT_ERROR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
- if (git_buf_grow(tgt, new_size) < 0)
- return -1;
-
- out = tgt->ptr;
- tgt->size = 0;
-
- /* Find the next \r and copy whole chunk up to there to tgt */
- for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
- if (next > scan) {
- size_t copylen = (size_t)(next - scan);
- memcpy(out, scan, copylen);
- out += copylen;
- }
-
- /* Do not drop \r unless it is followed by \n */
- if (next + 1 == scan_end || next[1] != '\n')
- *out++ = '\r';
- }
-
- /* Copy remaining input into dest */
- if (scan < scan_end) {
- size_t remaining = (size_t)(scan_end - scan);
- memcpy(out, scan, remaining);
- out += remaining;
- }
-
- tgt->size = (size_t)(out - tgt->ptr);
- tgt->ptr[tgt->size] = '\0';
-
- return 0;
-}
-
-int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
-{
- const char *start = src->ptr;
- const char *end = start + src->size;
- const char *scan = start;
- const char *next = memchr(scan, '\n', src->size);
- size_t alloclen;
-
- assert(tgt != src);
-
- if (!next)
- return git_buf_set(tgt, src->ptr, src->size);
-
- /* attempt to reduce reallocs while in the loop */
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
- if (git_buf_grow(tgt, alloclen) < 0)
- return -1;
- tgt->size = 0;
-
- for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
- size_t copylen = next - scan;
-
- /* if we find mixed line endings, carry on */
- if (copylen && next[-1] == '\r')
- copylen--;
-
- GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
- if (git_buf_grow_by(tgt, alloclen) < 0)
- return -1;
-
- if (copylen) {
- memcpy(tgt->ptr + tgt->size, scan, copylen);
- tgt->size += copylen;
- }
-
- tgt->ptr[tgt->size++] = '\r';
- tgt->ptr[tgt->size++] = '\n';
- }
-
- tgt->ptr[tgt->size] = '\0';
- return git_buf_put(tgt, scan, end - scan);
-}
-
-int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings)
-{
- size_t i;
- const char *str, *pfx;
-
- git_buf_clear(buf);
-
- if (!strings || !strings->count)
- return 0;
-
- /* initialize common prefix to first string */
- if (git_buf_sets(buf, strings->strings[0]) < 0)
- return -1;
-
- /* go through the rest of the strings, truncating to shared prefix */
- for (i = 1; i < strings->count; ++i) {
-
- for (str = strings->strings[i], pfx = buf->ptr;
- *str && *str == *pfx; str++, pfx++)
- /* scanning */;
-
- git_buf_truncate(buf, pfx - buf->ptr);
-
- if (!buf->size)
- break;
- }
-
- return 0;
-}
-
-bool git_buf_text_is_binary(const git_buf *buf)
-{
- const char *scan = buf->ptr, *end = buf->ptr + buf->size;
- git_bom_t bom;
- int printable = 0, nonprintable = 0;
-
- scan += git_buf_text_detect_bom(&bom, buf);
-
- if (bom > GIT_BOM_UTF8)
- return 1;
-
- while (scan < end) {
- unsigned char c = *scan++;
-
- /* Printable characters are those above SPACE (0x1F) excluding DEL,
- * and including BS, ESC and FF.
- */
- if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014')
- printable++;
- else if (c == '\0')
- return true;
- else if (!git__isspace(c))
- nonprintable++;
- }
-
- return ((printable >> 7) < nonprintable);
-}
-
-bool git_buf_text_contains_nul(const git_buf *buf)
-{
- return (memchr(buf->ptr, '\0', buf->size) != NULL);
-}
-
-int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf)
-{
- const char *ptr;
- size_t len;
-
- *bom = GIT_BOM_NONE;
- /* need at least 2 bytes to look for any BOM */
- if (buf->size < 2)
- return 0;
-
- ptr = buf->ptr;
- len = buf->size;
-
- switch (*ptr++) {
- case 0:
- if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') {
- *bom = GIT_BOM_UTF32_BE;
- return 4;
- }
- break;
- case '\xEF':
- if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') {
- *bom = GIT_BOM_UTF8;
- return 3;
- }
- break;
- case '\xFE':
- if (*ptr == '\xFF') {
- *bom = GIT_BOM_UTF16_BE;
- return 2;
- }
- break;
- case '\xFF':
- if (*ptr != '\xFE')
- break;
- if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) {
- *bom = GIT_BOM_UTF32_LE;
- return 4;
- } else {
- *bom = GIT_BOM_UTF16_LE;
- return 2;
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-bool git_buf_text_gather_stats(
- git_buf_text_stats *stats, const git_buf *buf, bool skip_bom)
-{
- const char *scan = buf->ptr, *end = buf->ptr + buf->size;
- int skip;
-
- memset(stats, 0, sizeof(*stats));
-
- /* BOM detection */
- skip = git_buf_text_detect_bom(&stats->bom, buf);
- if (skip_bom)
- scan += skip;
-
- /* Ignore EOF character */
- if (buf->size > 0 && end[-1] == '\032')
- end--;
-
- /* Counting loop */
- while (scan < end) {
- unsigned char c = *scan++;
-
- if (c > 0x1F && c != 0x7F)
- stats->printable++;
- else switch (c) {
- case '\0':
- stats->nul++;
- stats->nonprintable++;
- break;
- case '\n':
- stats->lf++;
- break;
- case '\r':
- stats->cr++;
- if (scan < end && *scan == '\n')
- stats->crlf++;
- break;
- case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
- stats->printable++;
- break;
- default:
- stats->nonprintable++;
- break;
- }
- }
-
- /* Treat files with a bare CR as binary */
- return (stats->cr != stats->crlf || stats->nul > 0 ||
- ((stats->printable >> 7) < stats->nonprintable));
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_buf_text_h__
-#define INCLUDE_buf_text_h__
-
-#include "common.h"
-
-#include "buffer.h"
-
-typedef enum {
- GIT_BOM_NONE = 0,
- GIT_BOM_UTF8 = 1,
- GIT_BOM_UTF16_LE = 2,
- GIT_BOM_UTF16_BE = 3,
- GIT_BOM_UTF32_LE = 4,
- GIT_BOM_UTF32_BE = 5
-} git_bom_t;
-
-typedef struct {
- git_bom_t bom; /* BOM found at head of text */
- unsigned int nul, cr, lf, crlf; /* NUL, CR, LF and CRLF counts */
- unsigned int printable, nonprintable; /* These are just approximations! */
-} git_buf_text_stats;
-
-/**
- * Append string to buffer, prefixing each character from `esc_chars` with
- * `esc_with` string.
- *
- * @param buf Buffer to append data to
- * @param string String to escape and append
- * @param esc_chars Characters to be escaped
- * @param esc_with String to insert in from of each found character
- * @return 0 on success, <0 on failure (probably allocation problem)
- */
-extern int git_buf_text_puts_escaped(
- git_buf *buf,
- const char *string,
- const char *esc_chars,
- const char *esc_with);
-
-/**
- * Append string escaping characters that are regex special
- */
-GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
-{
- return git_buf_text_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\");
-}
-
-/**
- * Unescape all characters in a buffer in place
- *
- * I.e. remove backslashes
- */
-extern void git_buf_text_unescape(git_buf *buf);
-
-/**
- * Replace all \r\n with \n.
- *
- * @return 0 on success, -1 on memory error
- */
-extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
-
-/**
- * Replace all \n with \r\n. Does not modify existing \r\n.
- *
- * @return 0 on success, -1 on memory error
- */
-extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
-
-/**
- * Fill buffer with the common prefix of a array of strings
- *
- * Buffer will be set to empty if there is no common prefix
- */
-extern int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strs);
-
-/**
- * Check quickly if buffer looks like it contains binary data
- *
- * @param buf Buffer to check
- * @return true if buffer looks like non-text data
- */
-extern bool git_buf_text_is_binary(const git_buf *buf);
-
-/**
- * Check quickly if buffer contains a NUL byte
- *
- * @param buf Buffer to check
- * @return true if buffer contains a NUL byte
- */
-extern bool git_buf_text_contains_nul(const git_buf *buf);
-
-/**
- * Check if a buffer begins with a UTF BOM
- *
- * @param bom Set to the type of BOM detected or GIT_BOM_NONE
- * @param buf Buffer in which to check the first bytes for a BOM
- * @return Number of bytes of BOM data (or 0 if no BOM found)
- */
-extern int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf);
-
-/**
- * Gather stats for a piece of text
- *
- * Fill the `stats` structure with counts of unreadable characters, carriage
- * returns, etc, so it can be used in heuristics. This automatically skips
- * a trailing EOF (\032 character). Also it will look for a BOM at the
- * start of the text and can be told to skip that as well.
- *
- * @param stats Structure to be filled in
- * @param buf Text to process
- * @param skip_bom Exclude leading BOM from stats if true
- * @return Does the buffer heuristically look like binary data
- */
-extern bool git_buf_text_gather_stats(
- git_buf_text_stats *stats, const git_buf *buf, bool skip_bom);
-
-#endif
#include "buffer.h"
#include "posix.h"
#include "git2/buffer.h"
-#include "buf_text.h"
#include <ctype.h>
/* Used as default value for git_buf->ptr so that people can always
}
#endif
-void git_buf_sanitize(git_buf *buf)
+int git_buf_sanitize(git_buf *buf)
{
if (buf->ptr == NULL) {
- assert(buf->size == 0 && buf->asize == 0);
+ GIT_ASSERT_ARG(buf->size == 0 && buf->asize == 0);
+
buf->ptr = git_buf__initbuf;
- } else if (buf->asize > buf->size)
+ } else if (buf->asize > buf->size) {
buf->ptr[buf->size] = '\0';
+ }
+
+ return 0;
}
void git_buf_clear(git_buf *buf)
return 0;
}
-int git_buf_is_binary(const git_buf *buf)
-{
- return git_buf_text_is_binary(buf);
-}
-
-int git_buf_contains_nul(const git_buf *buf)
-{
- return git_buf_text_contains_nul(buf);
-}
-
int git_buf_sets(git_buf *buf, const char *string)
{
return git_buf_set(buf, string, string ? strlen(string) : 0);
if (len) {
size_t new_size;
- assert(data);
+ GIT_ASSERT_ARG(data);
GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
int git_buf_puts(git_buf *buf, const char *string)
{
- assert(string);
+ GIT_ASSERT_ARG(string);
+
return git_buf_put(buf, string, strlen(string));
}
return -1;
}
- assert(len % 4 == 0);
+ GIT_ASSERT_ARG(len % 4 == 0);
GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
ENSURE_SIZE(buf, new_size);
return r;
}
-void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
+int git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
{
size_t copylen;
- assert(data && datasize && buf);
+ GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(datasize);
+ GIT_ASSERT_ARG(buf);
data[0] = '\0';
if (buf->size == 0 || buf->asize <= 0)
- return;
+ return 0;
copylen = buf->size;
if (copylen > datasize - 1)
copylen = datasize - 1;
memmove(data, buf->ptr, copylen);
data[copylen] = '\0';
+
+ return 0;
}
void git_buf_consume_bytes(git_buf *buf, size_t len)
git_buf_clear(buf);
}
+void git_buf_truncate_at_char(git_buf *buf, char separator)
+{
+ ssize_t idx = git_buf_find(buf, separator);
+ if (idx >= 0)
+ git_buf_truncate(buf, (size_t)idx);
+}
+
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
{
ssize_t idx = git_buf_rfind_next(buf, separator);
va_start(ap, nbuf);
for (i = 0; i < nbuf; ++i) {
- const char* segment;
+ const char *segment;
size_t segment_len;
segment = va_arg(ap, const char *);
va_start(ap, nbuf);
for (i = 0; i < nbuf; ++i) {
- const char* segment;
+ const char *segment;
size_t segment_len;
segment = va_arg(ap, const char *);
/* not safe to have str_b point internally to the buffer */
if (buf->size)
- assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
+ GIT_ASSERT_ARG(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
/* figure out if we need to insert a separator */
if (separator && strlen_a) {
char *tgt;
/* for this function, disallow pointers into the existing buffer */
- assert(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
- assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
- assert(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
+ GIT_ASSERT(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
+ GIT_ASSERT(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
+ GIT_ASSERT(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
if (separator) {
if (len_a > 0) {
char *splice_loc;
size_t new_size, alloc_size;
- assert(buf && where <= buf->size && nb_to_remove <= buf->size - where);
+ GIT_ASSERT(buf);
+ GIT_ASSERT(where <= buf->size);
+ GIT_ASSERT(nb_to_remove <= buf->size - where);
splice_loc = buf->ptr + where;
git_error_set(GIT_ERROR_INVALID, "invalid quoted line");
return -1;
}
+
+int git_buf_puts_escaped(
+ git_buf *buf,
+ const char *string,
+ const char *esc_chars,
+ const char *esc_with)
+{
+ const char *scan;
+ size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
+
+ if (!string)
+ return 0;
+
+ for (scan = string; *scan; ) {
+ /* count run of non-escaped characters */
+ count = strcspn(scan, esc_chars);
+ total += count;
+ scan += count;
+ /* count run of escaped characters */
+ count = strspn(scan, esc_chars);
+ total += count * (esc_len + 1);
+ scan += count;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, total, 1);
+ if (git_buf_grow_by(buf, alloclen) < 0)
+ return -1;
+
+ for (scan = string; *scan; ) {
+ count = strcspn(scan, esc_chars);
+
+ memmove(buf->ptr + buf->size, scan, count);
+ scan += count;
+ buf->size += count;
+
+ for (count = strspn(scan, esc_chars); count > 0; --count) {
+ /* copy escape sequence */
+ memmove(buf->ptr + buf->size, esc_with, esc_len);
+ buf->size += esc_len;
+ /* copy character to be escaped */
+ buf->ptr[buf->size] = *scan;
+ buf->size++;
+ scan++;
+ }
+ }
+
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
+void git_buf_unescape(git_buf *buf)
+{
+ buf->size = git__unescape(buf->ptr);
+}
+
+int git_buf_crlf_to_lf(git_buf *tgt, const git_buf *src)
+{
+ const char *scan = src->ptr;
+ const char *scan_end = src->ptr + src->size;
+ const char *next = memchr(scan, '\r', src->size);
+ size_t new_size;
+ char *out;
+
+ GIT_ASSERT(tgt != src);
+
+ if (!next)
+ return git_buf_set(tgt, src->ptr, src->size);
+
+ /* reduce reallocs while in the loop */
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
+ if (git_buf_grow(tgt, new_size) < 0)
+ return -1;
+
+ out = tgt->ptr;
+ tgt->size = 0;
+
+ /* Find the next \r and copy whole chunk up to there to tgt */
+ for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
+ if (next > scan) {
+ size_t copylen = (size_t)(next - scan);
+ memcpy(out, scan, copylen);
+ out += copylen;
+ }
+
+ /* Do not drop \r unless it is followed by \n */
+ if (next + 1 == scan_end || next[1] != '\n')
+ *out++ = '\r';
+ }
+
+ /* Copy remaining input into dest */
+ if (scan < scan_end) {
+ size_t remaining = (size_t)(scan_end - scan);
+ memcpy(out, scan, remaining);
+ out += remaining;
+ }
+
+ tgt->size = (size_t)(out - tgt->ptr);
+ tgt->ptr[tgt->size] = '\0';
+
+ return 0;
+}
+
+int git_buf_lf_to_crlf(git_buf *tgt, const git_buf *src)
+{
+ const char *start = src->ptr;
+ const char *end = start + src->size;
+ const char *scan = start;
+ const char *next = memchr(scan, '\n', src->size);
+ size_t alloclen;
+
+ GIT_ASSERT(tgt != src);
+
+ if (!next)
+ return git_buf_set(tgt, src->ptr, src->size);
+
+ /* attempt to reduce reallocs while in the loop */
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ if (git_buf_grow(tgt, alloclen) < 0)
+ return -1;
+ tgt->size = 0;
+
+ for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
+ size_t copylen = next - scan;
+
+ /* if we find mixed line endings, carry on */
+ if (copylen && next[-1] == '\r')
+ copylen--;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
+ if (git_buf_grow_by(tgt, alloclen) < 0)
+ return -1;
+
+ if (copylen) {
+ memcpy(tgt->ptr + tgt->size, scan, copylen);
+ tgt->size += copylen;
+ }
+
+ tgt->ptr[tgt->size++] = '\r';
+ tgt->ptr[tgt->size++] = '\n';
+ }
+
+ tgt->ptr[tgt->size] = '\0';
+ return git_buf_put(tgt, scan, end - scan);
+}
+
+int git_buf_common_prefix(git_buf *buf, char *const *const strings, size_t count)
+{
+ size_t i;
+ const char *str, *pfx;
+
+ git_buf_clear(buf);
+
+ if (!strings || !count)
+ return 0;
+
+ /* initialize common prefix to first string */
+ if (git_buf_sets(buf, strings[0]) < 0)
+ return -1;
+
+ /* go through the rest of the strings, truncating to shared prefix */
+ for (i = 1; i < count; ++i) {
+
+ for (str = strings[i], pfx = buf->ptr;
+ *str && *str == *pfx;
+ str++, pfx++)
+ /* scanning */;
+
+ git_buf_truncate(buf, pfx - buf->ptr);
+
+ if (!buf->size)
+ break;
+ }
+
+ return 0;
+}
+
+int git_buf_is_binary(const git_buf *buf)
+{
+ const char *scan = buf->ptr, *end = buf->ptr + buf->size;
+ git_buf_bom_t bom;
+ int printable = 0, nonprintable = 0;
+
+ scan += git_buf_detect_bom(&bom, buf);
+
+ if (bom > GIT_BUF_BOM_UTF8)
+ return 1;
+
+ while (scan < end) {
+ unsigned char c = *scan++;
+
+ /* Printable characters are those above SPACE (0x1F) excluding DEL,
+ * and including BS, ESC and FF.
+ */
+ if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014')
+ printable++;
+ else if (c == '\0')
+ return true;
+ else if (!git__isspace(c))
+ nonprintable++;
+ }
+
+ return ((printable >> 7) < nonprintable);
+}
+
+int git_buf_contains_nul(const git_buf *buf)
+{
+ return (memchr(buf->ptr, '\0', buf->size) != NULL);
+}
+
+int git_buf_detect_bom(git_buf_bom_t *bom, const git_buf *buf)
+{
+ const char *ptr;
+ size_t len;
+
+ *bom = GIT_BUF_BOM_NONE;
+ /* need at least 2 bytes to look for any BOM */
+ if (buf->size < 2)
+ return 0;
+
+ ptr = buf->ptr;
+ len = buf->size;
+
+ switch (*ptr++) {
+ case 0:
+ if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') {
+ *bom = GIT_BUF_BOM_UTF32_BE;
+ return 4;
+ }
+ break;
+ case '\xEF':
+ if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') {
+ *bom = GIT_BUF_BOM_UTF8;
+ return 3;
+ }
+ break;
+ case '\xFE':
+ if (*ptr == '\xFF') {
+ *bom = GIT_BUF_BOM_UTF16_BE;
+ return 2;
+ }
+ break;
+ case '\xFF':
+ if (*ptr != '\xFE')
+ break;
+ if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) {
+ *bom = GIT_BUF_BOM_UTF32_LE;
+ return 4;
+ } else {
+ *bom = GIT_BUF_BOM_UTF16_LE;
+ return 2;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+bool git_buf_gather_text_stats(
+ git_buf_text_stats *stats, const git_buf *buf, bool skip_bom)
+{
+ const char *scan = buf->ptr, *end = buf->ptr + buf->size;
+ int skip;
+
+ memset(stats, 0, sizeof(*stats));
+
+ /* BOM detection */
+ skip = git_buf_detect_bom(&stats->bom, buf);
+ if (skip_bom)
+ scan += skip;
+
+ /* Ignore EOF character */
+ if (buf->size > 0 && end[-1] == '\032')
+ end--;
+
+ /* Counting loop */
+ while (scan < end) {
+ unsigned char c = *scan++;
+
+ if (c > 0x1F && c != 0x7F)
+ stats->printable++;
+ else switch (c) {
+ case '\0':
+ stats->nul++;
+ stats->nonprintable++;
+ break;
+ case '\n':
+ stats->lf++;
+ break;
+ case '\r':
+ stats->cr++;
+ if (scan < end && *scan == '\n')
+ stats->crlf++;
+ break;
+ case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
+ stats->printable++;
+ break;
+ default:
+ stats->nonprintable++;
+ break;
+ }
+ }
+
+ /* Treat files with a bare CR as binary */
+ return (stats->cr != stats->crlf || stats->nul > 0 ||
+ ((stats->printable >> 7) < stats->nonprintable));
+}
* } git_buf;
*/
+typedef enum {
+ GIT_BUF_BOM_NONE = 0,
+ GIT_BUF_BOM_UTF8 = 1,
+ GIT_BUF_BOM_UTF16_LE = 2,
+ GIT_BUF_BOM_UTF16_BE = 3,
+ GIT_BUF_BOM_UTF32_LE = 4,
+ GIT_BUF_BOM_UTF32_BE = 5
+} git_buf_bom_t;
+
+typedef struct {
+ git_buf_bom_t bom; /* BOM found at head of text */
+ unsigned int nul, cr, lf, crlf; /* NUL, CR, LF and CRLF counts */
+ unsigned int printable, nonprintable; /* These are just approximations! */
+} git_buf_text_stats;
+
extern char git_buf__initbuf[];
extern char git_buf__oom[];
/* Use to initialize buffer structure when git_buf is on stack */
#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
+/**
+ * Static initializer for git_buf from static buffer
+ */
+#ifdef GIT_DEPRECATE_HARD
+# define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) }
+#endif
+
GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf)
{
return (buf->ptr != NULL && buf->asize > 0);
*/
extern int git_buf_init(git_buf *buf, size_t initial_size);
+#ifdef GIT_DEPRECATE_HARD
+
+/**
+ * Resize the buffer allocation to make more space.
+ *
+ * This will attempt to grow the buffer to accommodate the target size.
+ *
+ * If the buffer refers to memory that was not allocated by libgit2 (i.e.
+ * the `asize` field is zero), then `ptr` will be replaced with a newly
+ * allocated block of data. Be careful so that memory allocated by the
+ * caller is not lost. As a special variant, if you pass `target_size` as
+ * 0 and the memory is not allocated by libgit2, this will allocate a new
+ * buffer of size `size` and copy the external data into it.
+ *
+ * Currently, this will never shrink a buffer, only expand it.
+ *
+ * If the allocation fails, this will return an error and the buffer will be
+ * marked as invalid for future operations, invaliding the contents.
+ *
+ * @param buffer The buffer to be resized; may or may not be allocated yet
+ * @param target_size The desired available size
+ * @return 0 on success, -1 on allocation failure
+ */
+int git_buf_grow(git_buf *buffer, size_t target_size);
+
+#endif
+
/**
* Resize the buffer allocation to make more space.
*
* git_buf__initbuf. If a buffer with a non-NULL ptr is passed in, this method
* assures that the buffer is '\0'-terminated.
*/
-extern void git_buf_sanitize(git_buf *buf);
+extern int git_buf_sanitize(git_buf *buf);
extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
extern char *git_buf_detach(git_buf *buf);
* return code of these functions and call them in a series then just call
* git_buf_oom at the end.
*/
+
+#ifdef GIT_DEPRECATE_HARD
+int git_buf_set(git_buf *buffer, const void *data, size_t datalen);
+#endif
+
int git_buf_sets(git_buf *buf, const char *string);
int git_buf_putc(git_buf *buf, char c);
int git_buf_putcn(git_buf *buf, char c, size_t len);
void git_buf_consume(git_buf *buf, const char *end);
void git_buf_truncate(git_buf *buf, size_t len);
void git_buf_shorten(git_buf *buf, size_t amount);
+void git_buf_truncate_at_char(git_buf *buf, char separator);
void git_buf_rtruncate_at_char(git_buf *path, char separator);
/** General join with separator */
return buf->size;
}
-void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
+int git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
const char *data,
size_t nb_to_insert);
+/**
+ * Append string to buffer, prefixing each character from `esc_chars` with
+ * `esc_with` string.
+ *
+ * @param buf Buffer to append data to
+ * @param string String to escape and append
+ * @param esc_chars Characters to be escaped
+ * @param esc_with String to insert in from of each found character
+ * @return 0 on success, <0 on failure (probably allocation problem)
+ */
+extern int git_buf_puts_escaped(
+ git_buf *buf,
+ const char *string,
+ const char *esc_chars,
+ const char *esc_with);
+
+/**
+ * Append string escaping characters that are regex special
+ */
+GIT_INLINE(int) git_buf_puts_escape_regex(git_buf *buf, const char *string)
+{
+ return git_buf_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\");
+}
+
+/**
+ * Unescape all characters in a buffer in place
+ *
+ * I.e. remove backslashes
+ */
+extern void git_buf_unescape(git_buf *buf);
+
+/**
+ * Replace all \r\n with \n.
+ *
+ * @return 0 on success, -1 on memory error
+ */
+extern int git_buf_crlf_to_lf(git_buf *tgt, const git_buf *src);
+
+/**
+ * Replace all \n with \r\n. Does not modify existing \r\n.
+ *
+ * @return 0 on success, -1 on memory error
+ */
+extern int git_buf_lf_to_crlf(git_buf *tgt, const git_buf *src);
+
+/**
+ * Fill buffer with the common prefix of a array of strings
+ *
+ * Buffer will be set to empty if there is no common prefix
+ */
+extern int git_buf_common_prefix(git_buf *buf, char *const *const strings, size_t count);
+
+/**
+ * Check if a buffer begins with a UTF BOM
+ *
+ * @param bom Set to the type of BOM detected or GIT_BOM_NONE
+ * @param buf Buffer in which to check the first bytes for a BOM
+ * @return Number of bytes of BOM data (or 0 if no BOM found)
+ */
+extern int git_buf_detect_bom(git_buf_bom_t *bom, const git_buf *buf);
+
+/**
+ * Gather stats for a piece of text
+ *
+ * Fill the `stats` structure with counts of unreadable characters, carriage
+ * returns, etc, so it can be used in heuristics. This automatically skips
+ * a trailing EOF (\032 character). Also it will look for a BOM at the
+ * start of the text and can be told to skip that as well.
+ *
+ * @param stats Structure to be filled in
+ * @param buf Text to process
+ * @param skip_bom Exclude leading BOM from stats if true
+ * @return Does the buffer heuristically look like binary data
+ */
+extern bool git_buf_gather_text_stats(
+ git_buf_text_stats *stats, const git_buf *buf, bool skip_bom);
+
+#ifdef GIT_DEPRECATE_HARD
+
+/**
+* Check quickly if buffer looks like it contains binary data
+*
+* @param buf Buffer to check
+* @return 1 if buffer looks like non-text data
+*/
+int git_buf_is_binary(const git_buf *buf);
+
+/**
+* Check quickly if buffer contains a NUL byte
+*
+* @param buf Buffer to check
+* @return 1 if buffer contains a NUL byte
+*/
+int git_buf_contains_nul(const git_buf *buf);
+
+#endif
+
#endif
#include "repository.h"
#include "commit.h"
-#include "thread-utils.h"
+#include "thread.h"
#include "util.h"
#include "odb.h"
#include "object.h"
{
git_cached_obj *obj = _obj;
- if (git_atomic_dec(&obj->refcount) == 0) {
+ if (git_atomic32_dec(&obj->refcount) == 0) {
switch (obj->flags) {
case GIT_CACHE_STORE_RAW:
git_odb_object__free(_obj);
#include "git2/oid.h"
#include "git2/odb.h"
-#include "thread-utils.h"
+#include "thread.h"
#include "oidmap.h"
enum {
};
typedef struct {
- git_oid oid;
- int16_t type; /* git_object_t value */
- uint16_t flags; /* GIT_CACHE_STORE value */
- size_t size;
- git_atomic refcount;
+ git_oid oid;
+ int16_t type; /* git_object_t value */
+ uint16_t flags; /* GIT_CACHE_STORE value */
+ size_t size;
+ git_atomic32 refcount;
} git_cached_obj;
typedef struct {
GIT_INLINE(void) git_cached_obj_incref(void *_obj)
{
git_cached_obj *obj = _obj;
- git_atomic_inc(&obj->refcount);
+ git_atomic32_inc(&obj->refcount);
}
void git_cached_obj_decref(void *_obj);
# endif
#endif
-#ifdef __GNUC__
-# define GIT_TYPEOF(x) (__typeof__(x))
-#else
-# define GIT_TYPEOF(x)
-#endif
-
#if defined(__GNUC__)
# define GIT_ALIGN(x,size) x __attribute__ ((aligned(size)))
#elif defined(_MSC_VER)
# define GIT_ALIGN(x,size) x
#endif
-#define GIT_UNUSED(x) ((void)(x))
+#if defined(__GNUC__)
+# define GIT_UNUSED(x) \
+ do { \
+ __typeof__(x) _unused __attribute__((unused)); \
+ _unused = (x); \
+ } while (0)
+#else
+# define GIT_UNUSED(x) ((void)(x))
+#endif
-/* Define the printf format specifer to use for size_t output */
+/* Define the printf format specifier to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
/* Visual Studio 2012 and prior lack PRId64 entirely */
#include "diff.h"
#include "diff_generate.h"
#include "pathspec.h"
-#include "buf_text.h"
#include "diff_xdiff.h"
#include "path.h"
#include "attr.h"
if (path && git_buf_puts(&data->target_path, path) < 0)
return -1;
+ if (git_path_validate_workdir_buf(data->repo, &data->target_path) < 0)
+ return -1;
+
*out = &data->target_path;
return 0;
checkout_data *data = payload;
const char *name;
- assert(ancestor || ours || theirs);
+ GIT_ASSERT_ARG(ancestor || ours || theirs);
if (ancestor)
name = git__strdup(ancestor->path);
unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS;
if (action & CHECKOUT_ACTION__REMOVE) {
- if (!git_path_isvalid(repo, delta->old_file.path, delta->old_file.mode, flags)) {
+ if (!git_path_validate(repo, delta->old_file.path, delta->old_file.mode, flags)) {
git_error_set(GIT_ERROR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path);
return -1;
}
}
if (action & ~CHECKOUT_ACTION__REMOVE) {
- if (!git_path_isvalid(repo, delta->new_file.path, delta->new_file.mode, flags)) {
+ if (!git_path_validate(repo, delta->new_file.path, delta->new_file.mode, flags)) {
git_error_set(GIT_ERROR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path);
return -1;
}
static int checkout_stream_close(git_writestream *s)
{
struct checkout_stream *stream = (struct checkout_stream *)s;
- assert(stream && stream->open);
+
+ GIT_ASSERT_ARG(stream);
+ GIT_ASSERT_ARG(stream->open);
stream->open = 0;
return p_close(stream->fd);
int flags = data->opts.file_open_flags;
mode_t file_mode = data->opts.file_mode ?
data->opts.file_mode : entry_filemode;
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
struct checkout_stream writer;
mode_t mode;
git_filter_list *fl = NULL;
int fd;
int error = 0;
- if (hint_path == NULL)
- hint_path = path;
+ GIT_ASSERT(hint_path != NULL);
if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
return error;
return fd;
}
- filter_opts.attr_session = &data->attr_session;
- filter_opts.temp_buf = &data->tmp;
+ filter_session.attr_session = &data->attr_session;
+ filter_session.temp_buf = &data->tmp;
if (!data->opts.disable_filters &&
- (error = git_filter_list__load_ext(
+ (error = git_filter_list__load(
&fl, data->repo, blob, hint_path,
- GIT_FILTER_TO_WORKTREE, &filter_opts))) {
+ GIT_FILTER_TO_WORKTREE, &filter_session))) {
p_close(fd);
return error;
}
error = git_filter_list_stream_blob(fl, blob, &writer.base);
- assert(writer.open == 0);
+ GIT_ASSERT(writer.open == 0);
git_filter_list_free(fl);
}
error = checkout_write_content(
- data, &file->id, fullpath->ptr, NULL, file->mode, &st);
+ data, &file->id, fullpath->ptr, file->path, file->mode, &st);
/* update the index unless prevented */
if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
checkout_conflictdata *conflict,
const git_index_entry *side)
{
- const char *hint_path = NULL, *suffix;
+ const char *hint_path, *suffix;
git_buf *fullpath;
struct stat st;
int error;
- assert (side == conflict->ours || side == conflict->theirs);
+ GIT_ASSERT(side == conflict->ours || side == conflict->theirs);
if (checkout_target_fullpath(&fullpath, data, side->path) < 0)
return -1;
if (checkout_path_suffixed(fullpath, suffix) < 0)
return -1;
-
- hint_path = side->path;
}
+ hint_path = side->path;
+
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
(error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0)
return error;
const char *our_label_raw, *their_label_raw, *suffix;
int error = 0;
- if ((error = git_buf_joinpath(out, git_repository_workdir(data->repo), result->path)) < 0)
+ if ((error = git_buf_joinpath(out, data->opts.target_directory, result->path)) < 0 ||
+ (error = git_path_validate_workdir_buf(data->repo, out)) < 0)
return error;
/* Most conflicts simply use the filename in the index */
git_merge_file_result result = {0};
git_filebuf output = GIT_FILEBUF_INIT;
git_filter_list *fl = NULL;
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
int error = 0;
if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
in_data.ptr = (char *)result.ptr;
in_data.size = result.len;
- filter_opts.attr_session = &data->attr_session;
- filter_opts.temp_buf = &data->tmp;
+ filter_session.attr_session = &data->attr_session;
+ filter_session.temp_buf = &data->tmp;
- if ((error = git_filter_list__load_ext(
- &fl, data->repo, NULL, git_buf_cstr(&path_workdir),
- GIT_FILTER_TO_WORKTREE, &filter_opts)) < 0 ||
- (error = git_filter_list_apply_to_data(&out_data, fl, &in_data)) < 0)
+ if ((error = git_filter_list__load(
+ &fl, data->repo, NULL, result.path,
+ GIT_FILTER_TO_WORKTREE, &filter_session)) < 0 ||
+ (error = git_filter_list__convert_buf(&out_data, fl, &in_data)) < 0)
goto done;
} else {
out_data.ptr = (char *)result.ptr;
git_attr_session__free(&data->attr_session);
}
+static int validate_target_directory(checkout_data *data)
+{
+ int error;
+
+ if ((error = git_path_validate_workdir(data->repo, data->opts.target_directory)) < 0)
+ return error;
+
+ if (git_path_isdir(data->opts.target_directory))
+ return 0;
+
+ error = checkout_mkdir(data, data->opts.target_directory, NULL,
+ GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR);
+
+ return error;
+}
+
static int checkout_data_init(
checkout_data *data,
git_iterator *target,
if (!data->opts.target_directory)
data->opts.target_directory = git_repository_workdir(repo);
- else if (!git_path_isdir(data->opts.target_directory) &&
- (error = checkout_mkdir(data,
- data->opts.target_directory, NULL,
- GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0)
+ else if ((error = validate_target_directory(data)) < 0)
goto cleanup;
if ((error = git_repository_index(&data->index, data->repo)) < 0)
}
/* Should not have case insensitivity mismatch */
- assert(git_iterator_ignore_case(workdir) == git_iterator_ignore_case(baseline));
+ GIT_ASSERT(git_iterator_ignore_case(workdir) == git_iterator_ignore_case(baseline));
/* Generate baseline-to-target diff which will include an entry for
* every possible update that might need to be made.
if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0)
goto cleanup;
+ if (data.strategy & GIT_CHECKOUT_DRY_RUN)
+ goto cleanup;
+
data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
counts[CHECKOUT_ACTION__REMOVE_CONFLICT] +
counts[CHECKOUT_ACTION__UPDATE_BLOB] +
(error = checkout_extensions_update_index(&data)) < 0)
goto cleanup;
- assert(data.completed_steps == data.total_steps);
+ GIT_ASSERT(data.completed_steps == data.total_steps);
if (data.opts.perfdata_cb)
data.opts.perfdata_cb(&data.perfdata, data.opts.perfdata_payload);
git_repository *repo,
const git_checkout_options *opts)
{
- assert(repo);
+ GIT_ASSERT_ARG(repo);
+
return git_checkout_tree(repo, NULL, opts);
}
git_tree *parent_tree = NULL, *our_tree = NULL, *cherrypick_tree = NULL;
int parent = 0, error = 0;
- assert(out && repo && cherrypick_commit && our_commit);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(cherrypick_commit);
+ GIT_ASSERT_ARG(our_commit);
if (git_commit_parentcount(cherrypick_commit) > 1) {
if (!mainline)
git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
int error = 0;
- assert(repo && commit);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(commit);
GIT_ERROR_CHECK_VERSION(given_opts, GIT_CHERRYPICK_OPTIONS_VERSION, "git_cherrypick_options");
/* We know we have HEAD, let's see where it points */
remote_head = refs[0];
- assert(remote_head);
+ GIT_ASSERT(remote_head);
remote_head_id = &remote_head->oid;
static int update_head_to_branch(
git_repository *repo,
- const char *remote_name,
+ git_remote *remote,
const char *branch,
const char *reflog_message)
{
int retcode;
git_buf remote_branch_name = GIT_BUF_INIT;
- git_reference* remote_ref = NULL;
+ git_reference *remote_ref = NULL;
+ git_buf default_branch = GIT_BUF_INIT;
- assert(remote_name && branch);
+ GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(branch);
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
- remote_name, branch)) < 0 )
+ git_remote_name(remote), branch)) < 0 )
goto cleanup;
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
goto cleanup;
- retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch,
- reflog_message);
+ if ((retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch,
+ reflog_message)) < 0)
+ goto cleanup;
+
+ if ((retcode = git_remote_default_branch(&default_branch, remote)) < 0)
+ goto cleanup;
+
+ if (!git_remote__matching_refspec(remote, git_buf_cstr(&default_branch)))
+ goto cleanup;
+
+ retcode = update_remote_head(repo, remote, &default_branch, reflog_message);
cleanup:
git_reference_free(remote_ref);
git_buf_dispose(&remote_branch_name);
+ git_buf_dispose(&default_branch);
return retcode;
}
int error;
if (branch)
- error = update_head_to_branch(repo, git_remote_name(remote), branch,
- reflog_message);
+ error = update_head_to_branch(repo, remote, branch, reflog_message);
/* Point HEAD to the same ref as the remote's head */
else
error = update_head_to_remote(repo, remote, reflog_message);
git_fetch_options fetch_opts;
git_remote *remote;
- assert(repo && _remote);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(_remote);
if (!git_repository_is_empty(repo)) {
git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
git_repository_create_cb repository_cb;
- assert(out && url && local_path);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(local_path);
if (_options)
memcpy(&options, _options, sizeof(git_clone_options));
git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
git_buf reflog_message = GIT_BUF_INIT;
- assert(repo && remote);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(remote);
if (!git_repository_is_empty(repo)) {
git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
size_t i = 0;
const git_oid *parent;
- assert(out && tree);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(tree);
git_oid__writebuf(out, "tree ", tree);
int error = 0;
commit_parent_varargs data;
- assert(tree && git_tree_owner(tree) == repo);
+ GIT_ASSERT_ARG(tree);
+ GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
data.total = parent_count;
va_start(data.args, parent_count);
{
commit_parent_data data = { parent_count, parents, repo };
- assert(tree && git_tree_owner(tree) == repo);
+ GIT_ASSERT_ARG(tree);
+ GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
return git_commit__create_internal(
id, repo, update_ref, author, committer,
git_reference *ref;
int error;
- assert(id && commit_to_amend);
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(commit_to_amend);
repo = git_commit_owner(commit_to_amend);
git_oid_cpy(&tree_id, git_tree_id(old_tree));
git_tree_free(old_tree);
} else {
- assert(git_tree_owner(tree) == repo);
+ GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
git_oid_cpy(&tree_id, git_tree_id(tree));
}
size_t header_len;
git_signature dummy_sig;
- assert(commit && data);
+ GIT_ASSERT_ARG(commit);
+ GIT_ASSERT_ARG(data);
buffer = buffer_start;
return git_commit__parse_ext(_commit, odb_obj, 0);
}
-#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
+#define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \
_rvalue git_commit_##_name(const git_commit *commit) \
{\
- assert(commit); \
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, _invalid); \
return _return; \
}
-GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
-GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
-GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
-GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
-GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
-GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
-GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
-GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
-GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id)
+GIT_COMMIT_GETTER(const git_signature *, author, commit->author, NULL)
+GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer, NULL)
+GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message, NULL)
+GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding, NULL)
+GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header, NULL)
+GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time, INT64_MIN)
+GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset, -1)
+GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids), 0)
+GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id, NULL)
const char *git_commit_message(const git_commit *commit)
{
const char *message;
- assert(commit);
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
message = commit->raw_message;
const char *msg, *space;
bool space_contains_newline = false;
- assert(commit);
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
if (!commit->summary) {
for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
{
const char *msg, *end;
- assert(commit);
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
if (!commit->body) {
/* search for end of summary */
int git_commit_tree(git_tree **tree_out, const git_commit *commit)
{
- assert(commit);
+ GIT_ASSERT_ARG(commit);
return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
}
const git_oid *git_commit_parent_id(
const git_commit *commit, unsigned int n)
{
- assert(commit);
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
return git_array_get(commit->parent_ids, n);
}
git_commit **parent, const git_commit *commit, unsigned int n)
{
const git_oid *parent_id;
- assert(commit);
+ GIT_ASSERT_ARG(commit);
parent_id = git_commit_parent_id(commit, n);
if (parent_id == NULL) {
git_commit *current, *parent = NULL;
int error;
- assert(ancestor && commit);
+ GIT_ASSERT_ARG(ancestor);
+ GIT_ASSERT_ARG(commit);
if (git_commit_dup(¤t, (git_commit *)commit) < 0)
return -1;
git_array_oid_t parents_arr = GIT_ARRAY_INIT;
const git_oid *tree_id;
- assert(tree && git_tree_owner(tree) == repo);
+ GIT_ASSERT_ARG(tree);
+ GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
tree_id = git_tree_id(tree);
/**
* Append to 'out' properly marking continuations when there's a newline in 'content'
*/
-static void format_header_field(git_buf *out, const char *field, const char *content)
+static int format_header_field(git_buf *out, const char *field, const char *content)
{
const char *lf;
- assert(out && field && content);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(field);
+ GIT_ASSERT_ARG(content);
git_buf_puts(out, field);
git_buf_putc(out, ' ');
git_buf_puts(out, content);
git_buf_putc(out, '\n');
+
+ return git_buf_oom(out) ? -1 : 0;
}
static const git_oid *commit_parent_from_commit(size_t n, void *payload)
if (signature != NULL) {
field = signature_field ? signature_field : "gpgsig";
- format_header_field(&commit, field, signature);
+
+ if ((error = format_header_field(&commit, field, signature)) < 0)
+ goto cleanup;
}
git_buf_puts(&commit, header_end);
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "commit_graph.h"
+
+#include "array.h"
+#include "filebuf.h"
+#include "futils.h"
+#include "hash.h"
+#include "oidarray.h"
+#include "oidmap.h"
+#include "pack.h"
+#include "repository.h"
+#include "revwalk.h"
+
+#define GIT_COMMIT_GRAPH_MISSING_PARENT 0x70000000
+#define GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX 0x3FFFFFFF
+#define GIT_COMMIT_GRAPH_GENERATION_NUMBER_INFINITY 0xFFFFFFFF
+
+#define COMMIT_GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
+#define COMMIT_GRAPH_VERSION 1
+#define COMMIT_GRAPH_OBJECT_ID_VERSION 1
+struct git_commit_graph_header {
+ uint32_t signature;
+ uint8_t version;
+ uint8_t object_id_version;
+ uint8_t chunks;
+ uint8_t base_graph_files;
+};
+
+#define COMMIT_GRAPH_OID_FANOUT_ID 0x4f494446 /* "OIDF" */
+#define COMMIT_GRAPH_OID_LOOKUP_ID 0x4f49444c /* "OIDL" */
+#define COMMIT_GRAPH_COMMIT_DATA_ID 0x43444154 /* "CDAT" */
+#define COMMIT_GRAPH_EXTRA_EDGE_LIST_ID 0x45444745 /* "EDGE" */
+#define COMMIT_GRAPH_BLOOM_FILTER_INDEX_ID 0x42494458 /* "BIDX" */
+#define COMMIT_GRAPH_BLOOM_FILTER_DATA_ID 0x42444154 /* "BDAT" */
+
+struct git_commit_graph_chunk {
+ off64_t offset;
+ size_t length;
+};
+
+typedef git_array_t(size_t) parent_index_array_t;
+
+struct packed_commit {
+ size_t index;
+ git_oid sha1;
+ git_oid tree_oid;
+ uint32_t generation;
+ git_time_t commit_time;
+ git_array_oid_t parents;
+ parent_index_array_t parent_indices;
+};
+
+static void packed_commit_free(struct packed_commit *p)
+{
+ if (!p)
+ return;
+
+ git_array_clear(p->parents);
+ git_array_clear(p->parent_indices);
+ git__free(p);
+}
+
+static struct packed_commit *packed_commit_new(git_commit *commit)
+{
+ unsigned int i, parentcount = git_commit_parentcount(commit);
+ struct packed_commit *p = git__calloc(1, sizeof(struct packed_commit));
+ if (!p)
+ goto cleanup;
+
+ git_array_init_to_size(p->parents, parentcount);
+ if (parentcount && !p->parents.ptr)
+ goto cleanup;
+
+ if (git_oid_cpy(&p->sha1, git_commit_id(commit)) < 0)
+ goto cleanup;
+ if (git_oid_cpy(&p->tree_oid, git_commit_tree_id(commit)) < 0)
+ goto cleanup;
+ p->commit_time = git_commit_time(commit);
+
+ for (i = 0; i < parentcount; ++i) {
+ git_oid *parent_id = git_array_alloc(p->parents);
+ if (!parent_id)
+ goto cleanup;
+ if (git_oid_cpy(parent_id, git_commit_parent_id(commit, i)) < 0)
+ goto cleanup;
+ }
+
+ return p;
+
+cleanup:
+ packed_commit_free(p);
+ return NULL;
+}
+
+typedef int (*commit_graph_write_cb)(const char *buf, size_t size, void *cb_data);
+
+static int commit_graph_error(const char *message)
+{
+ git_error_set(GIT_ERROR_ODB, "invalid commit-graph file - %s", message);
+ return -1;
+}
+
+static int commit_graph_parse_oid_fanout(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ struct git_commit_graph_chunk *chunk_oid_fanout)
+{
+ uint32_t i, nr;
+ if (chunk_oid_fanout->offset == 0)
+ return commit_graph_error("missing OID Fanout chunk");
+ if (chunk_oid_fanout->length == 0)
+ return commit_graph_error("empty OID Fanout chunk");
+ if (chunk_oid_fanout->length != 256 * 4)
+ return commit_graph_error("OID Fanout chunk has wrong length");
+
+ file->oid_fanout = (const uint32_t *)(data + chunk_oid_fanout->offset);
+ nr = 0;
+ for (i = 0; i < 256; ++i) {
+ uint32_t n = ntohl(file->oid_fanout[i]);
+ if (n < nr)
+ return commit_graph_error("index is non-monotonic");
+ nr = n;
+ }
+ file->num_commits = nr;
+ return 0;
+}
+
+static int commit_graph_parse_oid_lookup(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ struct git_commit_graph_chunk *chunk_oid_lookup)
+{
+ uint32_t i;
+ git_oid *oid, *prev_oid, zero_oid = {{0}};
+
+ if (chunk_oid_lookup->offset == 0)
+ return commit_graph_error("missing OID Lookup chunk");
+ if (chunk_oid_lookup->length == 0)
+ return commit_graph_error("empty OID Lookup chunk");
+ if (chunk_oid_lookup->length != file->num_commits * GIT_OID_RAWSZ)
+ return commit_graph_error("OID Lookup chunk has wrong length");
+
+ file->oid_lookup = oid = (git_oid *)(data + chunk_oid_lookup->offset);
+ prev_oid = &zero_oid;
+ for (i = 0; i < file->num_commits; ++i, ++oid) {
+ if (git_oid_cmp(prev_oid, oid) >= 0)
+ return commit_graph_error("OID Lookup index is non-monotonic");
+ prev_oid = oid;
+ }
+
+ return 0;
+}
+
+static int commit_graph_parse_commit_data(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ struct git_commit_graph_chunk *chunk_commit_data)
+{
+ if (chunk_commit_data->offset == 0)
+ return commit_graph_error("missing Commit Data chunk");
+ if (chunk_commit_data->length == 0)
+ return commit_graph_error("empty Commit Data chunk");
+ if (chunk_commit_data->length != file->num_commits * (GIT_OID_RAWSZ + 16))
+ return commit_graph_error("Commit Data chunk has wrong length");
+
+ file->commit_data = data + chunk_commit_data->offset;
+
+ return 0;
+}
+
+static int commit_graph_parse_extra_edge_list(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ struct git_commit_graph_chunk *chunk_extra_edge_list)
+{
+ if (chunk_extra_edge_list->length == 0)
+ return 0;
+ if (chunk_extra_edge_list->length % 4 != 0)
+ return commit_graph_error("malformed Extra Edge List chunk");
+
+ file->extra_edge_list = data + chunk_extra_edge_list->offset;
+ file->num_extra_edge_list = chunk_extra_edge_list->length / 4;
+
+ return 0;
+}
+
+int git_commit_graph_file_parse(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ size_t size)
+{
+ struct git_commit_graph_header *hdr;
+ const unsigned char *chunk_hdr;
+ struct git_commit_graph_chunk *last_chunk;
+ uint32_t i;
+ off64_t last_chunk_offset, chunk_offset, trailer_offset;
+ git_oid cgraph_checksum = {{0}};
+ int error;
+ struct git_commit_graph_chunk chunk_oid_fanout = {0}, chunk_oid_lookup = {0},
+ chunk_commit_data = {0}, chunk_extra_edge_list = {0},
+ chunk_unsupported = {0};
+
+ GIT_ASSERT_ARG(file);
+
+ if (size < sizeof(struct git_commit_graph_header) + GIT_OID_RAWSZ)
+ return commit_graph_error("commit-graph is too short");
+
+ hdr = ((struct git_commit_graph_header *)data);
+
+ if (hdr->signature != htonl(COMMIT_GRAPH_SIGNATURE) || hdr->version != COMMIT_GRAPH_VERSION
+ || hdr->object_id_version != COMMIT_GRAPH_OBJECT_ID_VERSION) {
+ return commit_graph_error("unsupported commit-graph version");
+ }
+ if (hdr->chunks == 0)
+ return commit_graph_error("no chunks in commit-graph");
+
+ /*
+ * The very first chunk's offset should be after the header, all the chunk
+ * headers, and a special zero chunk.
+ */
+ last_chunk_offset = sizeof(struct git_commit_graph_header) + (1 + hdr->chunks) * 12;
+ trailer_offset = size - GIT_OID_RAWSZ;
+ if (trailer_offset < last_chunk_offset)
+ return commit_graph_error("wrong commit-graph size");
+ git_oid_cpy(&file->checksum, (git_oid *)(data + trailer_offset));
+
+ if (git_hash_buf(&cgraph_checksum, data, (size_t)trailer_offset) < 0)
+ return commit_graph_error("could not calculate signature");
+ if (!git_oid_equal(&cgraph_checksum, &file->checksum))
+ return commit_graph_error("index signature mismatch");
+
+ chunk_hdr = data + sizeof(struct git_commit_graph_header);
+ last_chunk = NULL;
+ for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) {
+ chunk_offset = ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) << 32
+ | ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 8))));
+ if (chunk_offset < last_chunk_offset)
+ return commit_graph_error("chunks are non-monotonic");
+ if (chunk_offset >= trailer_offset)
+ return commit_graph_error("chunks extend beyond the trailer");
+ if (last_chunk != NULL)
+ last_chunk->length = (size_t)(chunk_offset - last_chunk_offset);
+ last_chunk_offset = chunk_offset;
+
+ switch (ntohl(*((uint32_t *)(chunk_hdr + 0)))) {
+ case COMMIT_GRAPH_OID_FANOUT_ID:
+ chunk_oid_fanout.offset = last_chunk_offset;
+ last_chunk = &chunk_oid_fanout;
+ break;
+
+ case COMMIT_GRAPH_OID_LOOKUP_ID:
+ chunk_oid_lookup.offset = last_chunk_offset;
+ last_chunk = &chunk_oid_lookup;
+ break;
+
+ case COMMIT_GRAPH_COMMIT_DATA_ID:
+ chunk_commit_data.offset = last_chunk_offset;
+ last_chunk = &chunk_commit_data;
+ break;
+
+ case COMMIT_GRAPH_EXTRA_EDGE_LIST_ID:
+ chunk_extra_edge_list.offset = last_chunk_offset;
+ last_chunk = &chunk_extra_edge_list;
+ break;
+
+ case COMMIT_GRAPH_BLOOM_FILTER_INDEX_ID:
+ case COMMIT_GRAPH_BLOOM_FILTER_DATA_ID:
+ chunk_unsupported.offset = last_chunk_offset;
+ last_chunk = &chunk_unsupported;
+ break;
+
+ default:
+ return commit_graph_error("unrecognized chunk ID");
+ }
+ }
+ last_chunk->length = (size_t)(trailer_offset - last_chunk_offset);
+
+ error = commit_graph_parse_oid_fanout(file, data, &chunk_oid_fanout);
+ if (error < 0)
+ return error;
+ error = commit_graph_parse_oid_lookup(file, data, &chunk_oid_lookup);
+ if (error < 0)
+ return error;
+ error = commit_graph_parse_commit_data(file, data, &chunk_commit_data);
+ if (error < 0)
+ return error;
+ error = commit_graph_parse_extra_edge_list(file, data, &chunk_extra_edge_list);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir, bool open_file)
+{
+ git_commit_graph *cgraph = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(cgraph_out);
+ GIT_ASSERT_ARG(objects_dir);
+
+ cgraph = git__calloc(1, sizeof(git_commit_graph));
+ GIT_ERROR_CHECK_ALLOC(cgraph);
+
+ error = git_buf_joinpath(&cgraph->filename, objects_dir, "info/commit-graph");
+ if (error < 0)
+ goto error;
+
+ if (open_file) {
+ error = git_commit_graph_file_open(&cgraph->file, git_buf_cstr(&cgraph->filename));
+ if (error < 0)
+ goto error;
+ cgraph->checked = 1;
+ }
+
+ *cgraph_out = cgraph;
+ return 0;
+
+error:
+ git_commit_graph_free(cgraph);
+ return error;
+}
+
+int git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir)
+{
+ return git_commit_graph_new(cgraph_out, objects_dir, true);
+}
+
+int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path)
+{
+ git_commit_graph_file *file;
+ git_file fd = -1;
+ size_t cgraph_size;
+ struct stat st;
+ int error;
+
+ /* TODO: properly open the file without access time using O_NOATIME */
+ fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return fd;
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_ODB, "commit-graph file not found - '%s'", path);
+ return GIT_ENOTFOUND;
+ }
+
+ if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size)) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path);
+ return GIT_ENOTFOUND;
+ }
+ cgraph_size = (size_t)st.st_size;
+
+ file = git__calloc(1, sizeof(git_commit_graph_file));
+ GIT_ERROR_CHECK_ALLOC(file);
+
+ error = git_futils_mmap_ro(&file->graph_map, fd, 0, cgraph_size);
+ p_close(fd);
+ if (error < 0) {
+ git_commit_graph_file_free(file);
+ return error;
+ }
+
+ if ((error = git_commit_graph_file_parse(file, file->graph_map.data, cgraph_size)) < 0) {
+ git_commit_graph_file_free(file);
+ return error;
+ }
+
+ *file_out = file;
+ return 0;
+}
+
+int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph *cgraph)
+{
+ if (!cgraph->checked) {
+ int error = 0;
+ git_commit_graph_file *result = NULL;
+
+ /* We only check once, no matter the result. */
+ cgraph->checked = 1;
+
+ /* Best effort */
+ error = git_commit_graph_file_open(&result, git_buf_cstr(&cgraph->filename));
+
+ if (error < 0)
+ return error;
+
+ cgraph->file = result;
+ }
+ if (!cgraph->file)
+ return GIT_ENOTFOUND;
+
+ *file_out = cgraph->file;
+ return 0;
+}
+
+void git_commit_graph_refresh(git_commit_graph *cgraph)
+{
+ if (!cgraph->checked)
+ return;
+
+ if (cgraph->file
+ && git_commit_graph_file_needs_refresh(cgraph->file, git_buf_cstr(&cgraph->filename))) {
+ /* We just free the commit graph. The next time it is requested, it will be
+ * re-loaded. */
+ git_commit_graph_file_free(cgraph->file);
+ cgraph->file = NULL;
+ }
+ /* Force a lazy re-check next time it is needed. */
+ cgraph->checked = 0;
+}
+
+static int git_commit_graph_entry_get_byindex(
+ git_commit_graph_entry *e,
+ const git_commit_graph_file *file,
+ size_t pos)
+{
+ const unsigned char *commit_data;
+
+ GIT_ASSERT_ARG(e);
+ GIT_ASSERT_ARG(file);
+
+ if (pos >= file->num_commits) {
+ git_error_set(GIT_ERROR_INVALID, "commit index %zu does not exist", pos);
+ return GIT_ENOTFOUND;
+ }
+
+ commit_data = file->commit_data + pos * (GIT_OID_RAWSZ + 4 * sizeof(uint32_t));
+ git_oid_cpy(&e->tree_oid, (const git_oid *)commit_data);
+ e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + GIT_OID_RAWSZ)));
+ e->parent_indices[1] = ntohl(
+ *((uint32_t *)(commit_data + GIT_OID_RAWSZ + sizeof(uint32_t))));
+ e->parent_count = (e->parent_indices[0] != GIT_COMMIT_GRAPH_MISSING_PARENT)
+ + (e->parent_indices[1] != GIT_COMMIT_GRAPH_MISSING_PARENT);
+ e->generation = ntohl(*((uint32_t *)(commit_data + GIT_OID_RAWSZ + 2 * sizeof(uint32_t))));
+ e->commit_time = ntohl(*((uint32_t *)(commit_data + GIT_OID_RAWSZ + 3 * sizeof(uint32_t))));
+
+ e->commit_time |= (e->generation & UINT64_C(0x3)) << UINT64_C(32);
+ e->generation >>= 2u;
+ if (e->parent_indices[1] & 0x80000000u) {
+ uint32_t extra_edge_list_pos = e->parent_indices[1] & 0x7fffffff;
+
+ /* Make sure we're not being sent out of bounds */
+ if (extra_edge_list_pos >= file->num_extra_edge_list) {
+ git_error_set(GIT_ERROR_INVALID,
+ "commit %u does not exist",
+ extra_edge_list_pos);
+ return GIT_ENOTFOUND;
+ }
+
+ e->extra_parents_index = extra_edge_list_pos;
+ while (extra_edge_list_pos < file->num_extra_edge_list
+ && (ntohl(*(
+ (uint32_t *)(file->extra_edge_list
+ + extra_edge_list_pos * sizeof(uint32_t))))
+ & 0x80000000u)
+ == 0) {
+ extra_edge_list_pos++;
+ e->parent_count++;
+ }
+ }
+ git_oid_cpy(&e->sha1, &file->oid_lookup[pos]);
+ return 0;
+}
+
+bool git_commit_graph_file_needs_refresh(const git_commit_graph_file *file, const char *path)
+{
+ git_file fd = -1;
+ struct stat st;
+ ssize_t bytes_read;
+ git_oid cgraph_checksum = {{0}};
+
+ /* TODO: properly open the file without access time using O_NOATIME */
+ fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return true;
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ return true;
+ }
+
+ if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size)
+ || (size_t)st.st_size != file->graph_map.len) {
+ p_close(fd);
+ return true;
+ }
+
+ bytes_read = p_pread(fd, cgraph_checksum.id, GIT_OID_RAWSZ, st.st_size - GIT_OID_RAWSZ);
+ p_close(fd);
+ if (bytes_read != GIT_OID_RAWSZ)
+ return true;
+
+ return !git_oid_equal(&cgraph_checksum, &file->checksum);
+}
+
+int git_commit_graph_entry_find(
+ git_commit_graph_entry *e,
+ const git_commit_graph_file *file,
+ const git_oid *short_oid,
+ size_t len)
+{
+ int pos, found = 0;
+ uint32_t hi, lo;
+ const git_oid *current = NULL;
+
+ GIT_ASSERT_ARG(e);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(short_oid);
+
+ hi = ntohl(file->oid_fanout[(int)short_oid->id[0]]);
+ lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(file->oid_fanout[(int)short_oid->id[0] - 1]));
+
+ pos = git_pack__lookup_sha1(file->oid_lookup, GIT_OID_RAWSZ, lo, hi, short_oid->id);
+
+ if (pos >= 0) {
+ /* An object matching exactly the oid was found */
+ found = 1;
+ current = file->oid_lookup + pos;
+ } else {
+ /* No object was found */
+ /* pos refers to the object with the "closest" oid to short_oid */
+ pos = -1 - pos;
+ if (pos < (int)file->num_commits) {
+ current = file->oid_lookup + pos;
+
+ if (!git_oid_ncmp(short_oid, current, len))
+ found = 1;
+ }
+ }
+
+ if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)file->num_commits) {
+ /* Check for ambiguousity */
+ const git_oid *next = current + 1;
+
+ if (!git_oid_ncmp(short_oid, next, len)) {
+ found = 2;
+ }
+ }
+
+ if (!found)
+ return git_odb__error_notfound(
+ "failed to find offset for commit-graph index entry", short_oid, len);
+ if (found > 1)
+ return git_odb__error_ambiguous(
+ "found multiple offsets for commit-graph index entry");
+
+ return git_commit_graph_entry_get_byindex(e, file, pos);
+}
+
+int git_commit_graph_entry_parent(
+ git_commit_graph_entry *parent,
+ const git_commit_graph_file *file,
+ const git_commit_graph_entry *entry,
+ size_t n)
+{
+ GIT_ASSERT_ARG(parent);
+ GIT_ASSERT_ARG(file);
+
+ if (n >= entry->parent_count) {
+ git_error_set(GIT_ERROR_INVALID, "parent index %zu does not exist", n);
+ return GIT_ENOTFOUND;
+ }
+
+ if (n == 0 || (n == 1 && entry->parent_count == 2))
+ return git_commit_graph_entry_get_byindex(parent, file, entry->parent_indices[n]);
+
+ return git_commit_graph_entry_get_byindex(
+ parent,
+ file,
+ ntohl(
+ *(uint32_t *)(file->extra_edge_list
+ + (entry->extra_parents_index + n - 1)
+ * sizeof(uint32_t)))
+ & 0x7fffffff);
+}
+
+int git_commit_graph_file_close(git_commit_graph_file *file)
+{
+ GIT_ASSERT_ARG(file);
+
+ if (file->graph_map.data)
+ git_futils_mmap_free(&file->graph_map);
+
+ return 0;
+}
+
+void git_commit_graph_free(git_commit_graph *cgraph)
+{
+ if (!cgraph)
+ return;
+
+ git_buf_dispose(&cgraph->filename);
+ git_commit_graph_file_free(cgraph->file);
+ git__free(cgraph);
+}
+
+void git_commit_graph_file_free(git_commit_graph_file *file)
+{
+ if (!file)
+ return;
+
+ git_commit_graph_file_close(file);
+ git__free(file);
+}
+
+static int packed_commit__cmp(const void *a_, const void *b_)
+{
+ const struct packed_commit *a = a_;
+ const struct packed_commit *b = b_;
+ return git_oid_cmp(&a->sha1, &b->sha1);
+}
+
+int git_commit_graph_writer_new(git_commit_graph_writer **out, const char *objects_info_dir)
+{
+ git_commit_graph_writer *w = git__calloc(1, sizeof(git_commit_graph_writer));
+ GIT_ERROR_CHECK_ALLOC(w);
+
+ if (git_buf_sets(&w->objects_info_dir, objects_info_dir) < 0) {
+ git__free(w);
+ return -1;
+ }
+
+ if (git_vector_init(&w->commits, 0, packed_commit__cmp) < 0) {
+ git_buf_dispose(&w->objects_info_dir);
+ git__free(w);
+ return -1;
+ }
+
+ *out = w;
+ return 0;
+}
+
+void git_commit_graph_writer_free(git_commit_graph_writer *w)
+{
+ struct packed_commit *packed_commit;
+ size_t i;
+
+ if (!w)
+ return;
+
+ git_vector_foreach (&w->commits, i, packed_commit)
+ packed_commit_free(packed_commit);
+ git_vector_free(&w->commits);
+ git_buf_dispose(&w->objects_info_dir);
+ git__free(w);
+}
+
+struct object_entry_cb_state {
+ git_repository *repo;
+ git_odb *db;
+ git_vector *commits;
+};
+
+static int object_entry__cb(const git_oid *id, void *data)
+{
+ struct object_entry_cb_state *state = (struct object_entry_cb_state *)data;
+ git_commit *commit = NULL;
+ struct packed_commit *packed_commit = NULL;
+ size_t header_len;
+ git_object_t header_type;
+ int error = 0;
+
+ error = git_odb_read_header(&header_len, &header_type, state->db, id);
+ if (error < 0)
+ return error;
+
+ if (header_type != GIT_OBJECT_COMMIT)
+ return 0;
+
+ error = git_commit_lookup(&commit, state->repo, id);
+ if (error < 0)
+ return error;
+
+ packed_commit = packed_commit_new(commit);
+ git_commit_free(commit);
+ GIT_ERROR_CHECK_ALLOC(packed_commit);
+
+ error = git_vector_insert(state->commits, packed_commit);
+ if (error < 0) {
+ packed_commit_free(packed_commit);
+ return error;
+ }
+
+ return 0;
+}
+
+int git_commit_graph_writer_add_index_file(
+ git_commit_graph_writer *w,
+ git_repository *repo,
+ const char *idx_path)
+{
+ int error;
+ struct git_pack_file *p = NULL;
+ struct object_entry_cb_state state = {0};
+ state.repo = repo;
+ state.commits = &w->commits;
+
+ error = git_repository_odb(&state.db, repo);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_mwindow_get_pack(&p, idx_path);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_pack_foreach_entry(p, object_entry__cb, &state);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ if (p)
+ git_mwindow_put_pack(p);
+ git_odb_free(state.db);
+ return error;
+}
+
+int git_commit_graph_writer_add_revwalk(git_commit_graph_writer *w, git_revwalk *walk)
+{
+ int error;
+ git_oid id;
+ git_repository *repo = git_revwalk_repository(walk);
+ git_commit *commit;
+ struct packed_commit *packed_commit;
+
+ while ((git_revwalk_next(&id, walk)) == 0) {
+ error = git_commit_lookup(&commit, repo, &id);
+ if (error < 0)
+ return error;
+
+ packed_commit = packed_commit_new(commit);
+ git_commit_free(commit);
+ GIT_ERROR_CHECK_ALLOC(packed_commit);
+
+ error = git_vector_insert(&w->commits, packed_commit);
+ if (error < 0) {
+ packed_commit_free(packed_commit);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+enum generation_number_commit_state {
+ GENERATION_NUMBER_COMMIT_STATE_UNVISITED = 0,
+ GENERATION_NUMBER_COMMIT_STATE_ADDED = 1,
+ GENERATION_NUMBER_COMMIT_STATE_EXPANDED = 2,
+ GENERATION_NUMBER_COMMIT_STATE_VISITED = 3,
+};
+
+static int compute_generation_numbers(git_vector *commits)
+{
+ git_array_t(size_t) index_stack = GIT_ARRAY_INIT;
+ size_t i, j;
+ size_t *parent_idx;
+ enum generation_number_commit_state *commit_states = NULL;
+ struct packed_commit *child_packed_commit;
+ git_oidmap *packed_commit_map = NULL;
+ int error = 0;
+
+ /* First populate the parent indices fields */
+ error = git_oidmap_new(&packed_commit_map);
+ if (error < 0)
+ goto cleanup;
+ git_vector_foreach (commits, i, child_packed_commit) {
+ child_packed_commit->index = i;
+ error = git_oidmap_set(
+ packed_commit_map, &child_packed_commit->sha1, child_packed_commit);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ git_vector_foreach (commits, i, child_packed_commit) {
+ size_t parent_i, *parent_idx_ptr;
+ struct packed_commit *parent_packed_commit;
+ git_oid *parent_id;
+ git_array_init_to_size(
+ child_packed_commit->parent_indices,
+ git_array_size(child_packed_commit->parents));
+ if (git_array_size(child_packed_commit->parents)
+ && !child_packed_commit->parent_indices.ptr) {
+ error = -1;
+ goto cleanup;
+ }
+ git_array_foreach (child_packed_commit->parents, parent_i, parent_id) {
+ parent_packed_commit = git_oidmap_get(packed_commit_map, parent_id);
+ if (!parent_packed_commit) {
+ git_error_set(GIT_ERROR_ODB,
+ "parent commit %s not found in commit graph",
+ git_oid_tostr_s(parent_id));
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+ parent_idx_ptr = git_array_alloc(child_packed_commit->parent_indices);
+ if (!parent_idx_ptr) {
+ error = -1;
+ goto cleanup;
+ }
+ *parent_idx_ptr = parent_packed_commit->index;
+ }
+ }
+
+ /*
+ * We copy all the commits to the stack and then during visitation,
+ * each node can be added up to two times to the stack.
+ */
+ git_array_init_to_size(index_stack, 3 * git_vector_length(commits));
+ if (!index_stack.ptr) {
+ error = -1;
+ goto cleanup;
+ }
+
+ commit_states = (enum generation_number_commit_state *)git__calloc(
+ git_vector_length(commits), sizeof(enum generation_number_commit_state));
+ if (!commit_states) {
+ error = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Perform a Post-Order traversal so that all parent nodes are fully
+ * visited before the child node.
+ */
+ git_vector_foreach (commits, i, child_packed_commit)
+ *(size_t *)git_array_alloc(index_stack) = i;
+
+ while (git_array_size(index_stack)) {
+ size_t *index_ptr = git_array_pop(index_stack);
+ i = *index_ptr;
+ child_packed_commit = git_vector_get(commits, i);
+
+ if (commit_states[i] == GENERATION_NUMBER_COMMIT_STATE_VISITED) {
+ /* This commit has already been fully visited. */
+ continue;
+ }
+ if (commit_states[i] == GENERATION_NUMBER_COMMIT_STATE_EXPANDED) {
+ /* All of the commits parents have been visited. */
+ child_packed_commit->generation = 0;
+ git_array_foreach (child_packed_commit->parent_indices, j, parent_idx) {
+ struct packed_commit *parent = git_vector_get(commits, *parent_idx);
+ if (child_packed_commit->generation < parent->generation)
+ child_packed_commit->generation = parent->generation;
+ }
+ if (child_packed_commit->generation
+ < GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX) {
+ ++child_packed_commit->generation;
+ }
+ commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_VISITED;
+ continue;
+ }
+
+ /*
+ * This is the first time we see this commit. We need
+ * to visit all its parents before we can fully visit
+ * it.
+ */
+ if (git_array_size(child_packed_commit->parent_indices) == 0) {
+ /*
+ * Special case: if the commit has no parents, there's
+ * no need to add it to the stack just to immediately
+ * remove it.
+ */
+ commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_VISITED;
+ child_packed_commit->generation = 1;
+ continue;
+ }
+
+ /*
+ * Add this current commit again so that it is visited
+ * again once all its children have been visited.
+ */
+ *(size_t *)git_array_alloc(index_stack) = i;
+ git_array_foreach (child_packed_commit->parent_indices, j, parent_idx) {
+ if (commit_states[*parent_idx]
+ != GENERATION_NUMBER_COMMIT_STATE_UNVISITED) {
+ /* This commit has already been considered. */
+ continue;
+ }
+
+ commit_states[*parent_idx] = GENERATION_NUMBER_COMMIT_STATE_ADDED;
+ *(size_t *)git_array_alloc(index_stack) = *parent_idx;
+ }
+ commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_EXPANDED;
+ }
+
+cleanup:
+ git_oidmap_free(packed_commit_map);
+ git__free(commit_states);
+ git_array_clear(index_stack);
+
+ return error;
+}
+
+static int write_offset(off64_t offset, commit_graph_write_cb write_cb, void *cb_data)
+{
+ int error;
+ uint32_t word;
+
+ word = htonl((uint32_t)((offset >> 32) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ word = htonl((uint32_t)((offset >> 0) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int write_chunk_header(
+ int chunk_id,
+ off64_t offset,
+ commit_graph_write_cb write_cb,
+ void *cb_data)
+{
+ uint32_t word = htonl(chunk_id);
+ int error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ return write_offset(offset, write_cb, cb_data);
+}
+
+static int commit_graph_write_buf(const char *buf, size_t size, void *data)
+{
+ git_buf *b = (git_buf *)data;
+ return git_buf_put(b, buf, size);
+}
+
+struct commit_graph_write_hash_context {
+ commit_graph_write_cb write_cb;
+ void *cb_data;
+ git_hash_ctx *ctx;
+};
+
+static int commit_graph_write_hash(const char *buf, size_t size, void *data)
+{
+ struct commit_graph_write_hash_context *ctx = data;
+ int error;
+
+ error = git_hash_update(ctx->ctx, buf, size);
+ if (error < 0)
+ return error;
+
+ return ctx->write_cb(buf, size, ctx->cb_data);
+}
+
+static void packed_commit_free_dup(void *packed_commit)
+{
+ packed_commit_free(packed_commit);
+}
+
+static int commit_graph_write(
+ git_commit_graph_writer *w,
+ commit_graph_write_cb write_cb,
+ void *cb_data)
+{
+ int error = 0;
+ size_t i;
+ struct packed_commit *packed_commit;
+ struct git_commit_graph_header hdr = {0};
+ uint32_t oid_fanout_count;
+ uint32_t extra_edge_list_count;
+ uint32_t oid_fanout[256];
+ off64_t offset;
+ git_buf oid_lookup = GIT_BUF_INIT, commit_data = GIT_BUF_INIT,
+ extra_edge_list = GIT_BUF_INIT;
+ git_oid cgraph_checksum = {{0}};
+ git_hash_ctx ctx;
+ struct commit_graph_write_hash_context hash_cb_data = {0};
+
+ hdr.signature = htonl(COMMIT_GRAPH_SIGNATURE);
+ hdr.version = COMMIT_GRAPH_VERSION;
+ hdr.object_id_version = COMMIT_GRAPH_OBJECT_ID_VERSION;
+ hdr.chunks = 0;
+ hdr.base_graph_files = 0;
+ hash_cb_data.write_cb = write_cb;
+ hash_cb_data.cb_data = cb_data;
+ hash_cb_data.ctx = &ctx;
+
+ error = git_hash_ctx_init(&ctx);
+ if (error < 0)
+ return error;
+ cb_data = &hash_cb_data;
+ write_cb = commit_graph_write_hash;
+
+ /* Sort the commits. */
+ git_vector_sort(&w->commits);
+ git_vector_uniq(&w->commits, packed_commit_free_dup);
+ error = compute_generation_numbers(&w->commits);
+ if (error < 0)
+ goto cleanup;
+
+ /* Fill the OID Fanout table. */
+ oid_fanout_count = 0;
+ for (i = 0; i < 256; i++) {
+ while (oid_fanout_count < git_vector_length(&w->commits) &&
+ (packed_commit = (struct packed_commit *)git_vector_get(&w->commits, oid_fanout_count)) &&
+ packed_commit->sha1.id[0] <= i)
+ ++oid_fanout_count;
+ oid_fanout[i] = htonl(oid_fanout_count);
+ }
+
+ /* Fill the OID Lookup table. */
+ git_vector_foreach (&w->commits, i, packed_commit) {
+ error = git_buf_put(&oid_lookup,
+ (const char *)&packed_commit->sha1, sizeof(git_oid));
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Fill the Commit Data and Extra Edge List tables. */
+ extra_edge_list_count = 0;
+ git_vector_foreach (&w->commits, i, packed_commit) {
+ uint64_t commit_time;
+ uint32_t generation;
+ uint32_t word;
+ size_t *packed_index;
+ unsigned int parentcount = (unsigned int)git_array_size(packed_commit->parents);
+
+ error = git_buf_put(&commit_data,
+ (const char *)&packed_commit->tree_oid,
+ sizeof(git_oid));
+ if (error < 0)
+ goto cleanup;
+
+ if (parentcount == 0) {
+ word = htonl(GIT_COMMIT_GRAPH_MISSING_PARENT);
+ } else {
+ packed_index = git_array_get(packed_commit->parent_indices, 0);
+ word = htonl((uint32_t)*packed_index);
+ }
+ error = git_buf_put(&commit_data, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+
+ if (parentcount < 2) {
+ word = htonl(GIT_COMMIT_GRAPH_MISSING_PARENT);
+ } else if (parentcount == 2) {
+ packed_index = git_array_get(packed_commit->parent_indices, 1);
+ word = htonl((uint32_t)*packed_index);
+ } else {
+ word = htonl(0x80000000u | extra_edge_list_count);
+ }
+ error = git_buf_put(&commit_data, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+
+ if (parentcount > 2) {
+ unsigned int parent_i;
+ for (parent_i = 1; parent_i < parentcount; ++parent_i) {
+ packed_index = git_array_get(
+ packed_commit->parent_indices, parent_i);
+ word = htonl((uint32_t)(*packed_index | (parent_i + 1 == parentcount ? 0x80000000u : 0)));
+
+ error = git_buf_put(&extra_edge_list,
+ (const char *)&word,
+ sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ }
+ extra_edge_list_count += parentcount - 1;
+ }
+
+ generation = packed_commit->generation;
+ commit_time = (uint64_t)packed_commit->commit_time;
+ if (generation > GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX)
+ generation = GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX;
+ word = ntohl((uint32_t)((generation << 2) | ((commit_time >> 32ull) & 0x3ull)));
+ error = git_buf_put(&commit_data, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ word = ntohl((uint32_t)(commit_time & 0xffffffffull));
+ error = git_buf_put(&commit_data, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Write the header. */
+ hdr.chunks = 3;
+ if (git_buf_len(&extra_edge_list) > 0)
+ hdr.chunks++;
+ error = write_cb((const char *)&hdr, sizeof(hdr), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write the chunk headers. */
+ offset = sizeof(hdr) + (hdr.chunks + 1) * 12;
+ error = write_chunk_header(COMMIT_GRAPH_OID_FANOUT_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += sizeof(oid_fanout);
+ error = write_chunk_header(COMMIT_GRAPH_OID_LOOKUP_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&oid_lookup);
+ error = write_chunk_header(COMMIT_GRAPH_COMMIT_DATA_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&commit_data);
+ if (git_buf_len(&extra_edge_list) > 0) {
+ error = write_chunk_header(
+ COMMIT_GRAPH_EXTRA_EDGE_LIST_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&extra_edge_list);
+ }
+ error = write_chunk_header(0, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write all the chunks. */
+ error = write_cb((const char *)oid_fanout, sizeof(oid_fanout), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&oid_lookup), git_buf_len(&oid_lookup), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&commit_data), git_buf_len(&commit_data), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&extra_edge_list), git_buf_len(&extra_edge_list), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Finalize the checksum and write the trailer. */
+ error = git_hash_final(&cgraph_checksum, &ctx);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb((const char *)&cgraph_checksum, sizeof(cgraph_checksum), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_buf_dispose(&oid_lookup);
+ git_buf_dispose(&commit_data);
+ git_buf_dispose(&extra_edge_list);
+ git_hash_ctx_cleanup(&ctx);
+ return error;
+}
+
+static int commit_graph_write_filebuf(const char *buf, size_t size, void *data)
+{
+ git_filebuf *f = (git_filebuf *)data;
+ return git_filebuf_write(f, buf, size);
+}
+
+int git_commit_graph_writer_options_init(
+ git_commit_graph_writer_options *opts,
+ unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts,
+ version,
+ git_commit_graph_writer_options,
+ GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT);
+ return 0;
+}
+
+int git_commit_graph_writer_commit(
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts)
+{
+ int error;
+ int filebuf_flags = GIT_FILEBUF_DO_NOT_BUFFER;
+ git_buf commit_graph_path = GIT_BUF_INIT;
+ git_filebuf output = GIT_FILEBUF_INIT;
+
+ /* TODO: support options and fill in defaults. */
+ GIT_UNUSED(opts);
+
+ error = git_buf_joinpath(
+ &commit_graph_path, git_buf_cstr(&w->objects_info_dir), "commit-graph");
+ if (error < 0)
+ return error;
+
+ if (git_repository__fsync_gitdir)
+ filebuf_flags |= GIT_FILEBUF_FSYNC;
+ error = git_filebuf_open(&output, git_buf_cstr(&commit_graph_path), filebuf_flags, 0644);
+ git_buf_dispose(&commit_graph_path);
+ if (error < 0)
+ return error;
+
+ error = commit_graph_write(w, commit_graph_write_filebuf, &output);
+ if (error < 0) {
+ git_filebuf_cleanup(&output);
+ return error;
+ }
+
+ return git_filebuf_commit(&output);
+}
+
+int git_commit_graph_writer_dump(
+ git_buf *cgraph,
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts)
+{
+ /* TODO: support options. */
+ GIT_UNUSED(opts);
+ return commit_graph_write(w, commit_graph_write_buf, cgraph);
+}
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_commit_graph_h__
+#define INCLUDE_commit_graph_h__
+
+#include "common.h"
+
+#include "git2/types.h"
+#include "git2/sys/commit_graph.h"
+
+#include "map.h"
+#include "vector.h"
+
+/**
+ * A commit-graph file.
+ *
+ * This file contains metadata about commits, particularly the generation
+ * number for each one. This can help speed up graph operations without
+ * requiring a full graph traversal.
+ *
+ * Support for this feature was added in git 2.19.
+ */
+typedef struct git_commit_graph_file {
+ git_map graph_map;
+
+ /* The OID Fanout table. */
+ const uint32_t *oid_fanout;
+ /* The total number of commits in the graph. */
+ uint32_t num_commits;
+
+ /* The OID Lookup table. */
+ git_oid *oid_lookup;
+
+ /*
+ * The Commit Data table. Each entry contains the OID of the commit followed
+ * by two 8-byte fields in network byte order:
+ * - The indices of the first two parents (32 bits each).
+ * - The generation number (first 30 bits) and commit time in seconds since
+ * UNIX epoch (34 bits).
+ */
+ const unsigned char *commit_data;
+
+ /*
+ * The Extra Edge List table. Each 4-byte entry is a network byte order index
+ * of one of the i-th (i > 0) parents of commits in the `commit_data` table,
+ * when the commit has more than 2 parents.
+ */
+ const unsigned char *extra_edge_list;
+ /* The number of entries in the Extra Edge List table. Each entry is 4 bytes wide. */
+ size_t num_extra_edge_list;
+
+ /* The trailer of the file. Contains the SHA1-checksum of the whole file. */
+ git_oid checksum;
+} git_commit_graph_file;
+
+/**
+ * An entry in the commit-graph file. Provides a subset of the information that
+ * can be obtained from the commit header.
+ */
+typedef struct git_commit_graph_entry {
+ /* The generation number of the commit within the graph */
+ size_t generation;
+
+ /* Time in seconds from UNIX epoch. */
+ git_time_t commit_time;
+
+ /* The number of parents of the commit. */
+ size_t parent_count;
+
+ /*
+ * The indices of the parent commits within the Commit Data table. The value
+ * of `GIT_COMMIT_GRAPH_MISSING_PARENT` indicates that no parent is in that
+ * position.
+ */
+ size_t parent_indices[2];
+
+ /* The index within the Extra Edge List of any parent after the first two. */
+ size_t extra_parents_index;
+
+ /* The SHA-1 hash of the root tree of the commit. */
+ git_oid tree_oid;
+
+ /* The SHA-1 hash of the requested commit. */
+ git_oid sha1;
+} git_commit_graph_entry;
+
+/* A wrapper for git_commit_graph_file to enable lazy loading in the ODB. */
+struct git_commit_graph {
+ /* The path to the commit-graph file. Something like ".git/objects/info/commit-graph". */
+ git_buf filename;
+
+ /* The underlying commit-graph file. */
+ git_commit_graph_file *file;
+
+ /* Whether the commit-graph file was already checked for validity. */
+ bool checked;
+};
+
+/** Create a new commit-graph, optionally opening the underlying file. */
+int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir, bool open_file);
+
+/** Open and validate a commit-graph file. */
+int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path);
+
+/*
+ * Attempt to get the git_commit_graph's commit-graph file. This object is
+ * still owned by the git_commit_graph. If the repository does not contain a commit graph,
+ * it will return GIT_ENOTFOUND.
+ *
+ * This function is not thread-safe.
+ */
+int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph *cgraph);
+
+/* Marks the commit-graph file as needing a refresh. */
+void git_commit_graph_refresh(git_commit_graph *cgraph);
+
+/*
+ * A writer for `commit-graph` files.
+ */
+struct git_commit_graph_writer {
+ /*
+ * The path of the `objects/info` directory where the `commit-graph` will be
+ * stored.
+ */
+ git_buf objects_info_dir;
+
+ /* The list of packed commits. */
+ git_vector commits;
+};
+
+/*
+ * Returns whether the git_commit_graph_file needs to be reloaded since the
+ * contents of the commit-graph file have changed on disk.
+ */
+bool git_commit_graph_file_needs_refresh(
+ const git_commit_graph_file *file, const char *path);
+
+int git_commit_graph_entry_find(
+ git_commit_graph_entry *e,
+ const git_commit_graph_file *file,
+ const git_oid *short_oid,
+ size_t len);
+int git_commit_graph_entry_parent(
+ git_commit_graph_entry *parent,
+ const git_commit_graph_file *file,
+ const git_commit_graph_entry *entry,
+ size_t n);
+int git_commit_graph_file_close(git_commit_graph_file *cgraph);
+void git_commit_graph_file_free(git_commit_graph_file *cgraph);
+
+/* This is exposed for use in the fuzzers. */
+int git_commit_graph_file_parse(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ size_t size);
+
+#endif
#include "odb.h"
#include "commit.h"
+int git_commit_list_generation_cmp(const void *a, const void *b)
+{
+ uint32_t generation_a = ((git_commit_list_node *) a)->generation;
+ uint32_t generation_b = ((git_commit_list_node *) b)->generation;
+
+ if (!generation_a || !generation_b) {
+ /* Fall back to comparing by timestamps if at least one commit lacks a generation. */
+ return git_commit_list_time_cmp(a, b);
+ }
+
+ if (generation_a < generation_b)
+ return 1;
+ if (generation_a > generation_b)
+ return -1;
+
+ return 0;
+}
+
int git_commit_list_time_cmp(const void *a, const void *b)
{
int64_t time_a = ((git_commit_list_node *) a)->time;
return -1;
}
+ node->generation = 0;
node->time = commit->committer->when.time;
node->out_degree = (uint16_t) git_array_size(commit->parent_ids);
node->parents = alloc_parents(walk, node, node->out_degree);
int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
{
git_odb_object *obj;
+ git_commit_graph_file *cgraph_file = NULL;
int error;
if (commit->parsed)
return 0;
+ /* Let's try to use the commit graph first. */
+ git_odb__get_commit_graph_file(&cgraph_file, walk->odb);
+ if (cgraph_file) {
+ git_commit_graph_entry e;
+
+ error = git_commit_graph_entry_find(&e, cgraph_file, &commit->oid, GIT_OID_RAWSZ);
+ if (error == 0 && git__is_uint16(e.parent_count)) {
+ size_t i;
+ commit->generation = (uint32_t)e.generation;
+ commit->time = e.commit_time;
+ commit->out_degree = (uint16_t)e.parent_count;
+ commit->parents = alloc_parents(walk, commit, commit->out_degree);
+ GIT_ERROR_CHECK_ALLOC(commit->parents);
+
+ for (i = 0; i < commit->out_degree; ++i) {
+ git_commit_graph_entry parent;
+ error = git_commit_graph_entry_parent(&parent, cgraph_file, &e, i);
+ if (error < 0)
+ return error;
+ commit->parents[i] = git_revwalk__commit_lookup(walk, &parent.sha1);
+ }
+ commit->parsed = 1;
+ return 0;
+ }
+ }
+
if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
return error;
typedef struct git_commit_list_node {
git_oid oid;
int64_t time;
+ uint32_t generation;
unsigned int seen:1,
uninteresting:1,
topo_delay:1,
} git_commit_list;
git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk);
+int git_commit_list_generation_cmp(const void *a, const void *b);
int git_commit_list_time_cmp(const void *a, const void *b);
void git_commit_list_free(git_commit_list **list_p);
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p);
# define GIT_INLINE(type) static __inline type
#elif defined(__GNUC__)
# define GIT_INLINE(type) static __inline__ type
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define GIT_INLINE(type) static inline type
#else
# define GIT_INLINE(type) static type
#endif
# define __has_builtin(x) 0
#endif
+/**
+ * Declare that a function's return value must be used.
+ *
+ * Used mostly to guard against potential silent bugs at runtime. This is
+ * recommended to be added to functions that:
+ *
+ * - Allocate / reallocate memory. This prevents memory leaks or errors where
+ * buffers are expected to have grown to a certain size, but could not be
+ * resized.
+ * - Acquire locks. When a lock cannot be acquired, that will almost certainly
+ * cause a data race / undefined behavior.
+ */
+#if defined(__GNUC__)
+# define GIT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+# define GIT_WARN_UNUSED_RESULT
+#endif
+
#include <assert.h>
#include <errno.h>
#include <limits.h>
# include <pthread.h>
# include <sched.h>
# endif
-#define GIT_STDLIB_CALL
+
+#define GIT_LIBGIT2_CALL
+#define GIT_SYSTEM_CALL
#ifdef GIT_USE_STAT_ATIMESPEC
# define st_atim st_atimespec
#include "git2/types.h"
#include "git2/errors.h"
#include "errors.h"
-#include "thread-utils.h"
+#include "thread.h"
#include "integer.h"
#include "assert_safe.h"
+#include "utf8.h"
/*
* Include the declarations for deprecated functions; this ensures
#include "git2/config.h"
#include "git2/sys/config.h"
-#include "buf_text.h"
#include "config_backend.h"
#include "regexp.h"
#include "sysdir.h"
struct stat st;
int res;
- assert(cfg && path);
+ GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(path);
res = p_stat(path, &st);
if (res < 0 && errno != ENOENT && errno != ENOTDIR) {
backend_internal *internal;
int result;
- assert(cfg && backend);
+ GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(backend);
GIT_ERROR_CHECK_VERSION(backend, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
void *payload)
{
git_config_entry *entry;
- git_config_iterator* iter;
+ git_config_iterator *iter;
git_regexp regex;
int error = 0;
- assert(backend && cb);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(cb);
if (regexp && git_regexp_compile(®ex, regexp, 0) < 0)
return -1;
int ret;
const char *str;
- git_buf_sanitize(out);
+ if ((ret = git_buf_sanitize(out)) < 0)
+ return ret;
ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
str = !ret ? (entry->value ? entry->value : "") : NULL;
int git_config_find_global(git_buf *path)
{
- git_buf_sanitize(path);
+ int error;
+
+ if ((error = git_buf_sanitize(path)) < 0)
+ return error;
+
return git_sysdir_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL);
}
int git_config_find_xdg(git_buf *path)
{
- git_buf_sanitize(path);
+ int error;
+
+ if ((error = git_buf_sanitize(path)) < 0)
+ return error;
+
return git_sysdir_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG);
}
int git_config_find_system(git_buf *path)
{
- git_buf_sanitize(path);
+ int error;
+
+ if ((error = git_buf_sanitize(path)) < 0)
+ return error;
+
return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
}
{
int ret;
- git_buf_sanitize(path);
+ if ((ret = git_buf_sanitize(path)) < 0)
+ return ret;
+
ret = git_sysdir_find_programdata_file(path,
GIT_CONFIG_FILENAME_PROGRAMDATA);
if (ret != GIT_OK)
git_config_backend *backend;
backend_internal *internal;
- assert(cfg);
+ GIT_ASSERT_ARG(cfg);
internal = git_vector_get(&cfg->backends, 0);
if (!internal || !internal->backend) {
git_config_backend *backend;
backend_internal *internal;
- assert(cfg);
+ GIT_ASSERT_ARG(cfg);
internal = git_vector_get(&cfg->backends, 0);
if (!internal || !internal->backend) {
int git_config_parse_path(git_buf *out, const char *value)
{
- assert(out && value);
+ int error;
- git_buf_sanitize(out);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(value);
+
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
if (value[0] == '~') {
if (value[1] != '\0' && value[1] != '/') {
{
char *name, *fdot, *ldot;
- assert(in && out);
+ GIT_ASSERT_ARG(in);
+ GIT_ASSERT_ARG(out);
name = git__strdup(in);
GIT_ERROR_CHECK_ALLOC(name);
int error = 0;
struct rename_data data;
- git_buf_text_puts_escape_regex(&pattern, old_section_name);
+ git_buf_puts_escape_regex(&pattern, old_section_name);
if ((error = git_buf_puts(&pattern, "\\..+")) < 0)
goto cleanup;
{"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT },
{"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT },
{"core.fsyncobjectfiles", NULL, 0, GIT_FSYNCOBJECTFILES_DEFAULT },
+ {"core.longpaths", NULL, 0, GIT_LONGPATHS_DEFAULT },
};
int git_config__configmap_lookup(int *out, git_config *config, git_configmap_item item)
int git_repository__configmap_lookup(int *out, git_repository *repo, git_configmap_item item)
{
- *out = repo->configmap_cache[(int)item];
+ intptr_t value = (intptr_t)git_atomic_load(repo->configmap_cache[(int)item]);
- if (*out == GIT_CONFIGMAP_NOT_CACHED) {
- int error;
+ *out = (int)value;
+
+ if (value == GIT_CONFIGMAP_NOT_CACHED) {
git_config *config;
+ intptr_t oldval = value;
+ int error;
if ((error = git_repository_config__weakptr(&config, repo)) < 0 ||
(error = git_config__configmap_lookup(out, config, item)) < 0)
return error;
- repo->configmap_cache[(int)item] = *out;
+ value = *out;
+ git_atomic_compare_and_swap(&repo->configmap_cache[(int)item], (void *)oldval, (void *)value);
}
return 0;
return error;
}
+static void config_file_clear_includes(config_file_backend *cfg)
+{
+ config_file *include;
+ uint32_t i;
+
+ git_array_foreach(cfg->file.includes, i, include)
+ config_file_clear(include);
+ git_array_clear(cfg->file.includes);
+}
+
static int config_file_set_entries(git_config_backend *cfg, git_config_entries *entries)
{
config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
git_config_entries *old = NULL;
- config_file *include;
int error;
- uint32_t i;
if (b->parent.readonly) {
git_error_set(GIT_ERROR_CONFIG, "this backend is read-only");
return -1;
}
- git_array_foreach(b->file.includes, i, include)
- config_file_clear(include);
- git_array_clear(b->file.includes);
-
if ((error = git_mutex_lock(&b->values_mutex)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to lock config backend");
goto out;
git_config_entries *entries = NULL;
int error;
+ config_file_clear_includes(b);
+
if ((error = git_config_entries_new(&entries)) < 0 ||
(error = config_file_read_buffer(entries, b->repo, &b->file,
b->level, 0, buf, buflen)) < 0 ||
if (!modified)
return 0;
+ config_file_clear_includes(b);
+
if ((error = git_config_entries_new(&entries)) < 0 ||
(error = config_file_read(entries, b->repo, &b->file, b->level, 0)) < 0 ||
(error = config_file_set_entries(cfg, entries)) < 0)
int result;
char *key;
- assert(regexp);
+ GIT_ASSERT_ARG(regexp);
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
size_t len;
const char *esc;
- assert(ptr);
+ GIT_ASSERT_ARG_WITH_RETVAL(ptr, NULL);
len = strlen(ptr);
if (!len)
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
-static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char* value)
+static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char *value)
{
char *orig_section = NULL, *section = NULL, *orig_name, *name, *ldot;
#include "config_parse.h"
-#include "buf_text.h"
-
#include <ctype.h>
const char *git_config_escapes = "ntb\"\\";
/* Make sure we were given a section header */
c = line[pos++];
- assert(c == '[');
+ GIT_ASSERT(c == '[');
c = line[pos++];
static int skip_bom(git_parse_ctx *parser)
{
git_buf buf = GIT_BUF_INIT_CONST(parser->content, parser->content_len);
- git_bom_t bom;
- int bom_offset = git_buf_text_detect_bom(&bom, &buf);
+ git_buf_bom_t bom;
+ int bom_offset = git_buf_detect_bom(&bom, &buf);
- if (bom == GIT_BOM_UTF8)
+ if (bom == GIT_BUF_BOM_UTF8)
git_parse_advance_chars(parser, bom_offset);
/* TODO: reference implementation is pretty stupid with BoM */
}
/* If it was just a comment, pretend it didn't exist */
- quote_count = strip_comments(line, !!in_quotes);
+ quote_count = strip_comments(line, in_quotes);
if (line[0] == '\0')
goto next;
#include "futils.h"
#include "hash.h"
#include "filter.h"
-#include "buf_text.h"
#include "repository.h"
typedef enum {
if (ca->crlf_action == GIT_CRLF_BINARY || !git_buf_len(from))
return GIT_PASSTHROUGH;
- is_binary = git_buf_text_gather_stats(&stats, from, false);
+ is_binary = git_buf_gather_text_stats(&stats, from, false);
/* Heuristics to see if we can skip the conversion.
* Straight from Core Git.
return GIT_PASSTHROUGH;
/* Actually drop the carriage returns */
- return git_buf_text_crlf_to_lf(to, from);
+ return git_buf_crlf_to_lf(to, from);
}
static int crlf_apply_to_workdir(
if (git_buf_len(from) == 0 || output_eol(ca) != GIT_EOL_CRLF)
return GIT_PASSTHROUGH;
- is_binary = git_buf_text_gather_stats(&stats, from, false);
+ is_binary = git_buf_gather_text_stats(&stats, from, false);
/* If there are no LFs, or all LFs are part of a CRLF, nothing to do */
if (stats.lf == 0 || stats.lf == stats.crlf)
return GIT_PASSTHROUGH;
}
- return git_buf_text_lf_to_crlf(to, from);
+ return git_buf_lf_to_crlf(to, from);
}
static int convert_attrs(
return crlf_apply_to_odb(*payload, to, from, src);
}
+static int crlf_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, crlf_apply, NULL, payload, src, next);
+}
+
static void crlf_cleanup(
git_filter *self,
void *payload)
f->f.initialize = NULL;
f->f.shutdown = git_filter_free;
f->f.check = crlf_check;
- f->f.apply = crlf_apply;
+ f->f.stream = crlf_stream;
f->f.cleanup = crlf_cleanup;
return (git_filter *)f;
if (month > 0 && month < 13 && day > 0 && day < 32) {
struct tm check = *tm;
struct tm *r = (now_tm ? &check : tm);
- time_t specified;
+ git_time_t specified;
r->tm_mon = month - 1;
r->tm_mday = day;
while (tl->type) {
size_t len = strlen(tl->type);
if (match_string(date, tl->type) >= len-1) {
- update_tm(tm, now, tl->length * *num);
+ update_tm(tm, now, tl->length * (unsigned long)*num);
*num = 0;
*touched = 1;
return end;
struct tm gmt;
time_t t;
- assert(out && date);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(date);
t = (time_t) (date->time + date->offset * 60);
size_t git_delta_index_size(git_delta_index *index)
{
- assert(index);
+ GIT_ASSERT_ARG(index);
return index->memsize;
}
git_buf *buf,
int depth,
git_repository *repo,
- const git_oid* id,
+ const git_oid *id,
unsigned int abbrev_size)
{
int error, size = 0;
int error = -1;
git_describe_options normalized;
- assert(committish);
+ GIT_ASSERT_ARG(result);
+ GIT_ASSERT_ARG(committish);
data.result = git__calloc(1, sizeof(git_describe_result));
GIT_ERROR_CHECK_ALLOC(data.result);
struct commit_name *name;
git_describe_format_options opts;
- assert(out && result);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(result);
GIT_ERROR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options");
normalize_format_options(&opts, given);
- git_buf_sanitize(out);
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
if (opts.always_use_long_format && opts.abbreviated_size == 0) {
#include "diff.h"
-#include "git2/version.h"
-#include "diff_generate.h"
+#include "common.h"
#include "patch.h"
+#include "email.h"
#include "commit.h"
#include "index.h"
+#include "diff_generate.h"
+
+#include "git2/version.h"
+#include "git2/email.h"
struct patch_id_args {
git_hash_ctx ctx;
size_t git_diff_num_deltas(const git_diff *diff)
{
- assert(diff);
+ GIT_ASSERT_ARG(diff);
return diff->deltas.length;
}
size_t i, count = 0;
const git_diff_delta *delta;
- assert(diff);
+ GIT_ASSERT_ARG(diff);
git_vector_foreach(&diff->deltas, i, delta) {
count += (delta->status == type);
const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
{
- assert(diff);
+ GIT_ASSERT_ARG_WITH_RETVAL(diff, NULL);
return git_vector_get(&diff->deltas, idx);
}
int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff)
{
- assert(out);
+ GIT_ASSERT_ARG(out);
GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
out->stat_calls = diff->perf.stat_calls;
out->oid_calculations = diff->perf.oid_calculations;
git_diff_delta *delta;
size_t idx;
- assert(diff);
+ GIT_ASSERT_ARG(diff);
git_vector_foreach(&diff->deltas, idx, delta) {
git_patch *patch;
return error;
}
-static int diff_format_email_append_header_tobuf(
- git_buf *out,
- const git_oid *id,
- const git_signature *author,
- const char *summary,
- const char *body,
- size_t patch_no,
- size_t total_patches,
- bool exclude_patchno_marker)
-{
- char idstr[GIT_OID_HEXSZ + 1];
- char date_str[GIT_DATE_RFC2822_SZ];
- int error = 0;
-
- git_oid_fmt(idstr, id);
- idstr[GIT_OID_HEXSZ] = '\0';
-
- if ((error = git__date_rfc2822_fmt(date_str, sizeof(date_str),
- &author->when)) < 0)
- return error;
-
- error = git_buf_printf(out,
- "From %s Mon Sep 17 00:00:00 2001\n" \
- "From: %s <%s>\n" \
- "Date: %s\n" \
- "Subject: ",
- idstr,
- author->name, author->email,
- date_str);
-
- if (error < 0)
- return error;
-
- if (!exclude_patchno_marker) {
- if (total_patches == 1) {
- error = git_buf_puts(out, "[PATCH] ");
- } else {
- error = git_buf_printf(out, "[PATCH %"PRIuZ"/%"PRIuZ"] ",
- patch_no, total_patches);
- }
-
- if (error < 0)
- return error;
- }
-
- error = git_buf_printf(out, "%s\n\n", summary);
-
- if (body) {
- git_buf_puts(out, body);
-
- if (out->ptr[out->size - 1] != '\n')
- git_buf_putc(out, '\n');
- }
-
- return error;
-}
-
-static int diff_format_email_append_patches_tobuf(
- git_buf *out,
- git_diff *diff)
-{
- size_t i, deltas;
- int error = 0;
-
- deltas = git_diff_num_deltas(diff);
-
- for (i = 0; i < deltas; ++i) {
- git_patch *patch = NULL;
-
- if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
- error = git_patch_to_buf(out, patch);
-
- git_patch_free(patch);
-
- if (error < 0)
- break;
- }
-
- return error;
-}
+#ifndef GIT_DEPRECATE_HARD
int git_diff_format_email(
git_buf *out,
git_diff *diff,
const git_diff_format_email_options *opts)
{
- git_diff_stats *stats = NULL;
- char *summary = NULL, *loc = NULL;
- bool ignore_marker;
- unsigned int format_flags = 0;
- size_t allocsize;
+ git_email_create_options email_create_opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
int error;
- assert(out && diff && opts);
- assert(opts->summary && opts->id && opts->author);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+ GIT_ASSERT_ARG(opts && opts->summary && opts->id && opts->author);
GIT_ERROR_CHECK_VERSION(opts,
GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION,
"git_format_email_options");
- ignore_marker = (opts->flags &
- GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0;
-
- if (!ignore_marker) {
- if (opts->patch_no > opts->total_patches) {
- git_error_set(GIT_ERROR_INVALID,
- "patch %"PRIuZ" out of range. max %"PRIuZ,
- opts->patch_no, opts->total_patches);
- return -1;
- }
+ if ((opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0)
+ email_create_opts.subject_prefix = "";
- if (opts->patch_no == 0) {
- git_error_set(GIT_ERROR_INVALID,
- "invalid patch no %"PRIuZ". should be >0", opts->patch_no);
- return -1;
- }
- }
- /* the summary we receive may not be clean.
- * it could potentially contain new line characters
- * or not be set, sanitize, */
- if ((loc = strpbrk(opts->summary, "\r\n")) != NULL) {
- size_t offset = 0;
-
- if ((offset = (loc - opts->summary)) == 0) {
- git_error_set(GIT_ERROR_INVALID, "summary is empty");
- error = -1;
- goto on_error;
- }
-
- GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, offset, 1);
- summary = git__calloc(allocsize, sizeof(char));
- GIT_ERROR_CHECK_ALLOC(summary);
-
- strncpy(summary, opts->summary, offset);
- }
-
- error = diff_format_email_append_header_tobuf(out,
- opts->id, opts->author, summary == NULL ? opts->summary : summary,
- opts->body, opts->patch_no, opts->total_patches, ignore_marker);
-
- if (error < 0)
- goto on_error;
-
- format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
-
- if ((error = git_buf_puts(out, "---\n")) < 0 ||
- (error = git_diff_get_stats(&stats, diff)) < 0 ||
- (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) < 0 ||
- (error = git_buf_putc(out, '\n')) < 0 ||
- (error = diff_format_email_append_patches_tobuf(out, diff)) < 0)
- goto on_error;
-
- error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
-
-on_error:
- git__free(summary);
- git_diff_stats_free(stats);
+ error = git_email__append_from_diff(out, diff, opts->patch_no,
+ opts->total_patches, opts->id, opts->summary, opts->body,
+ opts->author, &email_create_opts);
return error;
}
const git_diff_options *diff_opts)
{
git_diff *diff = NULL;
- git_diff_format_email_options opts =
- GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ const git_oid *commit_id;
+ const char *summary, *body;
+ const git_signature *author;
int error;
- assert (out && repo && commit);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(commit);
+
+ commit_id = git_commit_id(commit);
+ summary = git_commit_summary(commit);
+ body = git_commit_body(commit);
+ author = git_commit_author(commit);
- opts.flags = flags;
- opts.patch_no = patch_no;
- opts.total_patches = total_patches;
- opts.id = git_commit_id(commit);
- opts.summary = git_commit_summary(commit);
- opts.body = git_commit_body(commit);
- opts.author = git_commit_author(commit);
+ if ((flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0)
+ opts.subject_prefix = "";
if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
return error;
- error = git_diff_format_email(out, diff, &opts);
+ error = git_email_create_from_diff(out, diff, patch_no, total_patches, commit_id, summary, body, author, &opts);
git_diff_free(diff);
return error;
}
-int git_diff_options_init(git_diff_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
- return 0;
-}
-
-#ifndef GIT_DEPRECATE_HARD
int git_diff_init_options(git_diff_options *opts, unsigned int version)
{
return git_diff_options_init(opts, version);
}
-#endif
-int git_diff_find_options_init(
- git_diff_find_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
- return 0;
-}
-
-#ifndef GIT_DEPRECATE_HARD
int git_diff_find_init_options(
git_diff_find_options *opts, unsigned int version)
{
return git_diff_find_options_init(opts, version);
}
-#endif
int git_diff_format_email_options_init(
git_diff_format_email_options *opts, unsigned int version)
return 0;
}
-#ifndef GIT_DEPRECATE_HARD
int git_diff_format_email_init_options(
git_diff_format_email_options *opts, unsigned int version)
{
return git_diff_format_email_options_init(opts, version);
}
+
#endif
+int git_diff_options_init(git_diff_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
+ return 0;
+}
+
+int git_diff_find_options_init(
+ git_diff_find_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
+ return 0;
+}
+
static int flush_hunk(git_oid *result, git_hash_ctx *ctx)
{
git_oid hash;
#include "diff.h"
#include "strmap.h"
#include "map.h"
-#include "buf_text.h"
#include "config.h"
#include "regexp.h"
#include "repository.h"
static git_diff_driver_registry *git_repository_driver_registry(
git_repository *repo)
{
- if (!repo->diff_drivers) {
- git_diff_driver_registry *reg = git_diff_driver_registry_new();
- reg = git__compare_and_swap(&repo->diff_drivers, NULL, reg);
+ git_diff_driver_registry *reg = git_atomic_load(repo->diff_drivers), *newreg;
+ if (reg)
+ return reg;
- if (reg != NULL) /* if we race, free losing allocation */
- git_diff_driver_registry_free(reg);
- }
-
- if (!repo->diff_drivers)
+ newreg = git_diff_driver_registry_new();
+ if (!newreg) {
git_error_set(GIT_ERROR_REPOSITORY, "unable to create diff driver registry");
-
- return repo->diff_drivers;
+ return newreg;
+ }
+ reg = git_atomic_compare_and_swap(&repo->diff_drivers, NULL, newreg);
+ if (!reg) {
+ reg = newreg;
+ } else {
+ /* if we race, free losing allocation */
+ git_diff_driver_registry_free(newreg);
+ }
+ return reg;
}
static int diff_driver_alloc(
int error = 0;
const char *values[1], *attrs[] = { "diff" };
- assert(out);
+ GIT_ASSERT_ARG(out);
*out = NULL;
if (!repo || !path || !strlen(path))
void git_diff_driver_free(git_diff_driver *driver)
{
- size_t i;
+ git_diff_driver_pattern *pat;
if (!driver)
return;
- for (i = 0; i < git_array_size(driver->fn_patterns); ++i)
- git_regexp_dispose(& git_array_get(driver->fn_patterns, i)->re);
+ while ((pat = git_array_pop(driver->fn_patterns)) != NULL)
+ git_regexp_dispose(&pat->re);
git_array_clear(driver->fn_patterns);
git_regexp_dispose(&driver->word_pattern);
* let's just use the simple NUL-byte detection that core git uses.
*/
- /* previously was: if (git_buf_text_is_binary(&search)) */
- if (git_buf_text_contains_nul(&search))
+ /* previously was: if (git_buf_is_binary(&search)) */
+ if (git_buf_contains_nul(&search))
return 1;
return 0;
if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) {
git_buf out = GIT_BUF_INIT;
- error = git_filter_list_apply_to_data(&out, fl, &raw);
-
- if (out.ptr != raw.ptr)
- git_buf_dispose(&raw);
+ error = git_filter_list__convert_buf(&out, fl, &raw);
if (!error) {
fc->map.len = out.size;
if (fc->file->mode == GIT_FILEMODE_TREE)
return 0;
- if (git_buf_joinpath(
- &path, git_repository_workdir(fc->repo), fc->file->path) < 0)
+ if (git_repository_workdir_path(&path, fc->repo, fc->file->path) < 0)
return -1;
if (S_ISLNK(fc->file->mode))
git_diff_delta *delta;
const char *matched_pathspec;
- assert((oitem != NULL) ^ (nitem != NULL));
+ GIT_ASSERT_ARG((oitem != NULL) ^ (nitem != NULL));
if (oitem) {
entry = oitem;
GIT_ERROR_CHECK_ALLOC(delta);
/* This fn is just for single-sided diffs */
- assert(status != GIT_DELTA_MODIFIED);
+ GIT_ASSERT(status != GIT_DELTA_MODIFIED);
delta->nfiles = 1;
if (has_old) {
git_diff_generated *diff;
git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
- assert(repo && old_iter && new_iter);
+ GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(old_iter, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(new_iter, NULL);
if ((diff = git__calloc(1, sizeof(git_diff_generated))) == NULL)
return NULL;
git_filter_list *fl = NULL;
int error = 0;
- assert(d->type == GIT_DIFF_TYPE_GENERATED);
+ GIT_ASSERT(d->type == GIT_DIFF_TYPE_GENERATED);
diff = (git_diff_generated *)d;
memset(out, 0, sizeof(*out));
- if (git_buf_joinpath(&full_path,
- git_repository_workdir(diff->base.repo), entry.path) < 0)
+ if (git_repository_workdir_path(&full_path, diff->base.repo, entry.path) < 0)
return -1;
if (!mode) {
git_iterator *new_iter;
const git_index_entry *oitem;
const git_index_entry *nitem;
+ git_strmap *submodule_cache;
+ bool submodule_cache_initialized;
} diff_in_progress;
#define MODE_BITS_MASK 0000777
git_submodule *sub;
unsigned int sm_status = 0;
git_submodule_ignore_t ign = diff->base.opts.ignore_submodules;
+ git_strmap *submodule_cache = NULL;
*status = GIT_DELTA_UNMODIFIED;
ign == GIT_SUBMODULE_IGNORE_ALL)
return 0;
- if ((error = git_submodule_lookup(
- &sub, diff->base.repo, info->nitem->path)) < 0) {
+ if (diff->base.repo->submodule_cache != NULL) {
+ submodule_cache = diff->base.repo->submodule_cache;
+ } else {
+ if (!info->submodule_cache_initialized) {
+ info->submodule_cache_initialized = true;
+ /*
+ * Try to cache the submodule information to avoid having to parse it for
+ * every submodule. It is okay if it fails, the cache will still be NULL
+ * and the submodules will be attempted to be looked up individually.
+ */
+ git_submodule_cache_init(&info->submodule_cache, diff->base.repo);
+ }
+ submodule_cache = info->submodule_cache;
+ }
+
+ if ((error = git_submodule__lookup_with_cache(
+ &sub, diff->base.repo, info->nitem->path, submodule_cache)) < 0) {
/* GIT_EEXISTS means dir with .git in it was found - ignore it */
if (error == GIT_EEXISTS) {
const git_diff_options *opts)
{
git_diff_generated *diff;
- diff_in_progress info;
+ diff_in_progress info = {0};
int error = 0;
*out = NULL;
/* make iterators have matching icase behavior */
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
- git_iterator_set_ignore_case(old_iter, true);
- git_iterator_set_ignore_case(new_iter, true);
+ if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
+ (error = git_iterator_set_ignore_case(new_iter, true)) < 0)
+ goto cleanup;
}
/* finish initialization */
*out = &diff->base;
else
git_diff_free(&diff->base);
+ if (info.submodule_cache)
+ git_submodule_cache_free(info.submodule_cache);
return error;
}
char *prefix = NULL;
int error = 0;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
*out = NULL;
bool index_ignore_case = false;
int error = 0;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
*out = NULL;
char *prefix = NULL;
int error = 0;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
*out = NULL;
git_index *index;
int error;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
*out = NULL;
git_index *index = NULL;
int error = 0;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
*out = NULL;
char *prefix = NULL;
int error;
- assert(out && old_index && new_index);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(old_index);
+ GIT_ASSERT_ARG(new_index);
*out = NULL;
git_diff_line_cb cb,
void *payload)
{
- assert(patch);
+ GIT_ASSERT_ARG(patch);
memset(pi, 0, sizeof(diff_print_info));
/* print a git_diff to a git_buf */
int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
{
- assert(out && diff);
- git_buf_sanitize(out);
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
+
return git_diff_print(diff, format, git_diff_print_callback__to_buf, out);
}
diff_print_info pi;
int error;
- assert(patch && print_cb);
+ GIT_ASSERT_ARG(patch);
+ GIT_ASSERT_ARG(print_cb);
if ((error = diff_print_info_init_frompatch(&pi, &temp, patch,
GIT_DIFF_FORMAT_PATCH, print_cb, payload)) < 0)
/* print a git_patch to a git_buf */
int git_patch_to_buf(git_buf *out, git_patch *patch)
{
- assert(out && patch);
- git_buf_sanitize(out);
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(patch);
+
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
+
return git_patch_print(patch, git_diff_print_callback__to_buf, out);
}
git_diff_stats *stats = NULL;
int error = 0;
- assert(out && diff);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
stats = git__calloc(1, sizeof(git_diff_stats));
GIT_ERROR_CHECK_ALLOC(stats);
size_t git_diff_stats_files_changed(
const git_diff_stats *stats)
{
- assert(stats);
+ GIT_ASSERT_ARG(stats);
return stats->files_changed;
}
size_t git_diff_stats_insertions(
const git_diff_stats *stats)
{
- assert(stats);
+ GIT_ASSERT_ARG(stats);
return stats->insertions;
}
size_t git_diff_stats_deletions(
const git_diff_stats *stats)
{
- assert(stats);
+ GIT_ASSERT_ARG(stats);
return stats->deletions;
}
size_t i;
const git_diff_delta *delta;
- assert(out && stats);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(stats);
if (format & GIT_DIFF_STATS_NUMBER) {
for (i = 0; i < stats->files_changed; ++i) {
a->status == GIT_DELTA_UNREADABLE)
return dup;
- assert(b->status != GIT_DELTA_UNMODIFIED);
+ GIT_ASSERT_WITH_RETVAL(b->status != GIT_DELTA_UNMODIFIED, NULL);
/* A cgit exception is that the diff of a file that is only in the
* index (i.e. not in HEAD nor workdir) is given as empty.
bool ignore_case, reversed;
unsigned int i, j;
- assert(onto && from);
+ GIT_ASSERT_ARG(onto);
+ GIT_ASSERT_ARG(from);
if (!from->deltas.length)
return 0;
git_diff_file *file = info->file;
if (info->src == GIT_ITERATOR_WORKDIR) {
- if ((error = git_buf_joinpath(
- &info->data, git_repository_workdir(info->repo), file->path)) < 0)
+ if ((error = git_repository_workdir_path(
+ &info->data, info->repo, file->path)) < 0)
return error;
/* if path is not a regular file, just skip this item */
diff_find_match *best_match;
git_diff_file swap;
- assert(diff);
+ GIT_ASSERT_ARG(diff);
if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
return error;
src->flags |= GIT_DIFF_FLAG__TO_DELETE;
num_rewrites++;
} else {
- assert(delta_is_split(tgt));
+ GIT_ASSERT(delta_is_split(tgt));
if (best_match->similarity < opts.rename_from_rewrite_threshold)
continue;
delta_make_rename(tgt, src, best_match->similarity);
num_rewrites--;
- assert(src->status == GIT_DELTA_DELETED);
+ GIT_ASSERT(src->status == GIT_DELTA_DELETED);
memcpy(&src->old_file, &swap, sizeof(src->old_file));
memset(&src->new_file, 0, sizeof(src->new_file));
src->new_file.path = src->old_file.path;
num_updates++;
} else {
- assert(delta_is_split(src));
+ GIT_ASSERT(delta_is_split(src));
if (best_match->similarity < opts.rename_from_rewrite_threshold)
continue;
memcpy(&src->old_file, &swap, sizeof(src->old_file));
/* if we've just swapped the new element into the correct
- * place, clear the SPLIT flag
+ * place, clear the SPLIT and RENAME_TARGET flags
*/
if (tgt2src[s].idx == t &&
tgt2src[s].similarity >
src->status = GIT_DELTA_RENAMED;
src->similarity = tgt2src[s].similarity;
tgt2src[s].similarity = 0;
- src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+ src->flags &= ~(GIT_DIFF_FLAG__TO_SPLIT | GIT_DIFF_FLAG__IS_RENAME_TARGET);
num_rewrites--;
}
/* otherwise, if we just overwrote a source, update mapping */
*/
#include "diff_xdiff.h"
-#include "util.h"
#include "git2/errors.h"
#include "diff.h"
info->hunk.header_len = sizeof(info->hunk.header) - 1;
/* Sanitize the hunk header in case there is invalid Unicode */
- buffer_len = git__utf8_valid_buf_length((const uint8_t *) bufs[0].ptr, info->hunk.header_len);
+ buffer_len = git_utf8_valid_buf_length(bufs[0].ptr, info->hunk.header_len);
/* Sanitizing the hunk header may delete the newline, so add it back again if there is room */
if (buffer_len < info->hunk.header_len) {
bufs[0].ptr[buffer_len] = '\n';
if (flags & GIT_DIFF_MINIMAL)
xo->params.flags |= XDF_NEED_MINIMAL;
+ if (flags & GIT_DIFF_IGNORE_BLANK_LINES)
+ xo->params.flags |= XDF_IGNORE_BLANK_LINES;
+
xo->callback.outf = git_xdiff_cb;
}
/* xdiff cannot cope with large files. these files should not be passed to
* xdiff. callers should treat these large files as binary.
*/
-#define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023)
+#define GIT_XDIFF_MAX_SIZE (INT64_C(1024) * 1024 * 1023)
/* A git_xdiff_output is a git_patch_generate_output with extra fields
* necessary to use libxdiff. Calling git_xdiff_init() will set the diff_cb
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "email.h"
+
+#include "buffer.h"
+#include "common.h"
+#include "diff_generate.h"
+
+#include "git2/email.h"
+#include "git2/patch.h"
+#include "git2/version.h"
+
+/*
+ * Git uses a "magic" timestamp to indicate that an email message
+ * is from `git format-patch` (or our equivalent).
+ */
+#define EMAIL_TIMESTAMP "Mon Sep 17 00:00:00 2001"
+
+GIT_INLINE(int) include_prefix(
+ size_t patch_count,
+ git_email_create_options *opts)
+{
+ return ((!opts->subject_prefix || *opts->subject_prefix) ||
+ (opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 ||
+ opts->reroll_number ||
+ (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS)));
+}
+
+static int append_prefix(
+ git_buf *out,
+ size_t patch_idx,
+ size_t patch_count,
+ git_email_create_options *opts)
+{
+ const char *subject_prefix = opts->subject_prefix ?
+ opts->subject_prefix : "PATCH";
+
+ git_buf_putc(out, '[');
+
+ if (*subject_prefix)
+ git_buf_puts(out, subject_prefix);
+
+ if (opts->reroll_number) {
+ if (*subject_prefix)
+ git_buf_putc(out, ' ');
+
+ git_buf_printf(out, "v%" PRIuZ, opts->reroll_number);
+ }
+
+ if ((opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 ||
+ (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS))) {
+ size_t start_number = opts->start_number ?
+ opts->start_number : 1;
+
+ if (*subject_prefix || opts->reroll_number)
+ git_buf_putc(out, ' ');
+
+ git_buf_printf(out, "%" PRIuZ "/%" PRIuZ,
+ patch_idx + (start_number - 1),
+ patch_count + (start_number - 1));
+ }
+
+ git_buf_puts(out, "]");
+
+ return git_buf_oom(out) ? -1 : 0;
+}
+
+static int append_subject(
+ git_buf *out,
+ size_t patch_idx,
+ size_t patch_count,
+ const char *summary,
+ git_email_create_options *opts)
+{
+ bool prefix = include_prefix(patch_count, opts);
+ size_t summary_len = summary ? strlen(summary) : 0;
+ int error;
+
+ if (summary_len) {
+ const char *nl = strchr(summary, '\n');
+
+ if (nl)
+ summary_len = (nl - summary);
+ }
+
+ if ((error = git_buf_puts(out, "Subject: ")) < 0)
+ return error;
+
+ if (prefix &&
+ (error = append_prefix(out, patch_idx, patch_count, opts)) < 0)
+ return error;
+
+ if (prefix && summary_len && (error = git_buf_putc(out, ' ')) < 0)
+ return error;
+
+ if (summary_len &&
+ (error = git_buf_put(out, summary, summary_len)) < 0)
+ return error;
+
+ return git_buf_putc(out, '\n');
+}
+
+static int append_header(
+ git_buf *out,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const git_signature *author,
+ git_email_create_options *opts)
+{
+ char id[GIT_OID_HEXSZ];
+ char date[GIT_DATE_RFC2822_SZ];
+ int error;
+
+ if ((error = git_oid_fmt(id, commit_id)) < 0 ||
+ (error = git_buf_printf(out, "From %.*s %s\n", GIT_OID_HEXSZ, id, EMAIL_TIMESTAMP)) < 0 ||
+ (error = git_buf_printf(out, "From: %s <%s>\n", author->name, author->email)) < 0 ||
+ (error = git__date_rfc2822_fmt(date, sizeof(date), &author->when)) < 0 ||
+ (error = git_buf_printf(out, "Date: %s\n", date)) < 0 ||
+ (error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0)
+ return error;
+
+ if ((error = git_buf_putc(out, '\n')) < 0)
+ return error;
+
+ return 0;
+}
+
+static int append_body(git_buf *out, const char *body)
+{
+ size_t body_len;
+ int error;
+
+ if (!body)
+ return 0;
+
+ body_len = strlen(body);
+
+ if ((error = git_buf_puts(out, body)) < 0)
+ return error;
+
+ if (body_len && body[body_len - 1] != '\n')
+ error = git_buf_putc(out, '\n');
+
+ return error;
+}
+
+static int append_diffstat(git_buf *out, git_diff *diff)
+{
+ git_diff_stats *stats = NULL;
+ unsigned int format_flags;
+ int error;
+
+ format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
+
+ if ((error = git_diff_get_stats(&stats, diff)) == 0 &&
+ (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) == 0)
+ error = git_buf_putc(out, '\n');
+
+ git_diff_stats_free(stats);
+ return error;
+}
+
+static int append_patches(git_buf *out, git_diff *diff)
+{
+ size_t i, deltas;
+ int error = 0;
+
+ deltas = git_diff_num_deltas(diff);
+
+ for (i = 0; i < deltas; ++i) {
+ git_patch *patch = NULL;
+
+ if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
+ error = git_patch_to_buf(out, patch);
+
+ git_patch_free(patch);
+
+ if (error < 0)
+ break;
+ }
+
+ return error;
+}
+
+int git_email__append_from_diff(
+ git_buf *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *given_opts)
+{
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+ GIT_ASSERT_ARG(!patch_idx || patch_idx <= patch_count);
+ GIT_ASSERT_ARG(commit_id);
+ GIT_ASSERT_ARG(author);
+
+ GIT_ERROR_CHECK_VERSION(given_opts,
+ GIT_EMAIL_CREATE_OPTIONS_VERSION,
+ "git_email_create_options");
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_email_create_options));
+
+ git_buf_sanitize(out);
+
+ if ((error = append_header(out, patch_idx, patch_count, commit_id, summary, author, &opts)) == 0 &&
+ (error = append_body(out, body)) == 0 &&
+ (error = git_buf_puts(out, "---\n")) == 0 &&
+ (error = append_diffstat(out, diff)) == 0 &&
+ (error = append_patches(out, diff)) == 0)
+ error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
+
+ return error;
+}
+
+int git_email_create_from_diff(
+ git_buf *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *given_opts)
+{
+ int error;
+
+ git_buf_sanitize(out);
+ git_buf_clear(out);
+
+ error = git_email__append_from_diff(out, diff, patch_idx,
+ patch_count, commit_id, summary, body, author,
+ given_opts);
+
+ return error;
+}
+
+int git_email_create_from_commit(
+ git_buf *out,
+ git_commit *commit,
+ const git_email_create_options *given_opts)
+{
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_repository *repo;
+ git_diff_options *diff_opts;
+ git_diff_find_options *find_opts;
+ const git_signature *author;
+ const char *summary, *body;
+ const git_oid *commit_id;
+ int error = -1;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(commit);
+
+ GIT_ERROR_CHECK_VERSION(given_opts,
+ GIT_EMAIL_CREATE_OPTIONS_VERSION,
+ "git_email_create_options");
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_email_create_options));
+
+ repo = git_commit_owner(commit);
+ author = git_commit_author(commit);
+ summary = git_commit_summary(commit);
+ body = git_commit_body(commit);
+ commit_id = git_commit_id(commit);
+ diff_opts = &opts.diff_opts;
+ find_opts = &opts.diff_find_opts;
+
+ if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
+ goto done;
+
+ if ((opts.flags & GIT_EMAIL_CREATE_NO_RENAMES) == 0 &&
+ (error = git_diff_find_similar(diff, find_opts)) < 0)
+ goto done;
+
+ error = git_email_create_from_diff(out, diff, 1, 1, commit_id, summary, body, author, &opts);
+
+done:
+ git_diff_free(diff);
+ return error;
+}
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_email_h__
+#define INCLUDE_email_h__
+
+#include "common.h"
+
+#include "git2/email.h"
+
+extern int git_email__append_from_diff(
+ git_buf *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *given_opts);
+
+#endif
#include "common.h"
-#include "global.h"
+#include "threadstate.h"
#include "posix.h"
#include "buffer.h"
+#include "libgit2.h"
/********************************************
* New error handling
GIT_ERROR_NOMEMORY
};
+static git_error g_git_uninitialized_error = {
+ "libgit2 has not been initialized; you must call git_libgit2_init",
+ GIT_ERROR_INVALID
+};
+
static void set_error_from_buffer(int error_class)
{
- git_error *error = &GIT_GLOBAL->error_t;
- git_buf *buf = &GIT_GLOBAL->error_buf;
+ git_error *error = &GIT_THREADSTATE->error_t;
+ git_buf *buf = &GIT_THREADSTATE->error_buf;
error->message = buf->ptr;
error->klass = error_class;
- GIT_GLOBAL->last_error = error;
+ GIT_THREADSTATE->last_error = error;
}
static void set_error(int error_class, char *string)
{
- git_buf *buf = &GIT_GLOBAL->error_buf;
+ git_buf *buf = &GIT_THREADSTATE->error_buf;
git_buf_clear(buf);
if (string) {
void git_error_set_oom(void)
{
- GIT_GLOBAL->last_error = &g_git_oom_error;
+ GIT_THREADSTATE->last_error = &g_git_oom_error;
}
void git_error_set(int error_class, const char *fmt, ...)
DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0;
#endif
int error_code = (error_class == GIT_ERROR_OS) ? errno : 0;
- git_buf *buf = &GIT_GLOBAL->error_buf;
+ git_buf *buf = &GIT_THREADSTATE->error_buf;
git_buf_clear(buf);
if (fmt) {
int git_error_set_str(int error_class, const char *string)
{
- git_buf *buf = &GIT_GLOBAL->error_buf;
-
- assert(string);
+ git_buf *buf = &GIT_THREADSTATE->error_buf;
- if (!string) {
- git_error_set(GIT_ERROR_INVALID, "unspecified caller error");
- return -1;
- }
+ GIT_ASSERT_ARG(string);
git_buf_clear(buf);
git_buf_puts(buf, string);
void git_error_clear(void)
{
- if (GIT_GLOBAL->last_error != NULL) {
+ if (GIT_THREADSTATE->last_error != NULL) {
set_error(0, NULL);
- GIT_GLOBAL->last_error = NULL;
+ GIT_THREADSTATE->last_error = NULL;
}
errno = 0;
const git_error *git_error_last(void)
{
- return GIT_GLOBAL->last_error;
+ /* If the library is not initialized, return a static error. */
+ if (!git_libgit2_init_count())
+ return &g_git_uninitialized_error;
+
+ return GIT_THREADSTATE->last_error;
}
int git_error_state_capture(git_error_state *state, int error_code)
{
- git_error *error = GIT_GLOBAL->last_error;
- git_buf *error_buf = &GIT_GLOBAL->error_buf;
+ git_error *error = GIT_THREADSTATE->last_error;
+ git_buf *error_buf = &GIT_THREADSTATE->error_buf;
memset(state, 0, sizeof(git_error_state));
#define INCLUDE_features_h__
#cmakedefine GIT_DEBUG_POOL 1
+#cmakedefine GIT_DEBUG_STRICT_ALLOC 1
+#cmakedefine GIT_DEBUG_STRICT_OPEN 1
+
#cmakedefine GIT_TRACE 1
#cmakedefine GIT_THREADS 1
-#cmakedefine GIT_MSVC_CRTDBG 1
+#cmakedefine GIT_WIN32_LEAKCHECK 1
#cmakedefine GIT_ARCH_64 1
#cmakedefine GIT_ARCH_32 1
#cmakedefine GIT_WINHTTP 1
#cmakedefine GIT_HTTPS 1
#cmakedefine GIT_OPENSSL 1
+#cmakedefine GIT_OPENSSL_DYNAMIC 1
#cmakedefine GIT_SECURE_TRANSPORT 1
#cmakedefine GIT_MBEDTLS 1
static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec, git_remote_autotag_option_t tagopt)
{
- int match = 0;
+ int match = 0, valid;
- if (!git_reference_is_valid_name(head->name))
+ if (git_reference_name_is_valid(&valid, head->name) < 0)
+ return -1;
+
+ if (!valid)
return 0;
if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
{
git_fetchhead_ref *fetchhead_ref;
- assert(out && oid);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(oid);
*out = NULL;
const char *type, *name;
int head = 0;
- assert(file && fetchhead_ref);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(fetchhead_ref);
git_oid_fmt(oid, &fetchhead_ref->oid);
oid[GIT_OID_HEXSZ] = '\0';
unsigned int i;
git_fetchhead_ref *fetchhead_ref;
- assert(repo && fetchhead_refs);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(fetchhead_refs);
if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
return -1;
size_t line_num = 0;
int error = 0;
- assert(repo && cb);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(cb);
if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
return -1;
} while (zs->avail_out == 0);
- assert(zs->avail_in == 0);
+ GIT_ASSERT(zs->avail_in == 0);
if (file->compute_digest)
git_hash_update(&file->digest, source, len);
int compression, error = -1;
size_t path_len, alloc_len;
- /* opening an already open buffer is a programming error;
- * assert that this never happens instead of returning
- * an error code */
- assert(file && path && file->buffer == NULL);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(path);
+ GIT_ASSERT(file->buffer == NULL);
memset(file, 0x0, sizeof(git_filebuf));
int git_filebuf_hash(git_oid *oid, git_filebuf *file)
{
- assert(oid && file && file->compute_digest);
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(file->compute_digest);
flush_buffer(file);
int git_filebuf_commit(git_filebuf *file)
{
/* temporary files cannot be committed */
- assert(file && file->path_original);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT(file->path_original);
file->flush_mode = Z_FINISH;
flush_buffer(file);
#include "futils.h"
#include "hash.h"
#include "repository.h"
-#include "global.h"
+#include "runtime.h"
#include "git2/sys/filter.h"
#include "git2/config.h"
#include "blob.h"
#include "array.h"
struct git_filter_source {
- git_repository *repo;
- const char *path;
- git_oid oid; /* zero if unknown (which is likely) */
- uint16_t filemode; /* zero if unknown */
- git_filter_mode_t mode;
- uint32_t flags;
+ git_repository *repo;
+ const char *path;
+ git_oid oid; /* zero if unknown (which is likely) */
+ uint16_t filemode; /* zero if unknown */
+ git_filter_mode_t mode;
+ git_filter_options options;
};
typedef struct {
GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
error = -1;
- git__on_shutdown(git_filter_global_shutdown);
+ if (!error)
+ error = git_runtime_shutdown_register(git_filter_global_shutdown);
done:
if (error) {
{
int error;
- assert(name && filter);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(filter);
if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
git_filter_def *fdef;
int error = 0;
- assert(name);
+ GIT_ASSERT_ARG(name);
/* cannot unregister default filters */
if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
uint32_t git_filter_source_flags(const git_filter_source *src)
{
- return src->flags;
+ return src->options.flags;
}
static int filter_list_new(
fl->source.repo = src->repo;
fl->source.path = fl->path;
fl->source.mode = src->mode;
- fl->source.flags = src->flags;
+
+ memcpy(&fl->source.options, &src->options, sizeof(git_filter_options));
*out = fl;
return 0;
static int filter_list_check_attributes(
const char ***out,
git_repository *repo,
- git_attr_session *attr_session,
+ git_filter_session *filter_session,
git_filter_def *fdef,
const git_filter_source *src)
{
const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
- uint32_t flags = 0;
+ git_attr_options attr_opts = GIT_ATTR_OPTIONS_INIT;
size_t i;
int error;
GIT_ERROR_CHECK_ALLOC(strs);
- if ((src->flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
- flags |= GIT_ATTR_CHECK_NO_SYSTEM;
+ if ((src->options.flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
+ attr_opts.flags |= GIT_ATTR_CHECK_NO_SYSTEM;
+
+ if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
+ attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
+
+ if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) {
+ attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_COMMIT;
- if ((src->flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
- flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
+#ifndef GIT_DEPRECATE_HARD
+ if (src->options.commit_id)
+ git_oid_cpy(&attr_opts.attr_commit_id, src->options.commit_id);
+ else
+#endif
+ git_oid_cpy(&attr_opts.attr_commit_id, &src->options.attr_commit_id);
+ }
error = git_attr_get_many_with_session(
- strs, repo, attr_session, flags, src->path, fdef->nattrs, fdef->attrs);
+ strs, repo, filter_session->attr_session, &attr_opts, src->path, fdef->nattrs, fdef->attrs);
/* if no values were found but no matches are needed, it's okay! */
if (error == GIT_ENOTFOUND && !fdef->nmatches) {
src.repo = repo;
src.path = NULL;
src.mode = mode;
- src.flags = flags;
+ src.options.flags = flags;
return filter_list_new(out, &src);
}
-int git_filter_list__load_ext(
+int git_filter_list__load(
git_filter_list **filters,
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
git_filter_mode_t mode,
- git_filter_options *filter_opts)
+ git_filter_session *filter_session)
{
int error = 0;
git_filter_list *fl = NULL;
src.repo = repo;
src.path = path;
src.mode = mode;
- src.flags = filter_opts->flags;
+
+ memcpy(&src.options, &filter_session->options, sizeof(git_filter_options));
if (blob)
git_oid_cpy(&src.oid, git_blob_id(blob));
if (fdef->nattrs > 0) {
error = filter_list_check_attributes(
- &values, repo, filter_opts->attr_session, fdef, &src);
+ &values, repo,
+ filter_session, fdef, &src);
if (error == GIT_ENOTFOUND) {
error = 0;
if ((error = filter_list_new(&fl, &src)) < 0)
break;
- fl->temp_buf = filter_opts->temp_buf;
+ fl->temp_buf = filter_session->temp_buf;
}
fe = git_array_alloc(fl->filters);
return error;
}
+int git_filter_list_load_ext(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode,
+ git_filter_options *opts)
+{
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
+
+ if (opts)
+ memcpy(&filter_session.options, opts, sizeof(git_filter_options));
+
+ return git_filter_list__load(
+ filters, repo, blob, path, mode, &filter_session);
+}
+
int git_filter_list_load(
git_filter_list **filters,
git_repository *repo,
git_filter_mode_t mode,
uint32_t flags)
{
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
- filter_opts.flags = flags;
+ filter_session.options.flags = flags;
- return git_filter_list__load_ext(
- filters, repo, blob, path, mode, &filter_opts);
+ return git_filter_list__load(
+ filters, repo, blob, path, mode, &filter_session);
}
void git_filter_list_free(git_filter_list *fl)
{
size_t i;
- assert(name);
+ GIT_ASSERT_ARG(name);
if (!fl)
return 0;
git_filter_def *fdef = NULL;
git_filter_entry *fe;
- assert(fl && filter);
+ GIT_ASSERT_ARG(fl);
+ GIT_ASSERT_ARG(filter);
if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
git_writestream *s, const char *buffer, size_t len)
{
struct buf_stream *buf_stream = (struct buf_stream *)s;
- assert(buf_stream);
-
- assert(buf_stream->complete == 0);
+ GIT_ASSERT_ARG(buf_stream);
+ GIT_ASSERT(buf_stream->complete == 0);
return git_buf_put(buf_stream->target, buffer, len);
}
static int buf_stream_close(git_writestream *s)
{
struct buf_stream *buf_stream = (struct buf_stream *)s;
- assert(buf_stream);
+ GIT_ASSERT_ARG(buf_stream);
- assert(buf_stream->complete == 0);
+ GIT_ASSERT(buf_stream->complete == 0);
buf_stream->complete = 1;
return 0;
git_buf_clear(target);
}
-int git_filter_list_apply_to_data(
- git_buf *tgt, git_filter_list *filters, git_buf *src)
+int git_filter_list_apply_to_buffer(
+ git_buf *out,
+ git_filter_list *filters,
+ const char *in,
+ size_t in_len)
{
struct buf_stream writer;
int error;
- git_buf_sanitize(tgt);
- git_buf_sanitize(src);
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
- if (!filters) {
- git_buf_attach_notowned(tgt, src->ptr, src->size);
+ buf_stream_init(&writer, out);
+
+ if ((error = git_filter_list_stream_buffer(filters,
+ in, in_len, &writer.parent)) < 0)
+ return error;
+
+ GIT_ASSERT(writer.complete);
+ return error;
+}
+
+int git_filter_list__convert_buf(
+ git_buf *out,
+ git_filter_list *filters,
+ git_buf *in)
+{
+ int error;
+
+ if (!filters || git_filter_list_length(filters) == 0) {
+ git_buf_swap(out, in);
+ git_buf_dispose(in);
return 0;
}
- buf_stream_init(&writer, tgt);
+ error = git_filter_list_apply_to_buffer(out, filters,
+ in->ptr, in->size);
- if ((error = git_filter_list_stream_data(filters, src,
- &writer.parent)) < 0)
- return error;
+ if (!error)
+ git_buf_dispose(in);
- assert(writer.complete);
return error;
}
filters, repo, path, &writer.parent)) < 0)
return error;
- assert(writer.complete);
+ GIT_ASSERT(writer.complete);
return error;
}
filters, blob, &writer.parent)) < 0)
return error;
- assert(writer.complete);
+ GIT_ASSERT(writer.complete);
return error;
}
-struct proxy_stream {
+struct buffered_stream {
git_writestream parent;
git_filter *filter;
+ int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *);
const git_filter_source *source;
void **payload;
git_buf input;
git_writestream *target;
};
-static int proxy_stream_write(
+static int buffered_stream_write(
git_writestream *s, const char *buffer, size_t len)
{
- struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
- assert(proxy_stream);
+ struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
+ GIT_ASSERT_ARG(buffered_stream);
- return git_buf_put(&proxy_stream->input, buffer, len);
+ return git_buf_put(&buffered_stream->input, buffer, len);
}
-static int proxy_stream_close(git_writestream *s)
+static int buffered_stream_close(git_writestream *s)
{
- struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
+ struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
git_buf *writebuf;
git_error_state error_state = {0};
int error;
- assert(proxy_stream);
+ GIT_ASSERT_ARG(buffered_stream);
- error = proxy_stream->filter->apply(
- proxy_stream->filter,
- proxy_stream->payload,
- proxy_stream->output,
- &proxy_stream->input,
- proxy_stream->source);
+ error = buffered_stream->write_fn(
+ buffered_stream->filter,
+ buffered_stream->payload,
+ buffered_stream->output,
+ &buffered_stream->input,
+ buffered_stream->source);
if (error == GIT_PASSTHROUGH) {
- writebuf = &proxy_stream->input;
+ writebuf = &buffered_stream->input;
} else if (error == 0) {
- git_buf_sanitize(proxy_stream->output);
- writebuf = proxy_stream->output;
+ if ((error = git_buf_sanitize(buffered_stream->output)) < 0)
+ return error;
+
+ writebuf = buffered_stream->output;
} else {
/* close stream before erroring out taking care
* to preserve the original error */
git_error_state_capture(&error_state, error);
- proxy_stream->target->close(proxy_stream->target);
+ buffered_stream->target->close(buffered_stream->target);
git_error_state_restore(&error_state);
return error;
}
- if ((error = proxy_stream->target->write(
- proxy_stream->target, writebuf->ptr, writebuf->size)) == 0)
- error = proxy_stream->target->close(proxy_stream->target);
+ if ((error = buffered_stream->target->write(
+ buffered_stream->target, writebuf->ptr, writebuf->size)) == 0)
+ error = buffered_stream->target->close(buffered_stream->target);
return error;
}
-static void proxy_stream_free(git_writestream *s)
+static void buffered_stream_free(git_writestream *s)
{
- struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
- assert(proxy_stream);
+ struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
- git_buf_dispose(&proxy_stream->input);
- git_buf_dispose(&proxy_stream->temp_buf);
- git__free(proxy_stream);
+ if (buffered_stream) {
+ git_buf_dispose(&buffered_stream->input);
+ git_buf_dispose(&buffered_stream->temp_buf);
+ git__free(buffered_stream);
+ }
}
-static int proxy_stream_init(
+int git_filter_buffered_stream_new(
git_writestream **out,
git_filter *filter,
+ int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *),
git_buf *temp_buf,
void **payload,
const git_filter_source *source,
git_writestream *target)
{
- struct proxy_stream *proxy_stream = git__calloc(1, sizeof(struct proxy_stream));
- GIT_ERROR_CHECK_ALLOC(proxy_stream);
-
- proxy_stream->parent.write = proxy_stream_write;
- proxy_stream->parent.close = proxy_stream_close;
- proxy_stream->parent.free = proxy_stream_free;
- proxy_stream->filter = filter;
- proxy_stream->payload = payload;
- proxy_stream->source = source;
- proxy_stream->target = target;
- proxy_stream->output = temp_buf ? temp_buf : &proxy_stream->temp_buf;
+ struct buffered_stream *buffered_stream = git__calloc(1, sizeof(struct buffered_stream));
+ GIT_ERROR_CHECK_ALLOC(buffered_stream);
+
+ buffered_stream->parent.write = buffered_stream_write;
+ buffered_stream->parent.close = buffered_stream_close;
+ buffered_stream->parent.free = buffered_stream_free;
+ buffered_stream->filter = filter;
+ buffered_stream->write_fn = write_fn;
+ buffered_stream->output = temp_buf ? temp_buf : &buffered_stream->temp_buf;
+ buffered_stream->payload = payload;
+ buffered_stream->source = source;
+ buffered_stream->target = target;
if (temp_buf)
git_buf_clear(temp_buf);
- *out = (git_writestream *)proxy_stream;
+ *out = (git_writestream *)buffered_stream;
return 0;
}
+static int setup_stream(
+ git_writestream **out,
+ git_filter_entry *fe,
+ git_filter_list *filters,
+ git_writestream *last_stream)
+{
+#ifndef GIT_DEPRECATE_HARD
+ GIT_ASSERT(fe->filter->stream || fe->filter->apply);
+
+ /*
+ * If necessary, create a stream that proxies the traditional
+ * application.
+ */
+ if (!fe->filter->stream) {
+ /* Create a stream that proxies the one-shot apply */
+ return git_filter_buffered_stream_new(out,
+ fe->filter, fe->filter->apply, filters->temp_buf,
+ &fe->payload, &filters->source, last_stream);
+ }
+#endif
+
+ GIT_ASSERT(fe->filter->stream);
+ return fe->filter->stream(out, fe->filter,
+ &fe->payload, &filters->source, last_stream);
+}
+
static int stream_list_init(
git_writestream **out,
git_vector *streams,
for (i = 0; i < git_array_size(filters->filters); ++i) {
size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ?
git_array_size(filters->filters) - 1 - i : i;
+
git_filter_entry *fe = git_array_get(filters->filters, filter_idx);
git_writestream *filter_stream;
- assert(fe->filter->stream || fe->filter->apply);
-
- /* If necessary, create a stream that proxies the traditional
- * application.
- */
- if (fe->filter->stream)
- error = fe->filter->stream(&filter_stream, fe->filter,
- &fe->payload, &filters->source, last_stream);
- else
- /* Create a stream that proxies the one-shot apply */
- error = proxy_stream_init(&filter_stream, fe->filter,
- filters->temp_buf, &fe->payload, &filters->source,
- last_stream);
+ error = setup_stream(&filter_stream, fe, filters, last_stream);
if (error < 0)
goto out;
if ((error = stream_list_init(
&stream_start, &filter_streams, filters, target)) < 0 ||
- (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0)
+ (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0 ||
+ (error = git_path_validate_workdir_buf(repo, &abspath)) < 0)
goto done;
+
initialized = 1;
if ((fd = git_futils_open_ro(abspath.ptr)) < 0) {
return error;
}
-int git_filter_list_stream_data(
+int git_filter_list_stream_buffer(
git_filter_list *filters,
- git_buf *data,
+ const char *buffer,
+ size_t len,
git_writestream *target)
{
git_vector filter_streams = GIT_VECTOR_INIT;
git_writestream *stream_start;
int error, initialized = 0;
- git_buf_sanitize(data);
-
if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0)
goto out;
initialized = 1;
- if ((error = stream_start->write(
- stream_start, data->ptr, data->size)) < 0)
+ if ((error = stream_start->write(stream_start, buffer, len)) < 0)
goto out;
out:
if (filters)
git_oid_cpy(&filters->source.oid, git_blob_id(blob));
- return git_filter_list_stream_data(filters, &in, target);
+ return git_filter_list_stream_buffer(filters, in.ptr, in.size, target);
}
int git_filter_init(git_filter *filter, unsigned int version)
GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter, version, git_filter, GIT_FILTER_INIT);
return 0;
}
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_filter_list_stream_data(
+ git_filter_list *filters,
+ git_buf *data,
+ git_writestream *target)
+{
+ int error;
+
+ if ((error = git_buf_sanitize(data)) < 0)
+ return error;
+
+ return git_filter_list_stream_buffer(filters, data->ptr, data->size, target);
+}
+
+int git_filter_list_apply_to_data(
+ git_buf *tgt, git_filter_list *filters, git_buf *src)
+{
+ int error;
+
+ if ((error = git_buf_sanitize(src)) < 0)
+ return error;
+
+ return git_filter_list_apply_to_buffer(tgt, filters, src->ptr, src->size);
+}
+
+#endif
#include "attr_file.h"
#include "git2/filter.h"
+#include "git2/sys/filter.h"
/* Amount of file to examine for NUL byte when checking binary-ness */
#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
typedef struct {
+ git_filter_options options;
git_attr_session *attr_session;
git_buf *temp_buf;
- uint32_t flags;
-} git_filter_options;
+} git_filter_session;
-#define GIT_FILTER_OPTIONS_INIT {0}
+#define GIT_FILTER_SESSION_INIT {GIT_FILTER_OPTIONS_INIT, 0}
extern int git_filter_global_init(void);
extern void git_filter_free(git_filter *filter);
-extern int git_filter_list__load_ext(
+extern int git_filter_list__load(
git_filter_list **filters,
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
git_filter_mode_t mode,
- git_filter_options *filter_opts);
+ git_filter_session *filter_session);
+
+/*
+ * The given input buffer will be converted to the given output buffer.
+ * The input buffer will be freed (_if_ it was allocated).
+ */
+extern int git_filter_list__convert_buf(
+ git_buf *out,
+ git_filter_list *filters,
+ git_buf *in);
/*
* Available filters
extern git_filter *git_crlf_filter_new(void);
extern git_filter *git_ident_filter_new(void);
+extern int git_filter_buffered_stream_new(
+ git_writestream **out,
+ git_filter *filter,
+ int (*write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *),
+ git_buf *temp_buf,
+ void **payload,
+ const git_filter_source *source,
+ git_writestream *target);
+
#endif
#include "futils.h"
-#include "global.h"
+#include "runtime.h"
#include "strmap.h"
+#include "hash.h"
#include <ctype.h>
#if GIT_WIN32
#include "win32/findfile.h"
git_buf buf = GIT_BUF_INIT;
git_oid checksum_new;
- assert(out && path && *path);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(path && *path);
if (updated != NULL)
*updated = 0;
goto done;
}
- assert(len);
+ GIT_ASSERT(len);
/*
* We've walked all the given path's parents and it's either relative
void git_futils_filestamp_set(
git_futils_filestamp *target, const git_futils_filestamp *source)
{
- assert(target);
-
if (source)
memcpy(target, source, sizeof(*target));
else
*/
extern int git_futils_filesize(uint64_t *out, git_file fd);
-#define GIT_PERMS_IS_EXEC(MODE) (((MODE) & 0111) != 0)
+#define GIT_PERMS_IS_EXEC(MODE) (((MODE) & 0100) != 0)
#define GIT_PERMS_CANONICAL(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0755 : 0644)
#define GIT_PERMS_FOR_WRITE(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0777 : 0666)
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "global.h"
-
-#include "alloc.h"
-#include "hash.h"
-#include "sysdir.h"
-#include "filter.h"
-#include "merge_driver.h"
-#include "pool.h"
-#include "streams/registry.h"
-#include "streams/mbedtls.h"
-#include "streams/openssl.h"
-#include "thread-utils.h"
-#include "git2/global.h"
-#include "transports/ssh.h"
-
-#if defined(GIT_MSVC_CRTDBG)
-#include "win32/w32_stack.h"
-#include "win32/w32_crtdbg_stacktrace.h"
-#endif
-
-git_mutex git__mwindow_mutex;
-
-typedef int (*git_global_init_fn)(void);
-
-static git_global_init_fn git__init_callbacks[] = {
- git_allocator_global_init,
- git_hash_global_init,
- git_sysdir_global_init,
- git_filter_global_init,
- git_merge_driver_global_init,
- git_transport_ssh_global_init,
- git_stream_registry_global_init,
- git_openssl_stream_global_init,
- git_mbedtls_stream_global_init,
- git_mwindow_global_init,
- git_pool_global_init
-};
-
-static git_global_shutdown_fn git__shutdown_callbacks[ARRAY_SIZE(git__init_callbacks)];
-
-static git_atomic git__n_shutdown_callbacks;
-static git_atomic git__n_inits;
-char *git__user_agent;
-char *git__ssl_ciphers;
-
-void git__on_shutdown(git_global_shutdown_fn callback)
-{
- int count = git_atomic_inc(&git__n_shutdown_callbacks);
- assert(count <= (int) ARRAY_SIZE(git__shutdown_callbacks) && count > 0);
- git__shutdown_callbacks[count - 1] = callback;
-}
-
-static void git__global_state_cleanup(git_global_st *st)
-{
- if (!st)
- return;
-
- git__free(st->error_t.message);
- st->error_t.message = NULL;
-}
-
-static int init_common(void)
-{
- size_t i;
- int ret;
-
- /* Initialize the CRT debug allocator first, before our first malloc */
-#if defined(GIT_MSVC_CRTDBG)
- git_win32__crtdbg_stacktrace_init();
- git_win32__stack_init();
-#endif
-
- /* Initialize subsystems that have global state */
- for (i = 0; i < ARRAY_SIZE(git__init_callbacks); i++)
- if ((ret = git__init_callbacks[i]()) != 0)
- break;
-
- GIT_MEMORY_BARRIER;
-
- return ret;
-}
-
-static void shutdown_common(void)
-{
- int pos;
-
- /* Shutdown subsystems that have registered */
- for (pos = git_atomic_get(&git__n_shutdown_callbacks);
- pos > 0;
- pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
-
- git_global_shutdown_fn cb = git__swap(
- git__shutdown_callbacks[pos - 1], NULL);
-
- if (cb != NULL)
- cb();
- }
-
- git__free(git__user_agent);
- git__free(git__ssl_ciphers);
-}
-
-/**
- * Handle the global state with TLS
- *
- * If libgit2 is built with GIT_THREADS enabled,
- * the `git_libgit2_init()` function must be called
- * before calling any other function of the library.
- *
- * This function allocates a TLS index (using pthreads
- * or the native Win32 API) to store the global state
- * on a per-thread basis.
- *
- * Any internal method that requires global state will
- * then call `git__global_state()` which returns a pointer
- * to the global state structure; this pointer is lazily
- * allocated on each thread.
- *
- * Before shutting down the library, the
- * `git_libgit2_shutdown` method must be called to free
- * the previously reserved TLS index.
- *
- * If libgit2 is built without threading support, the
- * `git__global_statestate()` call returns a pointer to a single,
- * statically allocated global state. The `git_thread_`
- * functions are not available in that case.
- */
-
-/*
- * `git_libgit2_init()` allows subsystems to perform global setup,
- * which may take place in the global scope. An explicit memory
- * fence exists at the exit of `git_libgit2_init()`. Without this,
- * CPU cores are free to reorder cache invalidation of `_tls_init`
- * before cache invalidation of the subsystems' newly written global
- * state.
- */
-#if defined(GIT_THREADS) && defined(GIT_WIN32)
-
-static DWORD _fls_index;
-static volatile LONG _mutex = 0;
-
-static void WINAPI fls_free(void *st)
-{
- git__global_state_cleanup(st);
- git__free(st);
-}
-
-static int synchronized_threads_init(void)
-{
- int error;
-
- if ((_fls_index = FlsAlloc(fls_free)) == FLS_OUT_OF_INDEXES)
- return -1;
-
- git_threads_init();
-
- if (git_mutex_init(&git__mwindow_mutex))
- return -1;
-
- error = init_common();
-
- return error;
-}
-
-int git_libgit2_init(void)
-{
- int ret;
-
- /* Enter the lock */
- while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
-
- /* Only do work on a 0 -> 1 transition of the refcount */
- if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
- if (synchronized_threads_init() < 0)
- ret = -1;
- }
-
- /* Exit the lock */
- InterlockedExchange(&_mutex, 0);
-
- return ret;
-}
-
-int git_libgit2_shutdown(void)
-{
- int ret;
-
- /* Enter the lock */
- while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
-
- /* Only do work on a 1 -> 0 transition of the refcount */
- if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
- shutdown_common();
-
- FlsFree(_fls_index);
- git_mutex_free(&git__mwindow_mutex);
-
-#if defined(GIT_MSVC_CRTDBG)
- git_win32__crtdbg_stacktrace_cleanup();
- git_win32__stack_cleanup();
-#endif
- }
-
- /* Exit the lock */
- InterlockedExchange(&_mutex, 0);
-
- return ret;
-}
-
-git_global_st *git__global_state(void)
-{
- git_global_st *ptr;
-
- assert(git_atomic_get(&git__n_inits) > 0);
-
- if ((ptr = FlsGetValue(_fls_index)) != NULL)
- return ptr;
-
- ptr = git__calloc(1, sizeof(git_global_st));
- if (!ptr)
- return NULL;
-
- git_buf_init(&ptr->error_buf, 0);
-
- FlsSetValue(_fls_index, ptr);
- return ptr;
-}
-
-#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
-
-static pthread_key_t _tls_key;
-static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
-int init_error = 0;
-
-static void cb__free_status(void *st)
-{
- git__global_state_cleanup(st);
- git__free(st);
-}
-
-static void init_once(void)
-{
- if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
- return;
-
- pthread_key_create(&_tls_key, &cb__free_status);
-
- init_error = init_common();
-}
-
-int git_libgit2_init(void)
-{
- int ret, err;
-
- if ((err = pthread_mutex_lock(&_init_mutex)) != 0)
- return err;
-
- ret = git_atomic_inc(&git__n_inits);
- err = pthread_once(&_once_init, init_once);
- err |= pthread_mutex_unlock(&_init_mutex);
-
- if (err || init_error)
- return err | init_error;
-
- return ret;
-}
-
-int git_libgit2_shutdown(void)
-{
- void *ptr = NULL;
- pthread_once_t new_once = PTHREAD_ONCE_INIT;
- int error, ret;
-
- if ((error = pthread_mutex_lock(&_init_mutex)) != 0)
- return error;
-
- if ((ret = git_atomic_dec(&git__n_inits)) != 0)
- goto out;
-
- /* Shut down any subsystems that have global state */
- shutdown_common();
-
- ptr = pthread_getspecific(_tls_key);
- pthread_setspecific(_tls_key, NULL);
-
- git__global_state_cleanup(ptr);
- git__free(ptr);
-
- pthread_key_delete(_tls_key);
- git_mutex_free(&git__mwindow_mutex);
- _once_init = new_once;
-
-out:
- if ((error = pthread_mutex_unlock(&_init_mutex)) != 0)
- return error;
-
- return ret;
-}
-
-git_global_st *git__global_state(void)
-{
- git_global_st *ptr;
-
- assert(git_atomic_get(&git__n_inits) > 0);
-
- if ((ptr = pthread_getspecific(_tls_key)) != NULL)
- return ptr;
-
- ptr = git__calloc(1, sizeof(git_global_st));
- if (!ptr)
- return NULL;
-
- git_buf_init(&ptr->error_buf, 0);
- pthread_setspecific(_tls_key, ptr);
- return ptr;
-}
-
-#else
-
-static git_global_st __state;
-
-int git_libgit2_init(void)
-{
- int ret;
-
- /* Only init subsystems the first time */
- if ((ret = git_atomic_inc(&git__n_inits)) != 1)
- return ret;
-
- if ((ret = init_common()) < 0)
- return ret;
-
- return 1;
-}
-
-int git_libgit2_shutdown(void)
-{
- int ret;
-
- /* Shut down any subsystems that have global state */
- if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
- shutdown_common();
- git__global_state_cleanup(&__state);
- memset(&__state, 0, sizeof(__state));
- }
-
- return ret;
-}
-
-git_global_st *git__global_state(void)
-{
- return &__state;
-}
-
-#endif /* GIT_THREADS */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_global_h__
-#define INCLUDE_global_h__
-
-#include "common.h"
-
-#include "mwindow.h"
-#include "hash.h"
-
-typedef struct {
- git_error *last_error;
- git_error error_t;
- git_buf error_buf;
- char oid_fmt[GIT_OID_HEXSZ+1];
-
- /* On Windows, this is the current child thread that was started by
- * `git_thread_create`. This is used to set the thread's exit code
- * when terminated by `git_thread_exit`. It is unused on POSIX.
- */
- git_thread *current_thread;
-} git_global_st;
-
-git_global_st *git__global_state(void);
-
-extern git_mutex git__mwindow_mutex;
-
-#define GIT_GLOBAL (git__global_state())
-
-typedef void (*git_global_shutdown_fn)(void);
-
-extern void git__on_shutdown(git_global_shutdown_fn callback);
-
-extern const char *git_libgit2__user_agent(void);
-extern const char *git_libgit2__ssl_ciphers(void);
-
-#endif
return 0;
}
- if (git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp) < 0)
+ if (git_pqueue_init(&list, 0, 2, git_commit_list_generation_cmp) < 0)
return -1;
if (git_commit_list_parse(walk, one) < 0)
int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const git_oid *ancestor)
{
- git_oid merge_base;
- int error;
-
if (git_oid_equal(commit, ancestor))
return 0;
- error = git_merge_base(&merge_base, repo, commit, ancestor);
- /* No merge-base found, it's not a descendant */
- if (error == GIT_ENOTFOUND)
+ return git_graph_reachable_from_any(repo, ancestor, commit, 1);
+}
+
+int git_graph_reachable_from_any(
+ git_repository *repo,
+ const git_oid *commit_id,
+ const git_oid descendant_array[],
+ size_t length)
+{
+ git_revwalk *walk = NULL;
+ git_vector list;
+ git_commit_list *result = NULL;
+ git_commit_list_node *commit;
+ size_t i;
+ uint32_t minimum_generation = 0xffffffff;
+ int error = 0;
+
+ if (!length)
return 0;
- if (error < 0)
+ for (i = 0; i < length; ++i) {
+ if (git_oid_equal(commit_id, &descendant_array[i]))
+ return 1;
+ }
+
+ if ((error = git_vector_init(&list, length + 1, NULL)) < 0)
return error;
- return git_oid_equal(&merge_base, ancestor);
+ if ((error = git_revwalk_new(&walk, repo)) < 0)
+ goto done;
+
+ for (i = 0; i < length; i++) {
+ commit = git_revwalk__commit_lookup(walk, &descendant_array[i]);
+ if (commit == NULL) {
+ error = -1;
+ goto done;
+ }
+
+ git_vector_insert(&list, commit);
+ if (minimum_generation > commit->generation)
+ minimum_generation = commit->generation;
+ }
+
+ commit = git_revwalk__commit_lookup(walk, commit_id);
+ if (commit == NULL) {
+ error = -1;
+ goto done;
+ }
+
+ if (minimum_generation > commit->generation)
+ minimum_generation = commit->generation;
+
+ if ((error = git_merge__bases_many(&result, walk, commit, &list, minimum_generation)) < 0)
+ goto done;
+
+ if (result) {
+ error = git_oid_equal(commit_id, &result->item->oid);
+ } else {
+ /* No merge-base found, it's not a descendant */
+ error = 0;
+ }
+
+done:
+ git_commit_list_free(&result);
+ git_vector_free(&list);
+ git_revwalk_free(walk);
+ return error;
}
{
int error;
- if ((error = git_hash_sha1_ctx_init(&ctx->sha1)) < 0)
+ if ((error = git_hash_sha1_ctx_init(&ctx->ctx.sha1)) < 0)
return error;
ctx->algo = GIT_HASH_ALGO_SHA1;
{
switch (ctx->algo) {
case GIT_HASH_ALGO_SHA1:
- git_hash_sha1_ctx_cleanup(&ctx->sha1);
+ git_hash_sha1_ctx_cleanup(&ctx->ctx.sha1);
return;
default:
- assert(0);
+ /* unreachable */ ;
}
}
{
switch (ctx->algo) {
case GIT_HASH_ALGO_SHA1:
- return git_hash_sha1_init(&ctx->sha1);
+ return git_hash_sha1_init(&ctx->ctx.sha1);
default:
- assert(0);
- return -1;
+ /* unreachable */ ;
}
+ GIT_ASSERT(0);
+ return -1;
}
int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
{
switch (ctx->algo) {
case GIT_HASH_ALGO_SHA1:
- return git_hash_sha1_update(&ctx->sha1, data, len);
+ return git_hash_sha1_update(&ctx->ctx.sha1, data, len);
default:
- assert(0);
- return -1;
+ /* unreachable */ ;
}
+ GIT_ASSERT(0);
+ return -1;
}
int git_hash_final(git_oid *out, git_hash_ctx *ctx)
{
switch (ctx->algo) {
case GIT_HASH_ALGO_SHA1:
- return git_hash_sha1_final(out, &ctx->sha1);
+ return git_hash_sha1_final(out, &ctx->ctx.sha1);
default:
- assert(0);
- return -1;
+ /* unreachable */ ;
}
+ GIT_ASSERT(0);
+ return -1;
}
int git_hash_buf(git_oid *out, const void *data, size_t len)
error = git_hash_final(out, &ctx);
git_hash_ctx_cleanup(&ctx);
-
+
return error;
}
typedef struct git_hash_ctx {
union {
git_hash_sha1_ctx sha1;
- };
+ } ctx;
git_hash_algo_t algo;
} git_hash_ctx;
int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
{
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
SHA1DCInit(&ctx->c);
return 0;
}
int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
{
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
SHA1DCUpdate(&ctx->c, data, len);
return 0;
}
int git_hash_sha1_final(git_oid *out, git_hash_sha1_ctx *ctx)
{
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
if (SHA1DCFinal(out->id, &ctx->c)) {
git_error_set(GIT_ERROR_SHA1, "SHA1 collision attack detected");
return -1;
int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
{
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
CC_SHA1_Init(&ctx->c);
return 0;
}
{
const unsigned char *data = _data;
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
while (len > 0) {
CC_LONG chunk = (len > CC_LONG_MAX) ? CC_LONG_MAX : (CC_LONG)len;
int git_hash_sha1_final(git_oid *out, git_hash_sha1_ctx *ctx)
{
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
CC_SHA1_Final(out->id, &ctx->c);
return 0;
}
#include "hash/sha1.h"
struct git_hash_sha1_ctx {
- unsigned long long size;
+ uint64_t size;
unsigned int H[5];
unsigned int W[16];
};
void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
{
- assert(ctx);
- mbedtls_sha1_free(&ctx->c);
+ if (ctx)
+ mbedtls_sha1_free(&ctx->c);
}
int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
{
- assert(ctx);
- mbedtls_sha1_init(&ctx->c);
- mbedtls_sha1_starts(&ctx->c);
- return 0;
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha1_init(&ctx->c);
+ mbedtls_sha1_starts(&ctx->c);
+ return 0;
}
int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
{
- assert(ctx);
- mbedtls_sha1_update(&ctx->c, data, len);
- return 0;
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha1_update(&ctx->c, data, len);
+ return 0;
}
int git_hash_sha1_final(git_oid *out, git_hash_sha1_ctx *ctx)
{
- assert(ctx);
- mbedtls_sha1_finish(&ctx->c, out->id);
- return 0;
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha1_finish(&ctx->c, out->id);
+ return 0;
}
int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
{
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
if (SHA1_Init(&ctx->c) != 1) {
git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to initialize hash context");
int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
{
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
if (SHA1_Update(&ctx->c, data, len) != 1) {
git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to update hash");
int git_hash_sha1_final(git_oid *out, git_hash_sha1_ctx *ctx)
{
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
if (SHA1_Final(out->id, &ctx->c) != 1) {
git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to finalize hash");
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
-#ifdef __unix__
#include <sys/types.h> /* make sure macros like _BIG_ENDIAN visible */
#endif
-#endif
#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C
#include SHA1DC_CUSTOM_INCLUDE_SHA1_C
-static void sha1_process(SHA1_CTX* ctx, const uint32_t block[16])
+static void sha1_process(SHA1_CTX *ctx, const uint32_t block[16])
{
unsigned i, j;
uint32_t ubc_dv_mask[DVMASKSIZE] = { 0xFFFFFFFF };
}
}
-void SHA1DCInit(SHA1_CTX* ctx)
+void SHA1DCInit(SHA1_CTX *ctx)
{
ctx->total = 0;
ctx->ihv[0] = 0x67452301;
ctx->callback = NULL;
}
-void SHA1DCSetSafeHash(SHA1_CTX* ctx, int safehash)
+void SHA1DCSetSafeHash(SHA1_CTX *ctx, int safehash)
{
if (safehash)
ctx->safe_hash = 1;
}
-void SHA1DCSetUseUBC(SHA1_CTX* ctx, int ubc_check)
+void SHA1DCSetUseUBC(SHA1_CTX *ctx, int ubc_check)
{
if (ubc_check)
ctx->ubc_check = 1;
ctx->ubc_check = 0;
}
-void SHA1DCSetUseDetectColl(SHA1_CTX* ctx, int detect_coll)
+void SHA1DCSetUseDetectColl(SHA1_CTX *ctx, int detect_coll)
{
if (detect_coll)
ctx->detect_coll = 1;
ctx->detect_coll = 0;
}
-void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX* ctx, int reduced_round_coll)
+void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX *ctx, int reduced_round_coll)
{
if (reduced_round_coll)
ctx->reduced_round_coll = 1;
ctx->reduced_round_coll = 0;
}
-void SHA1DCSetCallback(SHA1_CTX* ctx, collision_block_callback callback)
+void SHA1DCSetCallback(SHA1_CTX *ctx, collision_block_callback callback)
{
ctx->callback = callback;
}
-void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, size_t len)
+void SHA1DCUpdate(SHA1_CTX *ctx, const char *buf, size_t len)
{
unsigned left, fill;
#include "win32.h"
-#include "global.h"
+#include "runtime.h"
#include <wincrypt.h>
#include <strsafe.h>
if ((error = hash_cng_prov_init()) < 0)
error = hash_cryptoapi_prov_init();
- git__on_shutdown(sha1_shutdown);
+ if (!error)
+ error = git_runtime_shutdown_register(sha1_shutdown);
return error;
}
{
const BYTE *data = (BYTE *)_data;
- assert(ctx->ctx.cryptoapi.valid);
+ GIT_ASSERT(ctx->ctx.cryptoapi.valid);
while (len > 0) {
DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
DWORD len = 20;
int error = 0;
- assert(ctx->ctx.cryptoapi.valid);
+ GIT_ASSERT(ctx->ctx.cryptoapi.valid);
if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0)) {
git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished");
{
int error = 0;
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
/*
* When compiled with GIT_THREADS, the global hash_prov data is
int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
{
- assert(ctx && ctx->type);
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(ctx->type);
return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
}
int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
{
- assert(ctx && ctx->type);
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(ctx->type);
return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
}
int git_hash_sha1_final(git_oid *out, git_hash_sha1_ctx *ctx)
{
- assert(ctx && ctx->type);
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(ctx->type);
return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
}
void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
{
- assert(ctx);
-
- if (ctx->type == CNG)
+ if (!ctx)
+ return;
+ else if (ctx->type == CNG)
hash_ctx_cng_cleanup(ctx);
else if(ctx->type == CRYPTOAPI)
hash_ctx_cryptoapi_cleanup(ctx);
#define HASHSIG_SCALE 100
#define HASHSIG_MAX_RUN 80
-#define HASHSIG_HASH_START 0x012345678ABCDEF0LL
+#define HASHSIG_HASH_START INT64_C(0x012345678ABCDEF0)
#define HASHSIG_HASH_SHIFT 5
#define HASHSIG_HASH_MIX(S,CH) \
uint8_t ignore_ch[256];
} hashsig_in_progress;
-static void hashsig_in_progress_init(
+static int hashsig_in_progress_init(
hashsig_in_progress *prog, git_hashsig *sig)
{
int i;
/* no more than one can be set */
- assert(!(sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) ||
+ GIT_ASSERT(!(sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) ||
!(sig->opt & GIT_HASHSIG_SMART_WHITESPACE));
if (sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) {
} else {
memset(prog, 0, sizeof(*prog));
}
+
+ return 0;
}
static int hashsig_add_hashes(
git_hashsig *sig = hashsig_alloc(opts);
GIT_ERROR_CHECK_ALLOC(sig);
- hashsig_in_progress_init(&prog, sig);
+ if ((error = hashsig_in_progress_init(&prog, sig)) < 0)
+ return error;
error = hashsig_add_hashes(sig, (const uint8_t *)buf, buflen, &prog);
return fd;
}
- hashsig_in_progress_init(&prog, sig);
+ if ((error = hashsig_in_progress_init(&prog, sig)) < 0) {
+ p_close(fd);
+ return error;
+ }
while (!error) {
if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
{
int matches = 0, i, j, cmp;
- assert(a->cmp == b->cmp);
+ GIT_ASSERT_WITH_RETVAL(a->cmp == b->cmp, 0);
/* hash heaps are sorted - just look for overlap vs total */
/* if we have fewer than the maximum number of elements, then just use
* one array since the two arrays will be the same
*/
- if (a->mins.size < HASHSIG_HEAP_SIZE)
+ if (a->mins.size < HASHSIG_HEAP_SIZE) {
return hashsig_heap_compare(&a->mins, &b->mins);
- else
- return (hashsig_heap_compare(&a->mins, &b->mins) +
- hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
+ } else {
+ int mins, maxs;
+
+ if ((mins = hashsig_heap_compare(&a->mins, &b->mins)) < 0)
+ return mins;
+ if ((maxs = hashsig_heap_compare(&a->maxs, &b->maxs)) < 0)
+ return maxs;
+
+ return (mins + maxs) / 2;
+ }
}
#include "git2/sys/filter.h"
#include "filter.h"
#include "buffer.h"
-#include "buf_text.h"
static int ident_find_id(
const char **id_start, const char **id_end, const char *start, size_t len)
GIT_UNUSED(self); GIT_UNUSED(payload);
/* Don't filter binary files */
- if (git_buf_text_is_binary(from))
+ if (git_buf_is_binary(from))
return GIT_PASSTHROUGH;
if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
return ident_remove_id(to, from);
}
+static int ident_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, ident_apply, NULL, payload, src, next);
+}
+
git_filter *git_ident_filter_new(void)
{
git_filter *f = git__calloc(1, sizeof(git_filter));
f->version = GIT_FILTER_VERSION;
f->attributes = "+ident"; /* apply to files with ident attribute set */
f->shutdown = git_filter_free;
- f->apply = ident_apply;
+ f->stream = ident_stream;
return f;
}
*/
static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match)
{
- int error = 0, wildmatch_flags;
+ int error = 0, wildmatch_flags, effective_flags;
size_t i;
git_attr_fnmatch *rule;
char *path;
if (git_buf_oom(&buf))
goto out;
+ /*
+ * if rule isn't for full path we match without PATHNAME flag
+ * as lines like *.txt should match something like dir/test.txt
+ * requiring * to also match /
+ */
+ effective_flags = wildmatch_flags;
+ if (!(rule->flags & GIT_ATTR_FNMATCH_FULLPATH))
+ effective_flags &= ~WM_PATHNAME;
+
/* if we found a match, we want to keep this rule */
- if ((wildmatch(git_buf_cstr(&buf), path, wildmatch_flags)) == WM_MATCH) {
+ if ((wildmatch(git_buf_cstr(&buf), path, effective_flags)) == WM_MATCH) {
*out = 1;
error = 0;
goto out;
const char *base,
const char *filename)
{
- int error = 0;
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
git_attr_file *file = NULL;
+ int error = 0;
+
+ error = git_attr_cache__get(&file, ignores->repo, NULL, &source, parse_ignore_file, false);
- error = git_attr_cache__get(&file, ignores->repo, NULL, GIT_ATTR_FILE__FROM_FILE,
- base, filename, parse_ignore_file, false);
if (error < 0)
return error;
static int get_internal_ignores(git_attr_file **out, git_repository *repo)
{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_MEMORY, NULL, GIT_IGNORE_INTERNAL };
int error;
if ((error = git_attr_cache__init(repo)) < 0)
return error;
- error = git_attr_cache__get(out, repo, NULL, GIT_ATTR_FILE__IN_MEMORY, NULL,
- GIT_IGNORE_INTERNAL, NULL, false);
+ error = git_attr_cache__get(out, repo, NULL, &source, NULL, false);
/* if internal rules list is empty, insert default rules */
if (!error && !(*out)->rules.length)
const char *workdir = git_repository_workdir(repo);
git_buf infopath = GIT_BUF_INIT;
- assert(repo && ignores && path);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(ignores);
+ GIT_ASSERT_ARG(path);
memset(ignores, 0, sizeof(*ignores));
ignores->repo = repo;
if ((error = git_path_dirname_r(&local, path)) < 0 ||
(error = git_path_resolve_relative(&local, 0)) < 0 ||
(error = git_path_to_dir(&local)) < 0 ||
- (error = git_buf_joinpath(&ignores->dir, workdir, local.ptr)) < 0)
- {;} /* Nothing, we just want to stop on the first error */
+ (error = git_buf_joinpath(&ignores->dir, workdir, local.ptr)) < 0 ||
+ (error = git_path_validate_workdir_buf(repo, &ignores->dir)) < 0) {
+ /* Nothing, we just want to stop on the first error */
+ }
+
git_buf_dispose(&local);
} else {
- error = git_buf_joinpath(&ignores->dir, path, "");
+ if (!(error = git_buf_joinpath(&ignores->dir, path, "")))
+ error = git_path_validate_filesystem(ignores->dir.ptr, ignores->dir.size);
}
+
if (error < 0)
goto cleanup;
git_attr_file *file;
git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
- assert(repo && ignored && pathname);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(ignored);
+ GIT_ASSERT_ARG(pathname);
workdir = git_repository_workdir(repo);
git_attr_fnmatch *match;
int ignored;
git_buf path = GIT_BUF_INIT;
- const char *wd, *filename;
+ const char *filename;
git_index *idx;
if ((error = git_repository__ensure_not_bare(
(error = git_repository_index(&idx, repo)) < 0)
return error;
- wd = git_repository_workdir(repo);
-
git_vector_foreach(vspec, i, match) {
/* skip wildcard matches (if they are being used) */
if ((match->flags & GIT_ATTR_FNMATCH_HASWILD) != 0 &&
if (git_index_get_bypath(idx, filename, 0) != NULL)
continue;
- if ((error = git_buf_joinpath(&path, wd, filename)) < 0)
+ if ((error = git_repository_workdir_path(&path, repo, filename)) < 0)
break;
/* is there a file on disk that matches this exactly? */
return error;
}
-
git_index *index;
int error = -1;
- assert(index_out);
+ GIT_ASSERT_ARG(index_out);
index = git__calloc(1, sizeof(git_index));
GIT_ERROR_CHECK_ALLOC(index);
/* index iterators increment the refcount of the index, so if we
* get here then there should be no outstanding iterators.
*/
- assert(!git_atomic_get(&index->readers));
+ if (git_atomic32_get(&index->readers))
+ return;
git_index_clear(index);
git_idxmap_free(index->entries_map);
/* call with locked index */
static void index_free_deleted(git_index *index)
{
- int readers = (int)git_atomic_get(&index->readers);
+ int readers = (int)git_atomic32_get(&index->readers);
size_t i;
if (readers > 0 || !index->deleted.length)
return;
for (i = 0; i < index->deleted.length; ++i) {
- git_index_entry *ie = git__swap(index->deleted.contents[i], NULL);
+ git_index_entry *ie = git_atomic_swap(index->deleted.contents[i], NULL);
index_entry_free(ie);
}
error = git_vector_remove(&index->entries, pos);
if (!error) {
- if (git_atomic_get(&index->readers) > 0) {
+ if (git_atomic32_get(&index->readers) > 0) {
error = git_vector_insert(&index->deleted, entry);
} else {
index_entry_free(entry);
{
int error = 0;
- assert(index);
+ GIT_ASSERT_ARG(index);
index->dirty = 1;
index->tree = NULL;
{
unsigned int old_ignore_case;
- assert(index);
+ GIT_ASSERT_ARG(index);
old_ignore_case = index->ignore_case;
unsigned git_index_version(git_index *index)
{
- assert(index);
+ GIT_ASSERT_ARG(index);
return index->version;
}
int git_index_set_version(git_index *index, unsigned int version)
{
- assert(index);
+ GIT_ASSERT_ARG(index);
if (version < INDEX_VERSION_NUMBER_LB ||
version > INDEX_VERSION_NUMBER_UB) {
return error;
}
-const char * git_index_path(const git_index *index)
+const char *git_index_path(const git_index *index)
{
- assert(index);
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
return index->index_file_path;
}
{
git_repository *repo;
- assert(oid && index);
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(index);
repo = INDEX_OWNER(index);
int git_index_write_tree_to(
git_oid *oid, git_index *index, git_repository *repo)
{
- assert(oid && index && repo);
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(repo);
+
return git_tree__write_index(oid, index, repo);
}
size_t git_index_entrycount(const git_index *index)
{
- assert(index);
+ GIT_ASSERT_ARG(index);
+
return index->entries.length;
}
const git_index_entry *git_index_get_byindex(
git_index *index, size_t n)
{
- assert(index);
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+
git_vector_sort(&index->entries);
return git_vector_get(&index->entries, n);
}
git_index_entry key = {{ 0 }};
git_index_entry *value;
- assert(index);
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
key.path = path;
GIT_INDEX_ENTRY_STAGE_SET(&key, stage);
if (st)
mode = st->st_mode;
- if (!git_path_isvalid(repo, path, mode, path_valid_flags)) {
+ if (!git_path_validate(repo, path, mode, path_valid_flags)) {
git_error_set(GIT_ERROR_INDEX, "invalid path: '%s'", path);
return -1;
}
if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
return GIT_EBAREREPO;
- if (git_buf_joinpath(&path, git_repository_workdir(repo), rel_path) < 0)
+ if (git_repository_workdir_path(&path, repo, rel_path) < 0)
return -1;
error = git_path_lstat(path.ptr, &st);
{
git_index_reuc_entry *reuc = NULL;
- assert(reuc_out && path);
+ GIT_ASSERT_ARG(reuc_out);
+ GIT_ASSERT_ARG(path);
*reuc_out = reuc = reuc_entry_alloc(path);
GIT_ERROR_CHECK_ALLOC(reuc);
if ((reuc->mode[0] = ancestor_mode) > 0) {
- assert(ancestor_oid);
+ GIT_ASSERT(ancestor_oid);
git_oid_cpy(&reuc->oid[0], ancestor_oid);
}
if ((reuc->mode[1] = our_mode) > 0) {
- assert(our_oid);
+ GIT_ASSERT(our_oid);
git_oid_cpy(&reuc->oid[1], our_oid);
}
if ((reuc->mode[2] = their_mode) > 0) {
- assert(their_oid);
+ GIT_ASSERT(their_oid);
git_oid_cpy(&reuc->oid[2], their_oid);
}
size_t path_length, position;
int error;
- assert(index && entry_ptr);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(entry_ptr);
entry = *entry_ptr;
int error = 0;
git_oid id;
- assert(index && source_entry->path);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(source_entry && source_entry->path);
if (INDEX_OWNER(index) == NULL)
return create_index_error(-1,
struct stat st;
int error;
- if ((error = git_buf_joinpath(&abspath, git_repository_workdir(repo), path)) < 0)
+ if ((error = git_repository_workdir_path(&abspath, repo, path)) < 0)
return error;
if ((error = p_stat(abspath.ptr, &st)) < 0) {
git_index_entry *entry = NULL;
int ret;
- assert(index && path);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
if ((ret = index_entry_init(&entry, index, path)) == 0)
ret = index_insert(index, &entry, 1, false, false, true);
{
int ret;
- assert(index && path);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
if (((ret = git_index_remove(index, path, 0)) < 0 &&
ret != GIT_ENOTFOUND) ||
int error = 0;
size_t i;
- assert(index);
+ GIT_ASSERT_ARG(index);
if (!source_entries->length)
return 0;
git_index_entry *entry = NULL;
int ret;
- assert(index && source_entry && source_entry->path);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(source_entry && source_entry->path);
if (!valid_filemode(source_entry->mode)) {
git_error_set(GIT_ERROR_INDEX, "invalid entry mode");
int git_index__find_pos(
size_t *out, git_index *index, const char *path, size_t path_len, int stage)
{
- assert(index && path);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
return index_find(out, index, path, path_len, stage);
}
{
size_t pos;
- assert(index && path);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
if (git_vector_bsearch2(
&pos, &index->entries, index->entries_search_path, path) < 0) {
unsigned short i;
int ret = 0;
- assert (index);
+ GIT_ASSERT_ARG(index);
if ((ancestor_entry &&
(ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0) ||
size_t count;
int stage, len = 0;
- assert(ancestor_out && our_out && their_out && index);
+ GIT_ASSERT_ARG(ancestor_out);
+ GIT_ASSERT_ARG(our_out);
+ GIT_ASSERT_ARG(their_out);
+ GIT_ASSERT_ARG(index);
*ancestor_out = NULL;
*our_out = NULL;
size_t pos;
int len = 0;
- assert(ancestor_out && our_out && their_out && index && path);
+ GIT_ASSERT_ARG(ancestor_out);
+ GIT_ASSERT_ARG(our_out);
+ GIT_ASSERT_ARG(their_out);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
*ancestor_out = NULL;
*our_out = NULL;
int git_index_conflict_remove(git_index *index, const char *path)
{
- assert(index && path);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
return index_conflict_remove(index, path);
}
int git_index_conflict_cleanup(git_index *index)
{
- assert(index);
+ GIT_ASSERT_ARG(index);
return index_conflict_remove(index, NULL);
}
size_t i;
git_index_entry *entry;
- assert(index);
+ GIT_ASSERT_ARG(index);
git_vector_foreach(&index->entries, i, entry) {
if (GIT_INDEX_ENTRY_STAGE(entry) > 0)
git_index_iterator *it;
int error;
- assert(iterator_out && index);
+ GIT_ASSERT_ARG(iterator_out);
+ GIT_ASSERT_ARG(index);
it = git__calloc(1, sizeof(git_index_iterator));
GIT_ERROR_CHECK_ALLOC(it);
const git_index_entry **out,
git_index_iterator *it)
{
- assert(out && it);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(it);
if (it->cur >= git_vector_length(&it->snap))
return GIT_ITEROVER;
{
git_index_conflict_iterator *it = NULL;
- assert(iterator_out && index);
+ GIT_ASSERT_ARG(iterator_out);
+ GIT_ASSERT_ARG(index);
it = git__calloc(1, sizeof(git_index_conflict_iterator));
GIT_ERROR_CHECK_ALLOC(it);
const git_index_entry *entry;
int len;
- assert(ancestor_out && our_out && their_out && iterator);
+ GIT_ASSERT_ARG(ancestor_out);
+ GIT_ASSERT_ARG(our_out);
+ GIT_ASSERT_ARG(their_out);
+ GIT_ASSERT_ARG(iterator);
*ancestor_out = NULL;
*our_out = NULL;
size_t git_index_name_entrycount(git_index *index)
{
- assert(index);
+ GIT_ASSERT_ARG(index);
return index->names.length;
}
const git_index_name_entry *git_index_name_get_byindex(
git_index *index, size_t n)
{
- assert(index);
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
git_vector_sort(&index->names);
return git_vector_get(&index->names, n);
{
git_index_name_entry *conflict_name;
- assert((ancestor && ours) || (ancestor && theirs) || (ours && theirs));
+ GIT_ASSERT_ARG((ancestor && ours) || (ancestor && theirs) || (ours && theirs));
conflict_name = git__calloc(1, sizeof(git_index_name_entry));
GIT_ERROR_CHECK_ALLOC(conflict_name);
size_t i;
git_index_name_entry *conflict_name;
- assert(index);
+ GIT_ASSERT_ARG(index);
git_vector_foreach(&index->names, i, conflict_name)
index_name_entry_free(conflict_name);
size_t git_index_reuc_entrycount(git_index *index)
{
- assert(index);
+ GIT_ASSERT_ARG(index);
return index->reuc.length;
}
{
int res;
- assert(index && reuc && reuc->path != NULL);
- assert(git_vector_is_sorted(&index->reuc));
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(reuc && reuc->path != NULL);
+ GIT_ASSERT(git_vector_is_sorted(&index->reuc));
res = git_vector_insert_sorted(&index->reuc, reuc, &index_reuc_on_dup);
index->dirty = 1;
git_index_reuc_entry *reuc = NULL;
int error = 0;
- assert(index && path);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode,
ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 ||
git_index *index, const char *path)
{
size_t pos;
- assert(index && path);
+
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(path, NULL);
if (!index->reuc.length)
return NULL;
- assert(git_vector_is_sorted(&index->reuc));
+ GIT_ASSERT_WITH_RETVAL(git_vector_is_sorted(&index->reuc), NULL);
if (git_index_reuc_find(&pos, index, path) < 0)
return NULL;
const git_index_reuc_entry *git_index_reuc_get_byindex(
git_index *index, size_t n)
{
- assert(index);
- assert(git_vector_is_sorted(&index->reuc));
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+ GIT_ASSERT_WITH_RETVAL(git_vector_is_sorted(&index->reuc), NULL);
return git_vector_get(&index->reuc, n);
}
int error;
git_index_reuc_entry *reuc;
- assert(git_vector_is_sorted(&index->reuc));
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT(git_vector_is_sorted(&index->reuc));
reuc = git_vector_get(&index->reuc, position);
error = git_vector_remove(&index->reuc, position);
{
size_t i;
- assert(index);
+ GIT_ASSERT_ARG(index);
for (i = 0; i < index->reuc.length; ++i)
- index_entry_reuc_free(git__swap(index->reuc.contents[i], NULL));
+ index_entry_reuc_free(git_atomic_swap(index->reuc.contents[i], NULL));
git_vector_clear(&index->reuc);
seek_forward(INDEX_HEADER_SIZE);
- assert(!index->entries.length);
+ GIT_ASSERT(!index->entries.length);
if ((error = index_map_resize(index->entries_map, header.entry_count, index->ignore_case)) < 0)
return error;
if (last) {
varint_len = git_encode_varint((unsigned char *) path,
disk_size, strlen(last) - same_len);
- assert(varint_len > 0);
+ GIT_ASSERT(varint_len > 0);
+
path += varint_len;
disk_size -= varint_len;
* If using path compression, we are not allowed
* to have additional trailing NULs.
*/
- assert(disk_size == path_len + 1);
+ GIT_ASSERT(disk_size == path_len + 1);
} else {
/*
* If no path compression is used, we do have
* NULs as padding. As such, simply assert that
* we have enough space left to write the path.
*/
- assert(disk_size > path_len);
+ GIT_ASSERT(disk_size > path_len);
}
memcpy(path, path_start, path_len + 1);
{
int error = 0;
size_t i;
- git_vector case_sorted, *entries;
+ git_vector case_sorted = GIT_VECTOR_INIT, *entries = NULL;
git_index_entry *entry;
const char *last = NULL;
/* If index->entries is sorted case-insensitively, then we need
* to re-sort it case-sensitively before writing */
if (index->ignore_case) {
- git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp);
+ if ((error = git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp)) < 0)
+ goto done;
+
git_vector_sort(&case_sorted);
entries = &case_sorted;
} else {
last = entry->path;
}
- if (index->ignore_case)
- git_vector_free(&case_sorted);
-
+done:
+ git_vector_free(&case_sorted);
return error;
}
bool is_extended;
uint32_t index_version_number;
- assert(index && file);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(file);
if (index->version <= INDEX_VERSION_NUMBER_EXT) {
is_extended = is_index_extended(index);
/* well, this isn't good */;
} else {
git_vector_swap(&entries, &index->entries);
- entries_map = git__swap(index->entries_map, entries_map);
+ entries_map = git_atomic_swap(index->entries_map, entries_map);
}
index->dirty = 1;
size_t i;
int error;
- assert((new_iterator->flags & GIT_ITERATOR_DONT_IGNORE_CASE));
+ GIT_ASSERT((new_iterator->flags & GIT_ITERATOR_DONT_IGNORE_CASE));
if ((error = git_vector_init(&new_entries, new_length_hint, index->entries._cmp)) < 0 ||
(error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 ||
goto done;
git_vector_swap(&new_entries, &index->entries);
- new_entries_map = git__swap(index->entries_map, new_entries_map);
+ new_entries_map = git_atomic_swap(index->entries_map, new_entries_map);
git_vector_foreach(&remove_entries, i, entry) {
if (index->tree)
git_pathspec ps;
bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0;
- assert(index);
+ GIT_ASSERT_ARG(index);
repo = INDEX_OWNER(index);
if ((error = git_repository__ensure_not_bare(repo, "index add all")) < 0)
payload,
};
- assert(index);
- assert(action == INDEX_ACTION_UPDATE || action == INDEX_ACTION_ADDALL);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(action == INDEX_ACTION_UPDATE || action == INDEX_ACTION_ADDALL);
repo = INDEX_OWNER(index);
const char *match;
git_buf path = GIT_BUF_INIT;
- assert(index);
+ GIT_ASSERT_ARG(index);
if ((error = git_pathspec__init(&ps, paths)) < 0)
return error;
GIT_REFCOUNT_INC(index);
- git_atomic_inc(&index->readers);
+ git_atomic32_inc(&index->readers);
git_vector_sort(&index->entries);
error = git_vector_dup(snap, &index->entries, index->entries._cmp);
{
git_vector_free(snap);
- git_atomic_dec(&index->readers);
+ git_atomic32_dec(&index->readers);
git_index_free(index);
}
git_idxmap *entries_map;
git_vector deleted; /* deleted entries if readers > 0 */
- git_atomic readers; /* number of active iterators */
+ git_atomic32 readers; /* number of active iterators */
unsigned int on_disk:1;
unsigned int ignore_case:1;
#include "zstream.h"
#include "object.h"
-extern git_mutex git__mwindow_mutex;
-
size_t git_indexer__max_objects = UINT32_MAX;
#define UINT31_MAX (0x7FFFFFFF)
{
ssize_t read;
- assert(idx && stream);
+ GIT_ASSERT_ARG(idx);
+ GIT_ASSERT_ARG(stream);
do {
if ((read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf))) < 0)
{
git_mwindow *w = NULL;
- assert(type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA);
+ GIT_ASSERT_ARG(type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA);
if (type == GIT_OBJECT_REF_DELTA) {
idx->off += GIT_OID_RAWSZ;
{
ssize_t read;
- assert(stream);
+ GIT_ASSERT_ARG(stream);
do {
read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf));
idx->inbuf_len += size - to_expell;
}
+#if defined(NO_MMAP) || !defined(GIT_WIN32)
+
+static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size)
+{
+ size_t remaining_size = size;
+ const char *ptr = (const char *)data;
+
+ /* Handle data size larger that ssize_t */
+ while (remaining_size > 0) {
+ ssize_t nb;
+ HANDLE_EINTR(nb, p_pwrite(idx->pack->mwf.fd, (void *)ptr,
+ remaining_size, offset));
+ if (nb <= 0)
+ return -1;
+
+ ptr += nb;
+ offset += nb;
+ remaining_size -= nb;
+ }
+
+ return 0;
+}
+
+static int append_to_pack(git_indexer *idx, const void *data, size_t size)
+{
+ if (write_at(idx, data, idx->pack->mwf.size, size) < 0) {
+ git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+#else
+
+/*
+ * Windows may keep different views to a networked file for the mmap- and
+ * open-accessed versions of a file, so any writes done through
+ * `write(2)`/`pwrite(2)` may not be reflected on the data that `mmap(2)` is
+ * able to read.
+ */
+
static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size)
{
git_file fd = idx->pack->mwf.fd;
git_map map;
int error;
- assert(data && size);
+ GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(size);
if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
return error;
size_t page_offset;
off64_t page_start;
off64_t current_size = idx->pack->mwf.size;
- int fd = idx->pack->mwf.fd;
int error;
if (!size)
page_offset = new_size % mmap_alignment;
page_start = new_size - page_offset;
- if (p_lseek(fd, page_start + mmap_alignment - 1, SEEK_SET) < 0 ||
- p_write(idx->pack->mwf.fd, data, 1) < 0) {
+ if (p_pwrite(idx->pack->mwf.fd, data, 1, page_start + mmap_alignment - 1) < 0) {
git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
return -1;
}
return write_at(idx, data, idx->pack->mwf.size, size);
}
+#endif
+
static int read_stream_object(git_indexer *idx, git_indexer_progress *stats)
{
git_packfile_stream *stream = &idx->stream;
return GIT_EBUFS;
if (!idx->have_stream) {
- error = git_packfile_unpack_header(&entry_size, &type, &idx->pack->mwf, &w, &idx->off);
+ error = git_packfile_unpack_header(&entry_size, &type, idx->pack, &w, &idx->off);
if (error == GIT_EBUFS) {
idx->off = entry_start;
return error;
struct git_pack_header *hdr = &idx->hdr;
git_mwindow_file *mwf = &idx->pack->mwf;
- assert(idx && data && stats);
+ GIT_ASSERT_ARG(idx);
+ GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(stats);
if ((error = append_to_pack(idx, data, size)) < 0)
return error;
/* Now that we have data in the pack, let's try to parse it */
/* As the file grows any windows we try to use will be out of date */
- git_mwindow_free_all(mwf);
+ if ((error = git_mwindow_free_all(mwf)) < 0)
+ goto on_error;
while (stats->indexed_objects < idx->nr_objects) {
if ((error = read_stream_object(idx, stats)) != 0) {
* Rewind the packfile by the trailer, as we might need to fix the
* packfile by injecting objects at the tail and must overwrite it.
*/
-static void seek_back_trailer(git_indexer *idx)
+static int seek_back_trailer(git_indexer *idx)
{
idx->pack->mwf.size -= GIT_OID_RAWSZ;
- git_mwindow_free_all(&idx->pack->mwf);
+ return git_mwindow_free_all(&idx->pack->mwf);
}
static int inject_object(git_indexer *idx, git_oid *id)
{
- git_odb_object *obj;
- struct entry *entry;
+ git_odb_object *obj = NULL;
+ struct entry *entry = NULL;
struct git_pack_entry *pentry = NULL;
git_oid foo = {{0}};
unsigned char hdr[64];
size_t len, hdr_len;
int error;
- seek_back_trailer(idx);
+ if ((error = seek_back_trailer(idx)) < 0)
+ goto cleanup;
+
entry_start = idx->pack->mwf.size;
- if (git_odb_read(&obj, idx->odb, id) < 0) {
+ if ((error = git_odb_read(&obj, idx->odb, id)) < 0) {
git_error_set(GIT_ERROR_INDEXER, "missing delta bases");
- return -1;
+ goto cleanup;
}
data = git_odb_object_data(obj);
entry->crc = crc32(0L, Z_NULL, 0);
/* Write out the object header */
- hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
- if ((error = append_to_pack(idx, hdr, hdr_len)) < 0)
+ if ((error = git_packfile__object_header(&hdr_len, hdr, len, git_odb_object_type(obj))) < 0 ||
+ (error = append_to_pack(idx, hdr, hdr_len)) < 0)
goto cleanup;
idx->pack->mwf.size += hdr_len;
unsigned int left = 0;
git_oid base;
- assert(git_vector_length(&idx->deltas) > 0);
+ GIT_ASSERT(git_vector_length(&idx->deltas) > 0);
if (idx->odb == NULL) {
git_error_set(GIT_ERROR_INDEXER, "cannot fix a thin pack without an ODB");
continue;
curpos = delta->delta_off;
- error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
+ error = git_packfile_unpack_header(&size, &type, idx->pack, &w, &curpos);
if (error < 0)
return error;
* hash_partially() keep the existing trailer out of the
* calculation.
*/
- git_mwindow_free_all(mwf);
+ if (git_mwindow_free_all(mwf) < 0)
+ return -1;
+
idx->inbuf_len = 0;
while (hashed < mwf->size) {
ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
if (git_filebuf_commit_at(&index_file, filename.ptr) < 0)
goto on_error;
- git_mwindow_free_all(&idx->pack->mwf);
+ if (git_mwindow_free_all(&idx->pack->mwf) < 0)
+ goto on_error;
- /* Truncate file to undo rounding up to next page_size in append_to_pack */
+#if !defined(NO_MMAP) && defined(GIT_WIN32)
+ /*
+ * Some non-Windows remote filesystems fail when truncating files if the
+ * file permissions change after opening the file (done by p_mkstemp).
+ *
+ * Truncation is only needed when mmap is used to undo rounding up to next
+ * page_size in append_to_pack.
+ */
if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) {
git_error_set(GIT_ERROR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name);
return -1;
}
+#endif
if (idx->do_fsync && p_fsync(idx->pack->mwf.fd) < 0) {
git_error_set(GIT_ERROR_OS, "failed to fsync packfile");
git_vector_free_deep(&idx->deltas);
- if (!git_mutex_lock(&git__mwindow_mutex)) {
- if (!idx->pack_committed)
- git_packfile_close(idx->pack, true);
-
- git_packfile_free(idx->pack);
- git_mutex_unlock(&git__mwindow_mutex);
- }
+ git_packfile_free(idx->pack, !idx->pack_committed);
iter = 0;
while (git_oidmap_iterate((void **) &value, idx->expected_oids, &iter, &key) == 0)
}
/** @return true if p fits into the range of an int */
-GIT_INLINE(int) git__is_int(long long p)
+GIT_INLINE(int) git__is_int(int64_t p)
{
int r = (int)p;
- return p == (long long)r;
+ return p == (int64_t)r;
}
/* Use clang/gcc compiler intrinsics whenever possible */
# define git__sub_int_overflow(out, one, two) \
__builtin_ssub_overflow(one, two, out)
+# define git__add_int64_overflow(out, one, two) \
+ __builtin_add_overflow(one, two, out)
+
+/* clang on 32-bit systems produces an undefined reference to `__mulodi4`. */
+# if !defined(__clang__) || !defined(GIT_ARCH_32)
+# define git__multiply_int64_overflow(out, one, two) \
+ __builtin_mul_overflow(one, two, out)
+# endif
+
/* Use Microsoft's safe integer handling functions where available */
#elif defined(_MSC_VER)
(SizeTAdd(one, two, out) != S_OK)
# define git__multiply_sizet_overflow(out, one, two) \
(SizeTMult(one, two, out) != S_OK)
+
#define git__add_int_overflow(out, one, two) \
(IntAdd(one, two, out) != S_OK)
#define git__sub_int_overflow(out, one, two) \
(IntSub(one, two, out) != S_OK)
+#define git__add_int64_overflow(out, one, two) \
+ (LongLongAdd(one, two, out) != S_OK)
+#define git__multiply_int64_overflow(out, one, two) \
+ (LongLongMult(one, two, out) != S_OK)
+
#else
/**
return false;
}
+GIT_INLINE(bool) git__add_int64_overflow(int64_t *out, int64_t one, int64_t two)
+{
+ if ((two > 0 && one > (INT64_MAX - two)) ||
+ (two < 0 && one < (INT64_MIN - two)))
+ return true;
+ *out = one + two;
+ return false;
+}
+
+#endif
+
+/* If we could not provide an intrinsic implementation for this, provide a (slow) fallback. */
+#if !defined(git__multiply_int64_overflow)
+GIT_INLINE(bool) git__multiply_int64_overflow(int64_t *out, int64_t one, int64_t two)
+{
+ /*
+ * Detects whether `INT64_MAX < (one * two) || INT64_MIN > (one * two)`,
+ * without incurring in undefined behavior. That is done by performing the
+ * comparison with a division instead of a multiplication, which translates
+ * to `INT64_MAX / one < two || INT64_MIN / one > two`. Some caveats:
+ *
+ * - The comparison sign is inverted when both sides of the inequality are
+ * multiplied/divided by a negative number, so if `one < 0` the comparison
+ * needs to be flipped.
+ * - `INT64_MAX / -1` itself overflows (or traps), so that case should be
+ * avoided.
+ * - Since the overflow flag is defined as the discrepance between the result
+ * of performing the multiplication in a signed integer at twice the width
+ * of the operands, and the truncated+sign-extended version of that same
+ * result, there are four cases where the result is the opposite of what
+ * would be expected:
+ * * `INT64_MIN * -1` / `-1 * INT64_MIN`
+ * * `INT64_MIN * 1 / `1 * INT64_MIN`
+ */
+ if (one && two) {
+ if (one > 0 && two > 0) {
+ if (INT64_MAX / one < two)
+ return true;
+ } else if (one < 0 && two < 0) {
+ if ((one == -1 && two == INT64_MIN) ||
+ (two == -1 && one == INT64_MIN)) {
+ *out = INT64_MIN;
+ return false;
+ }
+ if (INT64_MAX / one > two)
+ return true;
+ } else if (one > 0 && two < 0) {
+ if ((one == 1 && two == INT64_MIN) ||
+ (INT64_MIN / one > two))
+ return true;
+ } else if (one == -1) {
+ if (INT64_MIN / two > one)
+ return true;
+ } else {
+ if ((one == INT64_MIN && two == 1) ||
+ (INT64_MIN / one < two))
+ return true;
+ }
+ }
+ *out = one * two;
+ return false;
+}
#endif
#endif
break;
/* an exact match would have been matched by the bsearch above */
- assert(p[path_len]);
+ GIT_ASSERT_WITH_RETVAL(p[path_len], ITERATOR_PATHLIST_NONE);
/* is this a literal directory entry (eg `foo/`) or a file beneath */
if (p[path_len] == '/') {
return error;
}
-static void tree_iterator_frame_pop(tree_iterator *iter)
+static int tree_iterator_frame_pop(tree_iterator *iter)
{
tree_iterator_frame *frame;
git_buf *buf = NULL;
git_tree *tree;
size_t i;
- assert(iter->frames.size);
+ GIT_ASSERT(iter->frames.size);
frame = git_array_pop(iter->frames);
git_vector_free(&frame->similar_trees);
git_buf_dispose(&frame->path);
+
+ return 0;
}
static int tree_iterator_current(
/* no more entries in this frame. pop the frame out */
if (frame->next_idx == frame->entries.length) {
- tree_iterator_frame_pop(iter);
+ if ((error = tree_iterator_frame_pop(iter)) < 0)
+ break;
+
continue;
}
const git_index_entry **out, git_iterator *i)
{
tree_iterator *iter = (tree_iterator *)i;
- tree_iterator_frame *frame;
+ tree_iterator_frame *frame;
tree_iterator_entry *prev_entry;
int error;
* we will have pushed a new (empty) frame on to the stack for this
* new directory. since it's empty, its current_entry should be null.
*/
- assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
+ GIT_ASSERT(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
if (prev_entry) {
if (!git_tree_entry__is_tree(prev_entry->tree_entry))
tree_iterator_frame *frame;
tree_iterator_entry *entry;
- assert(i->type == GIT_ITERATOR_TREE);
+ GIT_ASSERT(i->type == GIT_ITERATOR_TREE);
iter = (tree_iterator *)i;
tree_iterator *iter;
tree_iterator_frame *frame;
- assert(i->type == GIT_ITERATOR_TREE);
+ GIT_ASSERT(i->type == GIT_ITERATOR_TREE);
iter = (tree_iterator *)i;
- assert(depth < iter->frames.size);
+ GIT_ASSERT(depth < iter->frames.size);
frame = &iter->frames.ptr[iter->frames.size-depth-1];
*parent_tree = frame->tree;
return git_repository_hashfile(&entry->id,
iter->base.repo, entry->path, GIT_OBJECT_BLOB, NULL);
- if (!(error = git_buf_joinpath(&fullpath, iter->root, entry->path)))
+ if (!(error = git_buf_joinpath(&fullpath, iter->root, entry->path)) &&
+ !(error = git_path_validate_workdir_buf(iter->base.repo, &fullpath)))
error = git_odb_hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB);
git_buf_dispose(&fullpath);
else
git_buf_puts(&root, iter->root);
- if (git_buf_oom(&root)) {
+ if (git_buf_oom(&root) ||
+ git_path_validate_workdir_buf(iter->base.repo, &root) < 0) {
error = -1;
goto done;
}
iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL;
bool dir_expected = false;
- if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0)
+ if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0 ||
+ (error = git_path_validate_workdir_with_len(iter->base.repo, path, path_len)) < 0)
goto done;
- assert(path_len > iter->root_len);
+ GIT_ASSERT(path_len > iter->root_len);
/* remove the prefix if requested */
path += iter->root_len;
return error;
}
-GIT_INLINE(void) filesystem_iterator_frame_pop(filesystem_iterator *iter)
+GIT_INLINE(int) filesystem_iterator_frame_pop(filesystem_iterator *iter)
{
filesystem_iterator_frame *frame;
- assert(iter->frames.size);
+ GIT_ASSERT(iter->frames.size);
frame = git_array_pop(iter->frames);
filesystem_iterator_frame_pop_ignores(iter);
git_pool_clear(&frame->entry_pool);
git_vector_free(&frame->entries);
+
+ return 0;
}
static void filesystem_iterator_set_current(
}
if ((error = git_buf_joinpath(&fullpath, iter->root, entry->path)) < 0 ||
- (error = p_stat(fullpath.ptr, &st)) < 0)
+ (error = git_path_validate_workdir_buf(iter->base.repo, &fullpath)) < 0 ||
+ (error = p_stat(fullpath.ptr, &st)) < 0)
goto done;
*is_dir = S_ISDIR(st.st_mode);
* we will have pushed a new (empty) frame on to the stack for this
* new directory. since it's empty, its current_entry should be null.
*/
- assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
+ GIT_ASSERT(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
if (prev_entry) {
if (prev_entry->st.st_mode != GIT_FILEMODE_COMMIT &&
*out = NULL;
*status = GIT_ITERATOR_STATUS_NORMAL;
- assert(iterator__has_been_accessed(i));
+ GIT_ASSERT(iterator__has_been_accessed(i));
current_frame = filesystem_iterator_current_frame(iter);
- assert(current_frame);
+ GIT_ASSERT(current_frame);
+
current_entry = filesystem_iterator_current_entry(current_frame);
- assert(current_entry);
+ GIT_ASSERT(current_entry);
if ((error = git_iterator_current(&entry, i)) < 0)
return error;
static int index_iterator_skip_pseudotree(index_iterator *iter)
{
- assert(iterator__has_been_accessed(&iter->base));
- assert(S_ISDIR(iter->entry->mode));
+ GIT_ASSERT(iterator__has_been_accessed(&iter->base));
+ GIT_ASSERT(S_ISDIR(iter->entry->mode));
while (true) {
const git_index_entry *next_entry = NULL;
return i->cb->reset(i);
}
-void git_iterator_set_ignore_case(git_iterator *i, bool ignore_case)
+int git_iterator_set_ignore_case(git_iterator *i, bool ignore_case)
{
- assert(!iterator__has_been_accessed(i));
+ GIT_ASSERT(!iterator__has_been_accessed(i));
iterator_set_ignore_case(i, ignore_case);
+ return 0;
}
void git_iterator_free(git_iterator *iter)
return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0);
}
-extern void git_iterator_set_ignore_case(
+extern int git_iterator_set_ignore_case(
git_iterator *iter, bool ignore_case);
extern int git_iterator_current_tree_entry(
/* compiler specific configuration */
-#if UINT_MAX == 0xffffffffu
-typedef unsigned int khint32_t;
-#elif ULONG_MAX == 0xffffffffu
-typedef unsigned long khint32_t;
-#endif
-
-#if ULONG_MAX == ULLONG_MAX
-typedef unsigned long khint64_t;
-#else
-typedef unsigned long long khint64_t;
-#endif
+typedef uint32_t khint32_t;
+typedef uint64_t khint64_t;
#ifndef kh_inline
#ifdef _MSC_VER
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "libgit2.h"
+
+#include <git2.h>
+#include "alloc.h"
+#include "cache.h"
+#include "common.h"
+#include "filter.h"
+#include "hash.h"
+#include "index.h"
+#include "merge_driver.h"
+#include "pool.h"
+#include "mwindow.h"
+#include "object.h"
+#include "odb.h"
+#include "refs.h"
+#include "runtime.h"
+#include "sysdir.h"
+#include "thread.h"
+#include "threadstate.h"
+#include "git2/global.h"
+#include "streams/registry.h"
+#include "streams/mbedtls.h"
+#include "streams/openssl.h"
+#include "transports/smart.h"
+#include "transports/http.h"
+#include "transports/ssh.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;
+
+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[] = {
+#ifdef GIT_WIN32
+ git_win32_leakcheck_global_init,
+#endif
+ git_allocator_global_init,
+ git_threadstate_global_init,
+ git_threads_global_init,
+ git_hash_global_init,
+ git_sysdir_global_init,
+ git_filter_global_init,
+ git_merge_driver_global_init,
+ git_transport_ssh_global_init,
+ git_stream_registry_global_init,
+ git_openssl_stream_global_init,
+ git_mbedtls_stream_global_init,
+ git_mwindow_global_init,
+ git_pool_global_init,
+ git_libgit2_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();
+}
+
+int git_libgit2_version(int *major, int *minor, int *rev)
+{
+ *major = LIBGIT2_VER_MAJOR;
+ *minor = LIBGIT2_VER_MINOR;
+ *rev = LIBGIT2_VER_REVISION;
+
+ return 0;
+}
+
+int git_libgit2_features(void)
+{
+ return 0
+#ifdef GIT_THREADS
+ | GIT_FEATURE_THREADS
+#endif
+#ifdef GIT_HTTPS
+ | GIT_FEATURE_HTTPS
+#endif
+#if defined(GIT_SSH)
+ | GIT_FEATURE_SSH
+#endif
+#if defined(GIT_USE_NSEC)
+ | GIT_FEATURE_NSEC
+#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 *);
+ const git_buf *tmp;
+ int level;
+
+ if ((error = config_level_to_sysdir(&level, sysdir)) < 0 ||
+ (error = git_buf_sanitize(out)) < 0 ||
+ (error = git_sysdir_get(&tmp, level)) < 0)
+ break;
+
+ error = git_buf_sets(out, tmp->ptr);
+ }
+ 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 *);
+ const git_buf *tmp;
+
+ if ((error = git_buf_sanitize(out)) < 0 ||
+ (error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0)
+ break;
+
+ error = git_buf_sets(out, tmp->ptr);
+ }
+ 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 *);
+ if ((error = git_buf_sanitize(out)) < 0)
+ break;
+ error = git_buf_sets(out, git__user_agent);
+ }
+ 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;
+
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid option key");
+ error = -1;
+ }
+
+ va_end(ap);
+
+ return error;
+}
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#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
const git_mailmap_entry *b = (const git_mailmap_entry *)b_raw;
int cmp;
- assert(a && b && a->replace_email && b->replace_email);
+ GIT_ASSERT_ARG(a && a->replace_email);
+ GIT_ASSERT_ARG(b && b->replace_email);
cmp = git__strcmp(a->replace_email, b->replace_email);
if (cmp)
git_mailmap_entry *entry = git__calloc(1, sizeof(git_mailmap_entry));
GIT_ERROR_CHECK_ALLOC(entry);
- assert(mm && replace_email && *replace_email);
+ GIT_ASSERT_ARG(mm);
+ GIT_ASSERT_ARG(replace_email && *replace_email);
if (real_name_size > 0) {
entry->real_name = git__substrdup(real_name, real_name_size);
git_buf content = GIT_BUF_INIT;
int error;
- assert(mm && repo);
+ GIT_ASSERT_ARG(mm);
+ GIT_ASSERT_ARG(repo);
error = git_revparse_single(&object, repo, rev);
if (error < 0)
if (error < 0)
goto cleanup;
+ error = git_path_validate_workdir_buf(repo, &fullpath);
+ if (error < 0)
+ goto cleanup;
+
error = git_futils_readbuffer(&content, fullpath.ptr);
if (error < 0)
goto cleanup;
const char *rev = NULL;
const char *path = NULL;
- assert(mm && repo);
-
/* If we're in a bare repo, default blob to 'HEAD:.mailmap' */
if (repo->is_bare)
rev = MM_BLOB_DEFAULT;
int git_mailmap_from_repository(git_mailmap **out, git_repository *repo)
{
- int error = git_mailmap_new(out);
- if (error < 0)
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ if ((error = git_mailmap_new(out)) < 0)
return error;
+
mailmap_add_from_repository(*out, repo);
return 0;
}
git_mailmap_entry needle = { NULL };
needle.replace_email = (char *)email;
- assert(email);
+ GIT_ASSERT_ARG_WITH_RETVAL(email, NULL);
if (!mm)
return NULL;
if (git__strcmp(entry->replace_email, email))
break; /* it's a different email, so we're done looking */
- assert(entry->replace_name); /* should be specific */
+ /* should be specific */
+ GIT_ASSERT_WITH_RETVAL(entry->replace_name, NULL);
if (!name || !git__strcmp(entry->replace_name, name))
return entry;
}
const char *name, const char *email)
{
const git_mailmap_entry *entry = NULL;
- assert(name && email);
+
+ GIT_ASSERT(name);
+ GIT_ASSERT(email);
*real_name = name;
*real_email = email;
} git_map;
#define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \
- assert(out != NULL && len > 0); \
- assert((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \
- assert((flags & GIT_MAP_FIXED) == 0); } while (0)
+ GIT_ASSERT(out != NULL && len > 0); \
+ GIT_ASSERT((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \
+ GIT_ASSERT((flags & GIT_MAP_FIXED) == 0); } while (0)
extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset);
extern int p_munmap(git_map *map);
if (commit == NULL)
goto on_error;
- if (git_merge__bases_many(&result, walk, commit, &list) < 0)
+ if (git_merge__bases_many(&result, walk, commit, &list, 0) < 0)
goto on_error;
if (!result) {
git_commit_list *result = NULL;
int error = 0;
- assert(out && repo && input_array);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(input_array);
if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
return error;
int error = 0;
git_array_oid_t array;
- assert(out && repo && input_array);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(input_array);
if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
return error;
unsigned int i;
int error = -1;
- assert(out && repo && input_array);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(input_array);
if (length < 2) {
git_error_set(GIT_ERROR_INVALID, "at least two commits are required to find an ancestor");
if (commit == NULL)
goto on_error;
- if (git_merge__bases_many(&result, walk, commit, &list) < 0)
+ if (git_merge__bases_many(&result, walk, commit, &list, 0) < 0)
goto on_error;
if (!result) {
}
static int paint_down_to_common(
- git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
+ git_commit_list **out,
+ git_revwalk *walk,
+ git_commit_list_node *one,
+ git_vector *twos,
+ uint32_t minimum_generation)
{
git_pqueue list;
git_commit_list *result = NULL;
int error;
unsigned int i;
- if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
+ if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_generation_cmp) < 0)
return -1;
one->flags |= PARENT1;
return -1;
two->flags |= PARENT2;
-
if (git_pqueue_insert(&list, two) < 0)
return -1;
}
git_commit_list_node *p = commit->parents[i];
if ((p->flags & flags) == flags)
continue;
+ if (p->generation < minimum_generation)
+ continue;
if ((error = git_commit_list_parse(walk, p)) < 0)
return error;
return 0;
}
-static int remove_redundant(git_revwalk *walk, git_vector *commits)
+static int remove_redundant(git_revwalk *walk, git_vector *commits, uint32_t minimum_generation)
{
git_vector work = GIT_VECTOR_INIT;
unsigned char *redundant;
goto done;
}
- error = paint_down_to_common(&common, walk, commit, &work);
+ error = paint_down_to_common(&common, walk, commit, &work, minimum_generation);
if (error < 0)
goto done;
return error;
}
-int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
+int git_merge__bases_many(
+ git_commit_list **out,
+ git_revwalk *walk,
+ git_commit_list_node *one,
+ git_vector *twos,
+ uint32_t minimum_generation)
{
int error;
unsigned int i;
if (git_commit_list_parse(walk, one) < 0)
return -1;
- error = paint_down_to_common(&result, walk, one, twos);
+ error = paint_down_to_common(&result, walk, one, twos, minimum_generation);
if (error < 0)
return error;
if ((error = clear_commit_marks(one, ALL_FLAGS)) < 0 ||
(error = clear_commit_marks_many(twos, ALL_FLAGS)) < 0 ||
- (error = remove_redundant(walk, &redundant)) < 0) {
+ (error = remove_redundant(walk, &redundant, minimum_generation)) < 0) {
git_vector_free(&redundant);
return error;
}
git_oid oid;
int error = 0;
- assert(repo && cb);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(cb);
if ((error = git_buf_joinpath(&merge_head_path, repo->gitdir,
GIT_MERGE_HEAD_FILE)) < 0)
git_index_entry const *result = NULL;
int error = 0;
- assert(resolved && diff_list && conflict);
+ GIT_ASSERT_ARG(resolved);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(conflict);
*resolved = 0;
int ours_changed, theirs_changed;
int error = 0;
- assert(resolved && diff_list && conflict);
+ GIT_ASSERT_ARG(resolved);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(conflict);
*resolved = 0;
git_index_entry *merged;
int error = 0;
- assert(resolved && diff_list && conflict);
+ GIT_ASSERT_ARG(resolved);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(conflict);
*resolved = 0;
conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
return 0;
- ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0);
- theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0);
+ ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0) ||
+ (conflict->ancestor_entry.mode != conflict->our_entry.mode);
+
+ theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0) ||
+ (conflict->ancestor_entry.mode != conflict->their_entry.mode);
/* if both are modified (and not to a common target) require a merge */
if (ours_changed && theirs_changed &&
bool fallback = false;
int error;
- assert(resolved && diff_list && conflict);
+ GIT_ASSERT_ARG(resolved);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(conflict);
*resolved = 0;
git_oidmap_free(map);
}
-static int deletes_by_oid_enqueue(git_oidmap *map, git_pool* pool, const git_oid *id, size_t idx)
+static int deletes_by_oid_enqueue(git_oidmap *map, git_pool *pool, const git_oid *id, size_t idx)
{
deletes_by_oid_queue *queue;
size_t *array_entry;
size_t src_count, tgt_count, i;
int error = 0;
- assert(diff_list && opts);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(opts);
- if ((opts->flags & GIT_MERGE_FIND_RENAMES) == 0)
+ if ((opts->flags & GIT_MERGE_FIND_RENAMES) == 0 ||
+ !diff_list->conflicts.length)
return 0;
similarity_ours = git__calloc(diff_list->conflicts.length,
git_config_entry *entry = NULL;
int error = 0;
- assert(repo && opts);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(opts);
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
return error;
size_t i;
int error = 0;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
*out = NULL;
git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
int error;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
/* if one side is treesame to the ancestor, take the other side */
if (ancestor_tree && merge_opts && (merge_opts->flags & GIT_MERGE_SKIP_REUC)) {
result->type = GIT_ANNOTATED_COMMIT_VIRTUAL;
result->index = index;
- insert_head_ids(&result->parents, one);
- insert_head_ids(&result->parents, two);
+ if (insert_head_ids(&result->parents, one) < 0 ||
+ insert_head_ids(&result->parents, two) < 0) {
+ git_annotated_commit_free(result);
+ return -1;
+ }
*out = result;
return 0;
git_annotated_commit_free(other);
git_annotated_commit_free(new_base);
- git_oidarray_free(&bases);
+ git_oidarray_dispose(&bases);
git_array_clear(head_ids);
return error;
}
size_t i;
int error = 0;
- assert(repo && heads);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(heads);
if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0 ||
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) < 0)
git_buf file_path = GIT_BUF_INIT;
int error = 0;
- assert(repo);
+ GIT_ASSERT_ARG(repo);
if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_MERGE_MODE_FILE)) < 0 ||
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) < 0)
char sep = 0;
int error = 0;
- assert(repo && heads);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(heads);
entries = git__calloc(heads_len, sizeof(struct merge_msg_entry));
GIT_ERROR_CHECK_ALLOC(entries);
{
int error = 0;
- assert (repo && our_head && heads);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(our_head);
+ GIT_ASSERT_ARG(heads);
if ((error = git_repository__set_orig_head(repo, git_annotated_commit_id(our_head))) == 0 &&
(error = write_merge_head(repo, heads, heads_len)) == 0 &&
size_t i, alloc_len;
int error = 0;
- assert(repo && our_head && their_heads);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(our_head);
+ GIT_ASSERT_ARG(their_heads);
GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, their_heads_len, 1);
oids = git__calloc(alloc_len, sizeof(git_oid));
int error = 0;
bool unborn;
- assert(analysis_out && preference_out && repo && their_heads && their_heads_len > 0);
+ GIT_ASSERT_ARG(analysis_out);
+ GIT_ASSERT_ARG(preference_out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(their_heads && their_heads_len > 0);
if (their_heads_len != 1) {
git_error_set(GIT_ERROR_MERGE, "can only merge a single branch");
unsigned int checkout_strategy;
int error = 0;
- assert(repo && their_heads && their_heads_len > 0);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(their_heads && their_heads_len > 0);
if (their_heads_len != 1) {
git_error_set(GIT_ERROR_MERGE, "can only merge a single branch");
git_commit_list **out,
git_revwalk *walk,
git_commit_list_node *one,
- git_vector *twos);
+ git_vector *twos,
+ uint32_t minimum_generation);
/*
* Three-way tree differencing
#include "merge_driver.h"
#include "vector.h"
-#include "global.h"
+#include "runtime.h"
#include "merge.h"
#include "git2/merge.h"
#include "git2/sys/merge.h"
static void git_merge_driver_global_shutdown(void);
-git_repository* git_merge_driver_source_repo(const git_merge_driver_source *src)
+git_repository *git_merge_driver_source_repo(
+ const git_merge_driver_source *src)
{
- assert(src);
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
return src->repo;
}
-const git_index_entry* git_merge_driver_source_ancestor(const git_merge_driver_source *src)
+const git_index_entry *git_merge_driver_source_ancestor(
+ const git_merge_driver_source *src)
{
- assert(src);
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
return src->ancestor;
}
-const git_index_entry* git_merge_driver_source_ours(const git_merge_driver_source *src)
+const git_index_entry *git_merge_driver_source_ours(
+ const git_merge_driver_source *src)
{
- assert(src);
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
return src->ours;
}
-const git_index_entry* git_merge_driver_source_theirs(const git_merge_driver_source *src)
+const git_index_entry *git_merge_driver_source_theirs(
+ const git_merge_driver_source *src)
{
- assert(src);
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
return src->theirs;
}
-const git_merge_file_options* git_merge_driver_source_file_options(const git_merge_driver_source *src)
+const git_merge_file_options *git_merge_driver_source_file_options(
+ const git_merge_driver_source *src)
{
- assert(src);
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
return src->file_opts;
}
merge_driver_name__binary, &git_merge_driver__binary)) < 0)
goto done;
- git__on_shutdown(git_merge_driver_global_shutdown);
+ error = git_runtime_shutdown_register(git_merge_driver_global_shutdown);
done:
if (error < 0)
{
int error;
- assert(name && driver);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(driver);
if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry");
{
int error = 0;
- assert(input_out && odb_object_out && odb && entry);
+ GIT_ASSERT_ARG(input_out);
+ GIT_ASSERT_ARG(odb_object_out);
+ GIT_ASSERT_ARG(odb);
+ GIT_ASSERT_ARG(entry);
if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0)
goto done;
{
git_merge_file_input inputs[3] = { {0} };
- assert(out && ours && theirs);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ours);
+ GIT_ASSERT_ARG(theirs);
memset(out, 0x0, sizeof(git_merge_file_result));
git_odb_object *odb_object[3] = { 0 };
int error = 0;
- assert(out && repo && ours && theirs);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(ours);
+ GIT_ASSERT_ARG(theirs);
memset(out, 0x0, sizeof(git_merge_file_result));
int consecutive_empty_lines = 0;
size_t i, line_length, rtrimmed_line_length;
char *next_newline;
+ int error;
- git_buf_sanitize(message_out);
+ if ((error = git_buf_sanitize(message_out)) < 0)
+ return error;
for (i = 0; i < strlen(message); i += line_length) {
next_newline = memchr(message + i, '\n', message_len - i);
#include "midx.h"
+#include "array.h"
#include "buffer.h"
+#include "filebuf.h"
#include "futils.h"
#include "hash.h"
#include "odb.h"
#include "pack.h"
-
-#define GIT_MIDX_FILE_MODE 0444
+#include "path.h"
+#include "repository.h"
#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
#define MIDX_VERSION 1
size_t length;
};
+typedef int (*midx_write_cb)(const char *buf, size_t size, void *cb_data);
+
static int midx_error(const char *message)
{
git_error_set(GIT_ERROR_ODB, "invalid multi-pack-index file - %s", message);
return midx_error("missing OID Lookup chunk");
if (chunk_oid_lookup->length == 0)
return midx_error("empty OID Lookup chunk");
- if (chunk_oid_lookup->length != idx->num_objects * 20)
+ if (chunk_oid_lookup->length != idx->num_objects * GIT_OID_RAWSZ)
return midx_error("OID Lookup chunk has wrong length");
idx->oid_lookup = oid = (git_oid *)(data + chunk_oid_lookup->offset);
chunk_object_offsets = {0},
chunk_object_large_offsets = {0};
- assert(idx);
+ GIT_ASSERT_ARG(idx);
- if (size < sizeof(struct git_midx_header) + 20)
+ if (size < sizeof(struct git_midx_header) + GIT_OID_RAWSZ)
return midx_error("multi-pack index is too short");
hdr = ((struct git_midx_header *)data);
last_chunk_offset =
sizeof(struct git_midx_header) +
(1 + hdr->chunks) * 12;
- trailer_offset = size - 20;
+ trailer_offset = size - GIT_OID_RAWSZ;
if (trailer_offset < last_chunk_offset)
return midx_error("wrong index size");
git_oid_cpy(&idx->checksum, (git_oid *)(data + trailer_offset));
idx = git__calloc(1, sizeof(git_midx_file));
GIT_ERROR_CHECK_ALLOC(idx);
+ error = git_buf_sets(&idx->filename, path);
+ if (error < 0)
+ return error;
+
error = git_futils_mmap_ro(&idx->index_map, fd, 0, idx_size);
p_close(fd);
if (error < 0) {
return 0;
}
+bool git_midx_needs_refresh(
+ const git_midx_file *idx,
+ const char *path)
+{
+ git_file fd = -1;
+ struct stat st;
+ ssize_t bytes_read;
+ git_oid idx_checksum = {{0}};
+
+ /* TODO: properly open the file without access time using O_NOATIME */
+ fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return true;
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ return true;
+ }
+
+ if (!S_ISREG(st.st_mode) ||
+ !git__is_sizet(st.st_size) ||
+ (size_t)st.st_size != idx->index_map.len) {
+ p_close(fd);
+ return true;
+ }
+
+ bytes_read = p_pread(fd, &idx_checksum, GIT_OID_RAWSZ, st.st_size - GIT_OID_RAWSZ);
+ p_close(fd);
+
+ if (bytes_read != GIT_OID_RAWSZ)
+ return true;
+
+ return !git_oid_equal(&idx_checksum, &idx->checksum);
+}
+
int git_midx_entry_find(
git_midx_entry *e,
git_midx_file *idx,
const unsigned char *object_offset;
off64_t offset;
- assert(idx);
+ GIT_ASSERT_ARG(idx);
hi = ntohl(idx->oid_fanout[(int)short_oid->id[0]]);
lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(idx->oid_fanout[(int)short_oid->id[0] - 1]));
- pos = git_pack__lookup_sha1(idx->oid_lookup, 20, lo, hi, short_oid->id);
+ pos = git_pack__lookup_sha1(idx->oid_lookup, GIT_OID_RAWSZ, lo, hi, short_oid->id);
if (pos >= 0) {
/* An object matching exactly the oid was found */
return 0;
}
-void git_midx_close(git_midx_file *idx)
+int git_midx_foreach_entry(
+ git_midx_file *idx,
+ git_odb_foreach_cb cb,
+ void *data)
{
- assert(idx);
+ size_t i;
+ int error;
+
+ GIT_ASSERT_ARG(idx);
+
+ for (i = 0; i < idx->num_objects; ++i) {
+ if ((error = cb(&idx->oid_lookup[i], data)) != 0)
+ return git_error_set_after_callback(error);
+ }
+
+ return error;
+}
+
+int git_midx_close(git_midx_file *idx)
+{
+ GIT_ASSERT_ARG(idx);
if (idx->index_map.data)
git_futils_mmap_free(&idx->index_map);
+
git_vector_free(&idx->packfile_names);
+
+ return 0;
}
void git_midx_free(git_midx_file *idx)
if (!idx)
return;
+ git_buf_dispose(&idx->filename);
git_midx_close(idx);
git__free(idx);
}
+
+static int packfile__cmp(const void *a_, const void *b_)
+{
+ const struct git_pack_file *a = a_;
+ const struct git_pack_file *b = b_;
+
+ return strcmp(a->pack_name, b->pack_name);
+}
+
+int git_midx_writer_new(
+ git_midx_writer **out,
+ const char *pack_dir)
+{
+ git_midx_writer *w = git__calloc(1, sizeof(git_midx_writer));
+ GIT_ERROR_CHECK_ALLOC(w);
+
+ if (git_buf_sets(&w->pack_dir, pack_dir) < 0) {
+ git__free(w);
+ return -1;
+ }
+ git_path_squash_slashes(&w->pack_dir);
+
+ if (git_vector_init(&w->packs, 0, packfile__cmp) < 0) {
+ git_buf_dispose(&w->pack_dir);
+ git__free(w);
+ return -1;
+ }
+
+ *out = w;
+ return 0;
+}
+
+void git_midx_writer_free(git_midx_writer *w)
+{
+ struct git_pack_file *p;
+ size_t i;
+
+ if (!w)
+ return;
+
+ git_vector_foreach (&w->packs, i, p)
+ git_mwindow_put_pack(p);
+ git_vector_free(&w->packs);
+ git_buf_dispose(&w->pack_dir);
+ git__free(w);
+}
+
+int git_midx_writer_add(
+ git_midx_writer *w,
+ const char *idx_path)
+{
+ git_buf idx_path_buf = GIT_BUF_INIT;
+ int error;
+ struct git_pack_file *p;
+
+ error = git_path_prettify(&idx_path_buf, idx_path, git_buf_cstr(&w->pack_dir));
+ if (error < 0)
+ return error;
+
+ error = git_mwindow_get_pack(&p, git_buf_cstr(&idx_path_buf));
+ git_buf_dispose(&idx_path_buf);
+ if (error < 0)
+ return error;
+
+ error = git_vector_insert(&w->packs, p);
+ if (error < 0) {
+ git_mwindow_put_pack(p);
+ return error;
+ }
+
+ return 0;
+}
+
+typedef git_array_t(git_midx_entry) object_entry_array_t;
+
+struct object_entry_cb_state {
+ uint32_t pack_index;
+ object_entry_array_t *object_entries_array;
+};
+
+static int object_entry__cb(const git_oid *oid, off64_t offset, void *data)
+{
+ struct object_entry_cb_state *state = (struct object_entry_cb_state *)data;
+
+ git_midx_entry *entry = git_array_alloc(*state->object_entries_array);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ git_oid_cpy(&entry->sha1, oid);
+ entry->offset = offset;
+ entry->pack_index = state->pack_index;
+
+ return 0;
+}
+
+static int object_entry__cmp(const void *a_, const void *b_)
+{
+ const git_midx_entry *a = (const git_midx_entry *)a_;
+ const git_midx_entry *b = (const git_midx_entry *)b_;
+
+ return git_oid_cmp(&a->sha1, &b->sha1);
+}
+
+static int write_offset(off64_t offset, midx_write_cb write_cb, void *cb_data)
+{
+ int error;
+ uint32_t word;
+
+ word = htonl((uint32_t)((offset >> 32) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ word = htonl((uint32_t)((offset >> 0) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int write_chunk_header(int chunk_id, off64_t offset, midx_write_cb write_cb, void *cb_data)
+{
+ uint32_t word = htonl(chunk_id);
+ int error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ return write_offset(offset, write_cb, cb_data);
+
+ return 0;
+}
+
+static int midx_write_buf(const char *buf, size_t size, void *data)
+{
+ git_buf *b = (git_buf *)data;
+ return git_buf_put(b, buf, size);
+}
+
+struct midx_write_hash_context {
+ midx_write_cb write_cb;
+ void *cb_data;
+ git_hash_ctx *ctx;
+};
+
+static int midx_write_hash(const char *buf, size_t size, void *data)
+{
+ struct midx_write_hash_context *ctx = (struct midx_write_hash_context *)data;
+ int error;
+
+ error = git_hash_update(ctx->ctx, buf, size);
+ if (error < 0)
+ return error;
+
+ return ctx->write_cb(buf, size, ctx->cb_data);
+}
+
+static int midx_write(
+ git_midx_writer *w,
+ midx_write_cb write_cb,
+ void *cb_data)
+{
+ int error = 0;
+ size_t i;
+ struct git_pack_file *p;
+ struct git_midx_header hdr = {0};
+ uint32_t oid_fanout_count;
+ uint32_t object_large_offsets_count;
+ uint32_t oid_fanout[256];
+ off64_t offset;
+ git_buf packfile_names = GIT_BUF_INIT,
+ oid_lookup = GIT_BUF_INIT,
+ object_offsets = GIT_BUF_INIT,
+ object_large_offsets = GIT_BUF_INIT;
+ git_oid idx_checksum = {{0}};
+ git_midx_entry *entry;
+ object_entry_array_t object_entries_array = GIT_ARRAY_INIT;
+ git_vector object_entries = GIT_VECTOR_INIT;
+ git_hash_ctx ctx;
+ struct midx_write_hash_context hash_cb_data = {0};
+
+ hdr.signature = htonl(MIDX_SIGNATURE);
+ hdr.version = MIDX_VERSION;
+ hdr.object_id_version = MIDX_OBJECT_ID_VERSION;
+ hdr.base_midx_files = 0;
+
+ hash_cb_data.write_cb = write_cb;
+ hash_cb_data.cb_data = cb_data;
+ hash_cb_data.ctx = &ctx;
+
+ error = git_hash_ctx_init(&ctx);
+ if (error < 0)
+ return error;
+ cb_data = &hash_cb_data;
+ write_cb = midx_write_hash;
+
+ git_vector_sort(&w->packs);
+ git_vector_foreach (&w->packs, i, p) {
+ git_buf relative_index = GIT_BUF_INIT;
+ struct object_entry_cb_state state = {0};
+ size_t path_len;
+
+ state.pack_index = (uint32_t)i;
+ state.object_entries_array = &object_entries_array;
+
+ error = git_buf_sets(&relative_index, p->pack_name);
+ if (error < 0)
+ goto cleanup;
+ error = git_path_make_relative(&relative_index, git_buf_cstr(&w->pack_dir));
+ if (error < 0) {
+ git_buf_dispose(&relative_index);
+ goto cleanup;
+ }
+ path_len = git_buf_len(&relative_index);
+ if (path_len <= strlen(".pack") || git__suffixcmp(git_buf_cstr(&relative_index), ".pack") != 0) {
+ git_buf_dispose(&relative_index);
+ git_error_set(GIT_ERROR_INVALID, "invalid packfile name: '%s'", p->pack_name);
+ error = -1;
+ goto cleanup;
+ }
+ path_len -= strlen(".pack");
+
+ git_buf_put(&packfile_names, git_buf_cstr(&relative_index), path_len);
+ git_buf_puts(&packfile_names, ".idx");
+ git_buf_putc(&packfile_names, '\0');
+ git_buf_dispose(&relative_index);
+
+ error = git_pack_foreach_entry_offset(p, object_entry__cb, &state);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Sort the object entries. */
+ error = git_vector_init(&object_entries, git_array_size(object_entries_array), object_entry__cmp);
+ if (error < 0)
+ goto cleanup;
+ git_array_foreach (object_entries_array, i, entry) {
+ if ((error = git_vector_set(NULL, &object_entries, i, entry)) < 0)
+ goto cleanup;
+ }
+ git_vector_set_sorted(&object_entries, 0);
+ git_vector_sort(&object_entries);
+ git_vector_uniq(&object_entries, NULL);
+
+ /* Pad the packfile names so it is a multiple of four. */
+ while (git_buf_len(&packfile_names) & 3)
+ git_buf_putc(&packfile_names, '\0');
+
+ /* Fill the OID Fanout table. */
+ oid_fanout_count = 0;
+ for (i = 0; i < 256; i++) {
+ while (oid_fanout_count < git_vector_length(&object_entries) &&
+ ((const git_midx_entry *)git_vector_get(&object_entries, oid_fanout_count))->sha1.id[0] <= i)
+ ++oid_fanout_count;
+ oid_fanout[i] = htonl(oid_fanout_count);
+ }
+
+ /* Fill the OID Lookup table. */
+ git_vector_foreach (&object_entries, i, entry) {
+ error = git_buf_put(&oid_lookup, (const char *)&entry->sha1, sizeof(entry->sha1));
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Fill the Object Offsets and Object Large Offsets tables. */
+ object_large_offsets_count = 0;
+ git_vector_foreach (&object_entries, i, entry) {
+ uint32_t word;
+
+ word = htonl((uint32_t)entry->pack_index);
+ error = git_buf_put(&object_offsets, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ if (entry->offset >= 0x80000000l) {
+ word = htonl(0x80000000u | object_large_offsets_count++);
+ if ((error = write_offset(entry->offset, midx_write_buf, &object_large_offsets)) < 0)
+ goto cleanup;
+ } else {
+ word = htonl((uint32_t)entry->offset & 0x7fffffffu);
+ }
+
+ error = git_buf_put(&object_offsets, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Write the header. */
+ hdr.packfiles = htonl((uint32_t)git_vector_length(&w->packs));
+ hdr.chunks = 4;
+ if (git_buf_len(&object_large_offsets) > 0)
+ hdr.chunks++;
+ error = write_cb((const char *)&hdr, sizeof(hdr), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write the chunk headers. */
+ offset = sizeof(hdr) + (hdr.chunks + 1) * 12;
+ error = write_chunk_header(MIDX_PACKFILE_NAMES_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&packfile_names);
+ error = write_chunk_header(MIDX_OID_FANOUT_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += sizeof(oid_fanout);
+ error = write_chunk_header(MIDX_OID_LOOKUP_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&oid_lookup);
+ error = write_chunk_header(MIDX_OBJECT_OFFSETS_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&object_offsets);
+ if (git_buf_len(&object_large_offsets) > 0) {
+ error = write_chunk_header(MIDX_OBJECT_LARGE_OFFSETS_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&object_large_offsets);
+ }
+ error = write_chunk_header(0, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write all the chunks. */
+ error = write_cb(git_buf_cstr(&packfile_names), git_buf_len(&packfile_names), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb((const char *)oid_fanout, sizeof(oid_fanout), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&oid_lookup), git_buf_len(&oid_lookup), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&object_offsets), git_buf_len(&object_offsets), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&object_large_offsets), git_buf_len(&object_large_offsets), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Finalize the checksum and write the trailer. */
+ error = git_hash_final(&idx_checksum, &ctx);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb((const char *)&idx_checksum, sizeof(idx_checksum), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_array_clear(object_entries_array);
+ git_vector_free(&object_entries);
+ git_buf_dispose(&packfile_names);
+ git_buf_dispose(&oid_lookup);
+ git_buf_dispose(&object_offsets);
+ git_buf_dispose(&object_large_offsets);
+ git_hash_ctx_cleanup(&ctx);
+ return error;
+}
+
+static int midx_write_filebuf(const char *buf, size_t size, void *data)
+{
+ git_filebuf *f = (git_filebuf *)data;
+ return git_filebuf_write(f, buf, size);
+}
+
+int git_midx_writer_commit(
+ git_midx_writer *w)
+{
+ int error;
+ int filebuf_flags = GIT_FILEBUF_DO_NOT_BUFFER;
+ git_buf midx_path = GIT_BUF_INIT;
+ git_filebuf output = GIT_FILEBUF_INIT;
+
+ error = git_buf_joinpath(&midx_path, git_buf_cstr(&w->pack_dir), "multi-pack-index");
+ if (error < 0)
+ return error;
+
+ if (git_repository__fsync_gitdir)
+ filebuf_flags |= GIT_FILEBUF_FSYNC;
+ error = git_filebuf_open(&output, git_buf_cstr(&midx_path), filebuf_flags, 0644);
+ git_buf_dispose(&midx_path);
+ if (error < 0)
+ return error;
+
+ error = midx_write(w, midx_write_filebuf, &output);
+ if (error < 0) {
+ git_filebuf_cleanup(&output);
+ return error;
+ }
+
+ return git_filebuf_commit(&output);
+}
+
+int git_midx_writer_dump(
+ git_buf *midx,
+ git_midx_writer *w)
+{
+ return midx_write(w, midx_write_buf, midx);
+}
#include <ctype.h>
+#include "git2/sys/midx.h"
+
#include "map.h"
#include "mwindow.h"
+#include "odb.h"
/*
* A multi-pack-index file.
/* The trailer of the file. Contains the SHA1-checksum of the whole file. */
git_oid checksum;
+
+ /* something like ".git/objects/pack/multi-pack-index". */
+ git_buf filename;
} git_midx_file;
/*
git_oid sha1;
} git_midx_entry;
+/*
+ * A writer for `multi-pack-index` files.
+ */
+struct git_midx_writer {
+ /*
+ * The path of the directory where the .pack/.idx files are stored. The
+ * `multi-pack-index` file will be written to the same directory.
+ */
+ git_buf pack_dir;
+
+ /* The list of `git_pack_file`s. */
+ git_vector packs;
+};
+
int git_midx_open(
git_midx_file **idx_out,
const char *path);
+bool git_midx_needs_refresh(
+ const git_midx_file *idx,
+ const char *path);
int git_midx_entry_find(
git_midx_entry *e,
git_midx_file *idx,
const git_oid *short_oid,
size_t len);
-void git_midx_close(git_midx_file *idx);
+int git_midx_foreach_entry(
+ git_midx_file *idx,
+ git_odb_foreach_cb cb,
+ void *data);
+int git_midx_close(git_midx_file *idx);
void git_midx_free(git_midx_file *idx);
/* This is exposed for use in the fuzzers. */
#include "vector.h"
#include "futils.h"
#include "map.h"
-#include "global.h"
+#include "runtime.h"
#include "strmap.h"
#include "pack.h"
: 32 * 1024 * 1024)
#define DEFAULT_MAPPED_LIMIT \
- ((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))
+ ((1024 * 1024) * (sizeof(void*) >= 8 ? UINT64_C(8192) : UINT64_C(256)))
/* default is unlimited */
#define DEFAULT_FILE_LIMIT 0
size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
size_t git_mwindow__file_limit = DEFAULT_FILE_LIMIT;
-/* Whenever you want to read or modify this, grab git__mwindow_mutex */
+/* Mutex to control access to `git_mwindow__mem_ctl` and `git__pack_cache`. */
+git_mutex git__mwindow_mutex;
+
+/* Whenever you want to read or modify this, grab `git__mwindow_mutex` */
git_mwindow_ctl git_mwindow__mem_ctl;
/* Global list of mwindow files, to open packs once across repos */
git_strmap *git__pack_cache = NULL;
-static void git_mwindow_files_free(void)
+static void git_mwindow_global_shutdown(void)
{
git_strmap *tmp = git__pack_cache;
+ git_mutex_free(&git__mwindow_mutex);
+
git__pack_cache = NULL;
git_strmap_free(tmp);
}
int git_mwindow_global_init(void)
{
- assert(!git__pack_cache);
+ int error;
+
+ GIT_ASSERT(!git__pack_cache);
- git__on_shutdown(git_mwindow_files_free);
- return git_strmap_new(&git__pack_cache);
+ if ((error = git_mutex_init(&git__mwindow_mutex)) < 0 ||
+ (error = git_strmap_new(&git__pack_cache)) < 0)
+ return error;
+
+ return git_runtime_shutdown_register(git_mwindow_global_shutdown);
}
int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
git__free(packname);
if (pack != NULL) {
- git_atomic_inc(&pack->refcount);
+ git_atomic32_inc(&pack->refcount);
git_mutex_unlock(&git__mwindow_mutex);
*out = pack;
return 0;
return error;
}
- git_atomic_inc(&pack->refcount);
+ git_atomic32_inc(&pack->refcount);
error = git_strmap_set(git__pack_cache, pack->pack_name, pack);
git_mutex_unlock(&git__mwindow_mutex);
-
if (error < 0) {
- git_packfile_free(pack);
- return -1;
+ git_packfile_free(pack, false);
+ return error;
}
*out = pack;
return 0;
}
-void git_mwindow_put_pack(struct git_pack_file *pack)
+int git_mwindow_put_pack(struct git_pack_file *pack)
{
- int count;
+ int count, error;
+ struct git_pack_file *pack_to_delete = NULL;
- if (git_mutex_lock(&git__mwindow_mutex) < 0)
- return;
+ if ((error = git_mutex_lock(&git__mwindow_mutex)) < 0)
+ return error;
/* put before get would be a corrupted state */
- assert(git__pack_cache);
+ GIT_ASSERT(git__pack_cache);
/* if we cannot find it, the state is corrupted */
- assert(git_strmap_exists(git__pack_cache, pack->pack_name));
+ GIT_ASSERT(git_strmap_exists(git__pack_cache, pack->pack_name));
- count = git_atomic_dec(&pack->refcount);
+ count = git_atomic32_dec(&pack->refcount);
if (count == 0) {
git_strmap_delete(git__pack_cache, pack->pack_name);
- git_packfile_free(pack);
+ pack_to_delete = pack;
}
-
git_mutex_unlock(&git__mwindow_mutex);
- return;
-}
+ git_packfile_free(pack_to_delete, false);
-void git_mwindow_free_all(git_mwindow_file *mwf)
-{
- if (git_mutex_lock(&git__mwindow_mutex)) {
- git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
- return;
- }
-
- git_mwindow_free_all_locked(mwf);
-
- git_mutex_unlock(&git__mwindow_mutex);
+ return 0;
}
/*
* Free all the windows in a sequence, typically because we're done
- * with the file
+ * with the file. Needs to hold the git__mwindow_mutex.
*/
-void git_mwindow_free_all_locked(git_mwindow_file *mwf)
+static int git_mwindow_free_all_locked(git_mwindow_file *mwf)
{
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
size_t i;
while (mwf->windows) {
git_mwindow *w = mwf->windows;
- assert(w->inuse_cnt == 0);
+ GIT_ASSERT(w->inuse_cnt == 0);
ctl->mapped -= w->window_map.len;
ctl->open_windows--;
mwf->windows = w->next;
git__free(w);
}
+
+ return 0;
+}
+
+int git_mwindow_free_all(git_mwindow_file *mwf)
+{
+ int error;
+
+ if (git_mutex_lock(&git__mwindow_mutex)) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
+ return -1;
+ }
+
+ error = git_mwindow_free_all_locked(mwf);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+
+ return error;
}
/*
git_mwindow *lru_window = NULL, *lru_last = NULL;
bool found = false;
- assert(mwf);
- assert(out_window);
+ GIT_ASSERT_ARG(mwf);
+ GIT_ASSERT_ARG(out_window);
lru_window = *out_window;
if (out_last)
/*
* Close the least recently used window (that is currently not being used) out
- * of all the files. Called under lock from new_window.
+ * of all the files. Called under lock from new_window_locked.
*/
-static int git_mwindow_close_lru_window(void)
+static int git_mwindow_close_lru_window_locked(void)
{
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
git_mwindow_file *cur;
}
/*
- * Close the file that does not have any open windows AND whose
+ * Finds the file that does not have any open windows AND whose
* most-recently-used window is the least-recently used one across all
* currently open files.
*
- * Called under lock from new_window.
+ * Called under lock from new_window_locked.
*/
-static int git_mwindow_close_lru_file(void)
+static int git_mwindow_find_lru_file_locked(git_mwindow_file **out)
{
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
git_mwindow_file *lru_file = NULL, *current_file = NULL;
current_file, &mru_window, NULL, true, GIT_MWINDOW__MRU)) {
continue;
}
- if (!lru_window || lru_window->last_used > mru_window->last_used)
+ if (!lru_window || lru_window->last_used > mru_window->last_used) {
+ lru_window = mru_window;
lru_file = current_file;
+ }
}
if (!lru_file) {
return -1;
}
- git_mwindow_free_all_locked(lru_file);
- p_close(lru_file->fd);
- lru_file->fd = -1;
-
+ *out = lru_file;
return 0;
}
/* This gets called under lock from git_mwindow_open */
-static git_mwindow *new_window(
+static git_mwindow *new_window_locked(
git_file fd,
off64_t size,
off64_t offset)
off64_t len;
git_mwindow *w;
- w = git__malloc(sizeof(*w));
+ w = git__calloc(1, sizeof(*w));
if (w == NULL)
return NULL;
- memset(w, 0x0, sizeof(*w));
w->offset = (offset / walign) * walign;
len = size - w->offset;
ctl->mapped += (size_t)len;
while (git_mwindow__mapped_limit < ctl->mapped &&
- git_mwindow_close_lru_window() == 0) /* nop */;
+ git_mwindow_close_lru_window_locked() == 0) /* nop */;
/*
* We treat `mapped_limit` as a soft limit. If we can't find a
* we're below our soft limits, so free up what we can and try again.
*/
- while (git_mwindow_close_lru_window() == 0)
+ while (git_mwindow_close_lru_window_locked() == 0)
/* nop */;
if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
* one.
*/
if (!w) {
- w = new_window(mwf->fd, mwf->size, offset);
+ w = new_window_locked(mwf->fd, mwf->size, offset);
if (w == NULL) {
git_mutex_unlock(&git__mwindow_mutex);
return NULL;
int git_mwindow_file_register(git_mwindow_file *mwf)
{
+ git_vector closed_files = GIT_VECTOR_INIT;
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
- int ret;
+ int error;
+ size_t i;
+ git_mwindow_file *closed_file = NULL;
if (git_mutex_lock(&git__mwindow_mutex)) {
git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
}
if (ctl->windowfiles.length == 0 &&
- git_vector_init(&ctl->windowfiles, 8, NULL) < 0) {
+ (error = git_vector_init(&ctl->windowfiles, 8, NULL)) < 0) {
git_mutex_unlock(&git__mwindow_mutex);
- return -1;
+ goto cleanup;
}
if (git_mwindow__file_limit) {
+ git_mwindow_file *lru_file;
while (git_mwindow__file_limit <= ctl->windowfiles.length &&
- git_mwindow_close_lru_file() == 0) /* nop */;
+ git_mwindow_find_lru_file_locked(&lru_file) == 0) {
+ if ((error = git_vector_insert(&closed_files, lru_file)) < 0) {
+ /*
+ * Exceeding the file limit seems preferrable to being open to
+ * data races that can end up corrupting the heap.
+ */
+ break;
+ }
+ git_mwindow_free_all_locked(lru_file);
+ }
}
- ret = git_vector_insert(&ctl->windowfiles, mwf);
+ error = git_vector_insert(&ctl->windowfiles, mwf);
git_mutex_unlock(&git__mwindow_mutex);
+ if (error < 0)
+ goto cleanup;
+
+ /*
+ * Once we have released the global windowfiles lock, we can close each
+ * individual file. Before doing so, acquire that file's lock to avoid
+ * closing a file that is currently being used.
+ */
+ git_vector_foreach(&closed_files, i, closed_file) {
+ error = git_mutex_lock(&closed_file->lock);
+ if (error < 0)
+ continue;
+ p_close(closed_file->fd);
+ closed_file->fd = -1;
+ git_mutex_unlock(&closed_file->lock);
+ }
- return ret;
+cleanup:
+ git_vector_free(&closed_files);
+ return error;
}
void git_mwindow_file_deregister(git_mwindow_file *mwf)
} git_mwindow;
typedef struct git_mwindow_file {
+ git_mutex lock; /* protects updates to fd */
git_mwindow *windows;
int fd;
off64_t size;
} git_mwindow_ctl;
int git_mwindow_contains(git_mwindow *win, off64_t offset);
-void git_mwindow_free_all(git_mwindow_file *mwf); /* locks */
-void git_mwindow_free_all_locked(git_mwindow_file *mwf); /* run under lock */
+int git_mwindow_free_all(git_mwindow_file *mwf); /* locks */
unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, off64_t offset, size_t extra, unsigned int *left);
int git_mwindow_file_register(git_mwindow_file *mwf);
void git_mwindow_file_deregister(git_mwindow_file *mwf);
struct git_pack_file; /* just declaration to avoid cyclical includes */
int git_mwindow_get_pack(struct git_pack_file **out, const char *path);
-void git_mwindow_put_pack(struct git_pack_file *pack);
+int git_mwindow_put_pack(struct git_pack_file *pack);
#endif
#include "posix.h"
#include "buffer.h"
#include "http_parser.h"
-#include "global.h"
+#include "runtime.h"
#define DEFAULT_PORT_HTTP "80"
#define DEFAULT_PORT_HTTPS "443"
return NULL;
}
+int git_net_url_dup(git_net_url *out, git_net_url *in)
+{
+ if (in->scheme) {
+ out->scheme = git__strdup(in->scheme);
+ GIT_ERROR_CHECK_ALLOC(out->scheme);
+ }
+
+ if (in->host) {
+ out->host = git__strdup(in->host);
+ GIT_ERROR_CHECK_ALLOC(out->host);
+ }
+
+ if (in->port) {
+ out->port = git__strdup(in->port);
+ GIT_ERROR_CHECK_ALLOC(out->port);
+ }
+
+ if (in->path) {
+ out->path = git__strdup(in->path);
+ GIT_ERROR_CHECK_ALLOC(out->path);
+ }
+
+ if (in->query) {
+ out->query = git__strdup(in->query);
+ GIT_ERROR_CHECK_ALLOC(out->query);
+ }
+
+ if (in->username) {
+ out->username = git__strdup(in->username);
+ GIT_ERROR_CHECK_ALLOC(out->username);
+ }
+
+ if (in->password) {
+ out->password = git__strdup(in->password);
+ GIT_ERROR_CHECK_ALLOC(out->password);
+ }
+
+ return 0;
+}
+
int git_net_url_parse(git_net_url *url, const char *given)
{
struct http_parser_url u = {0};
git_net_url tmp = GIT_NET_URL_INIT;
int error = 0;
- assert(url && redirect_location);
+ GIT_ASSERT(url);
+ GIT_ASSERT(redirect_location);
if (redirect_location[0] == '/') {
git__free(url->path);
return (url->host && url->port && url->path);
}
-int git_net_url_is_default_port(git_net_url *url)
+bool git_net_url_is_default_port(git_net_url *url)
{
const char *default_port;
return false;
}
+bool git_net_url_is_ipv6(git_net_url *url)
+{
+ return (strchr(url->host, ':') != NULL);
+}
+
void git_net_url_swap(git_net_url *a, git_net_url *b)
{
git_net_url tmp = GIT_NET_URL_INIT;
int git_net_url_fmt(git_buf *buf, git_net_url *url)
{
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(url->scheme);
+ GIT_ASSERT_ARG(url->host);
+
git_buf_puts(buf, url->scheme);
git_buf_puts(buf, "://");
return git_buf_oom(buf) ? -1 : 0;
}
+static bool matches_pattern(
+ git_net_url *url,
+ const char *pattern,
+ size_t pattern_len)
+{
+ const char *domain, *port = NULL, *colon;
+ size_t host_len, domain_len, port_len = 0, wildcard = 0;
+
+ GIT_UNUSED(url);
+ GIT_UNUSED(pattern);
+
+ if (!pattern_len)
+ return false;
+ else if (pattern_len == 1 && pattern[0] == '*')
+ return true;
+ else if (pattern_len > 1 && pattern[0] == '*' && pattern[1] == '.')
+ wildcard = 2;
+ else if (pattern[0] == '.')
+ wildcard = 1;
+
+ domain = pattern + wildcard;
+ domain_len = pattern_len - wildcard;
+
+ if ((colon = memchr(domain, ':', domain_len)) != NULL) {
+ domain_len = colon - domain;
+ port = colon + 1;
+ port_len = pattern_len - wildcard - domain_len - 1;
+ }
+
+ /* A pattern's port *must* match if it's specified */
+ if (port_len && git__strlcmp(url->port, port, port_len) != 0)
+ return false;
+
+ /* No wildcard? Host must match exactly. */
+ if (!wildcard)
+ return !git__strlcmp(url->host, domain, domain_len);
+
+ /* Wildcard: ensure there's (at least) a suffix match */
+ if ((host_len = strlen(url->host)) < domain_len ||
+ memcmp(url->host + (host_len - domain_len), domain, domain_len))
+ return false;
+
+ /* The pattern is *.domain and the host is simply domain */
+ if (host_len == domain_len)
+ return true;
+
+ /* The pattern is *.domain and the host is foo.domain */
+ return (url->host[host_len - domain_len - 1] == '.');
+}
+
+bool git_net_url_matches_pattern(git_net_url *url, const char *pattern)
+{
+ return matches_pattern(url, pattern, strlen(pattern));
+}
+
+bool git_net_url_matches_pattern_list(
+ git_net_url *url,
+ const char *pattern_list)
+{
+ const char *pattern, *pattern_end, *sep;
+
+ for (pattern = pattern_list;
+ pattern && *pattern;
+ pattern = sep ? sep + 1 : NULL) {
+ sep = strchr(pattern, ',');
+ pattern_end = sep ? sep : strchr(pattern, '\0');
+
+ if (matches_pattern(url, pattern, (pattern_end - pattern)))
+ return true;
+ }
+
+ return false;
+}
+
void git_net_url_dispose(git_net_url *url)
{
if (url->username)
#define GIT_NET_URL_INIT { NULL }
+/** Duplicate a URL */
+extern int git_net_url_dup(git_net_url *out, git_net_url *in);
+
/** Parses a string containing a URL into a structure. */
extern int git_net_url_parse(git_net_url *url, const char *str);
/** Ensures that a URL is minimally valid (contains a host, port and path) */
extern bool git_net_url_valid(git_net_url *url);
-/** Returns nonzero if the URL is on the default port. */
-extern int git_net_url_is_default_port(git_net_url *url);
+/** Returns true if the URL is on the default port. */
+extern bool git_net_url_is_default_port(git_net_url *url);
+
+/** Returns true if the host portion of the URL is an ipv6 address. */
+extern bool git_net_url_is_ipv6(git_net_url *url);
/* Applies a redirect to the URL with a git-aware service suffix. */
extern int git_net_url_apply_redirect(
/** Place the path and query string into the given buffer. */
extern int git_net_url_fmt_path(git_buf *buf, git_net_url *url);
+/** Determines if the url matches given pattern or pattern list */
+extern bool git_net_url_matches_pattern(
+ git_net_url *url,
+ const char *pattern);
+extern bool git_net_url_matches_pattern_list(
+ git_net_url *url,
+ const char *pattern_list);
+
/** Disposes the contents of the structure. */
extern void git_net_url_dispose(git_net_url *url);
#include "posix.h"
#include "buffer.h"
#include "http_parser.h"
-#include "global.h"
+#include "runtime.h"
int gitno_recv(gitno_buffer *buf)
{
}
/* Consume up to ptr and move the rest of the buffer to the beginning */
-void gitno_consume(gitno_buffer *buf, const char *ptr)
+int gitno_consume(gitno_buffer *buf, const char *ptr)
{
size_t consumed;
- assert(ptr - buf->data >= 0);
- assert(ptr - buf->data <= (int) buf->len);
+ GIT_ASSERT(ptr - buf->data >= 0);
+ GIT_ASSERT(ptr - buf->data <= (int) buf->len);
consumed = ptr - buf->data;
memmove(buf->data, ptr, buf->offset - consumed);
memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
buf->offset -= consumed;
+
+ return 0;
}
/* Consume const bytes and move the rest of the buffer to the beginning */
#include "net.h"
#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
+# include "streams/openssl.h"
#endif
typedef struct gitno_ssl {
void gitno_buffer_setup_callback(gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data);
int gitno_recv(gitno_buffer *buf);
-void gitno_consume(gitno_buffer *buf, const char *ptr);
+int gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons);
#endif
return error;
}
-static int note_get_default_ref(char **out, git_repository *repo)
+static int note_get_default_ref(git_buf *out, git_repository *repo)
{
git_config *cfg;
- int ret = git_repository_config__weakptr(&cfg, repo);
+ int error;
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ return error;
- *out = (ret != 0) ? NULL : git_config__get_string_force(
- cfg, "core.notesref", GIT_NOTES_DEFAULT_REF);
+ error = git_config_get_string_buf(out, cfg, "core.notesref");
- return ret;
+ if (error == GIT_ENOTFOUND)
+ error = git_buf_puts(out, GIT_NOTES_DEFAULT_REF);
+
+ return error;
}
-static int normalize_namespace(char **out, git_repository *repo, const char *notes_ref)
+static int normalize_namespace(git_buf *out, git_repository *repo, const char *notes_ref)
{
- if (notes_ref) {
- *out = git__strdup(notes_ref);
- GIT_ERROR_CHECK_ALLOC(*out);
- return 0;
- }
+ if (notes_ref)
+ return git_buf_puts(out, notes_ref);
return note_get_default_ref(out, repo);
}
static int retrieve_note_commit(
git_commit **commit_out,
- char **notes_ref_out,
+ git_buf *notes_ref_out,
git_repository *repo,
const char *notes_ref)
{
if ((error = normalize_namespace(notes_ref_out, repo, notes_ref)) < 0)
return error;
- if ((error = git_reference_name_to_id(&oid, repo, *notes_ref_out)) < 0)
+ if ((error = git_reference_name_to_id(&oid, repo, notes_ref_out->ptr)) < 0)
return error;
if (git_commit_lookup(commit_out, repo, &oid) < 0)
const char *notes_ref_in, const git_oid *oid)
{
int error;
- char *notes_ref = NULL;
+ git_buf notes_ref = GIT_BUF_INIT;
git_commit *commit = NULL;
error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in);
error = git_note_commit_read(out, repo, commit, oid);
cleanup:
- git__free(notes_ref);
+ git_buf_dispose(¬es_ref);
git_commit_free(commit);
return error;
}
int allow_note_overwrite)
{
int error;
- char *notes_ref = NULL;
+ git_buf notes_ref = GIT_BUF_INIT;
git_commit *existing_notes_commit = NULL;
git_reference *ref = NULL;
git_oid notes_blob_oid, notes_commit_oid;
if (error < 0)
goto cleanup;
- error = git_reference_create(&ref, repo, notes_ref,
+ error = git_reference_create(&ref, repo, notes_ref.ptr,
¬es_commit_oid, 1, NULL);
if (out != NULL)
git_oid_cpy(out, ¬es_blob_oid);
cleanup:
- git__free(notes_ref);
+ git_buf_dispose(¬es_ref);
git_commit_free(existing_notes_commit);
git_reference_free(ref);
return error;
const git_oid *oid)
{
int error;
- char *notes_ref_target = NULL;
+ git_buf notes_ref_target = GIT_BUF_INIT;
git_commit *existing_notes_commit = NULL;
git_oid new_notes_commit;
git_reference *notes_ref = NULL;
if (error < 0)
goto cleanup;
- error = git_reference_create(¬es_ref, repo, notes_ref_target,
+ error = git_reference_create(¬es_ref, repo, notes_ref_target.ptr,
&new_notes_commit, 1, NULL);
cleanup:
- git__free(notes_ref_target);
+ git_buf_dispose(¬es_ref_target);
git_reference_free(notes_ref);
git_commit_free(existing_notes_commit);
return error;
int git_note_default_ref(git_buf *out, git_repository *repo)
{
- char *default_ref;
int error;
- assert(out && repo);
-
- git_buf_sanitize(out);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
- if ((error = note_get_default_ref(&default_ref, repo)) < 0)
- return error;
+ if ((error = git_buf_sanitize(out)) < 0 ||
+ (error = note_get_default_ref(out, repo)) < 0)
+ git_buf_dispose(out);
- git_buf_attach(out, default_ref, strlen(default_ref));
- return 0;
+ return error;
}
const git_signature *git_note_committer(const git_note *note)
{
- assert(note);
+ GIT_ASSERT_ARG_WITH_RETVAL(note, NULL);
return note->committer;
}
const git_signature *git_note_author(const git_note *note)
{
- assert(note);
+ GIT_ASSERT_ARG_WITH_RETVAL(note, NULL);
return note->author;
}
-const char * git_note_message(const git_note *note)
+const char *git_note_message(const git_note *note)
{
- assert(note);
+ GIT_ASSERT_ARG_WITH_RETVAL(note, NULL);
return note->message;
}
-const git_oid * git_note_id(const git_note *note)
+const git_oid *git_note_id(const git_note *note)
{
- assert(note);
+ GIT_ASSERT_ARG_WITH_RETVAL(note, NULL);
return ¬e->id;
}
}
static int process_entry_path(
- const char* entry_path,
+ const char *entry_path,
git_oid *annotated_object_id)
{
int error = 0;
{
int error;
git_commit *commit = NULL;
- char *notes_ref;
+ git_buf notes_ref = GIT_BUF_INIT;
error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in);
if (error < 0)
error = git_note_commit_iterator_new(it, commit);
cleanup:
- git__free(notes_ref);
+ git_buf_dispose(¬es_ref);
git_commit_free(commit);
return error;
}
int git_note_next(
- git_oid* note_id,
- git_oid* annotated_id,
+ git_oid *note_id,
+ git_oid *annotated_id,
git_note_iterator *it)
{
int error;
size_t object_size;
int error;
- assert(object_out);
+ GIT_ASSERT_ARG(object_out);
*object_out = NULL;
/* Validate type match */
/* Parse raw object data */
def = &git_objects_table[type];
- assert(def->free && def->parse_raw);
+ GIT_ASSERT(def->free && def->parse_raw);
if ((error = def->parse_raw(object, data, size)) < 0) {
def->free(object);
git_object_def *def;
git_object *object = NULL;
- assert(object_out);
+ GIT_ASSERT_ARG(object_out);
*object_out = NULL;
/* Validate type match */
/* Parse raw object data */
def = &git_objects_table[odb_obj->cached.type];
- assert(def->free && def->parse);
+ GIT_ASSERT(def->free && def->parse);
if ((error = def->parse(object, odb_obj)) < 0)
def->free(object);
git_odb_object *odb_obj = NULL;
int error = 0;
- assert(repo && object_out && id);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(object_out);
+ GIT_ASSERT_ARG(id);
if (len < GIT_OID_MINPREFIXLEN) {
git_error_set(GIT_ERROR_OBJECT, "ambiguous lookup - OID prefix is too short");
} else if (cached->flags == GIT_CACHE_STORE_RAW) {
odb_obj = (git_odb_object *)cached;
} else {
- assert(!"Wrong caching type in the global object cache");
+ GIT_ASSERT(!"Wrong caching type in the global object cache");
}
} else {
/* Object was not found in the cache, let's explore the backends.
const git_oid *git_object_id(const git_object *obj)
{
- assert(obj);
+ GIT_ASSERT_ARG_WITH_RETVAL(obj, NULL);
return &obj->cached.oid;
}
git_object_t git_object_type(const git_object *obj)
{
- assert(obj);
+ GIT_ASSERT_ARG_WITH_RETVAL(obj, GIT_OBJECT_INVALID);
return obj->cached.type;
}
git_repository *git_object_owner(const git_object *obj)
{
- assert(obj);
+ GIT_ASSERT_ARG_WITH_RETVAL(obj, NULL);
return obj->repo;
}
git_object *source, *deref = NULL;
int error;
- assert(object && peeled);
+ GIT_ASSERT_ARG(object);
+ GIT_ASSERT_ARG(peeled);
- assert(target_type == GIT_OBJECT_TAG ||
+ GIT_ASSERT_ARG(target_type == GIT_OBJECT_TAG ||
target_type == GIT_OBJECT_COMMIT ||
target_type == GIT_OBJECT_TREE ||
target_type == GIT_OBJECT_BLOB ||
git_tree *tree = NULL;
git_tree_entry *entry = NULL;
- assert(out && treeish && path);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(treeish);
+ GIT_ASSERT_ARG(path);
if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJECT_TREE)) < 0 ||
(error = git_tree_entry_bypath(&entry, tree, path)) < 0)
git_oid id = {{0}};
git_odb *odb;
- assert(out && obj);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(obj);
+
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
- git_buf_sanitize(out);
repo = git_object_owner(obj);
if ((error = git_repository__configmap_lookup(&len, repo, GIT_CONFIGMAP_ABBREV)) < 0)
#define GIT_ALTERNATES_FILE "info/alternates"
+#define GIT_ALTERNATES_MAX_DEPTH 5
+
/*
* We work under the assumption that most objects for long-running
* operations will be packed
*/
-#define GIT_LOOSE_PRIORITY 1
-#define GIT_PACKED_PRIORITY 2
-
-#define GIT_ALTERNATES_MAX_DEPTH 5
+int git_odb__loose_priority = GIT_ODB_DEFAULT_LOOSE_PRIORITY;
+int git_odb__packed_priority = GIT_ODB_DEFAULT_PACKED_PRIORITY;
bool git_odb__strict_hash_verification = true;
size_t hdrlen;
int error;
- assert(id && obj);
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(obj);
if (!git_object_typeisloose(obj->type)) {
git_error_set(GIT_ERROR_INVALID, "invalid object type");
if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) {
git_buf post = GIT_BUF_INIT;
- error = git_filter_list_apply_to_data(&post, fl, &raw);
-
- git_buf_dispose(&raw);
+ error = git_filter_list__convert_buf(&post, fl, &raw);
if (!error)
error = git_odb_hash(out, post.ptr, post.size, type);
GIT_ERROR_CHECK_ALLOC(link_data);
read_len = p_readlink(path, link_data, size);
- link_data[size] = '\0';
- if (read_len != size) {
+ if (read_len == -1) {
git_error_set(GIT_ERROR_OS, "failed to read symlink data for '%s'", path);
git__free(link_data);
return -1;
}
+ GIT_ASSERT(read_len <= size);
+ link_data[read_len] = '\0';
- result = git_odb_hash(out, link_data, size, GIT_OBJECT_BLOB);
+ result = git_odb_hash(out, link_data, read_len, GIT_OBJECT_BLOB);
git__free(link_data);
} else {
int fd = git_futils_open_ro(path);
{
git_rawobj raw;
- assert(id);
+ GIT_ASSERT_ARG(id);
raw.data = (void *)data;
raw.len = len;
{
fake_wstream *stream = (fake_wstream *)_stream;
- assert(stream->written + len <= stream->size);
+ GIT_ASSERT(stream->written + len <= stream->size);
memcpy(stream->buffer + stream->written, data, len);
stream->written += len;
git_odb *db = git__calloc(1, sizeof(*db));
GIT_ERROR_CHECK_ALLOC(db);
+ if (git_mutex_init(&db->lock) < 0) {
+ git__free(db);
+ return -1;
+ }
if (git_cache_init(&db->own_cache) < 0) {
+ git_mutex_free(&db->lock);
git__free(db);
return -1;
}
if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) {
git_cache_dispose(&db->own_cache);
+ git_mutex_free(&db->lock);
git__free(db);
return -1;
}
{
backend_internal *internal;
- assert(odb && backend);
+ GIT_ASSERT_ARG(odb);
+ GIT_ASSERT_ARG(backend);
GIT_ERROR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend");
/* Check if the backend is already owned by another ODB */
- assert(!backend->odb || backend->odb == odb);
+ GIT_ASSERT(!backend->odb || backend->odb == odb);
internal = git__malloc(sizeof(backend_internal));
GIT_ERROR_CHECK_ALLOC(internal);
internal->is_alternate = is_alternate;
internal->disk_inode = disk_inode;
+ if (git_mutex_lock(&odb->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return -1;
+ }
if (git_vector_insert(&odb->backends, internal) < 0) {
+ git_mutex_unlock(&odb->lock);
git__free(internal);
return -1;
}
-
git_vector_sort(&odb->backends);
internal->backend->odb = odb;
+ git_mutex_unlock(&odb->lock);
return 0;
}
size_t git_odb_num_backends(git_odb *odb)
{
- assert(odb);
- return odb->backends.length;
+ size_t length;
+ bool locked = true;
+
+ GIT_ASSERT_ARG(odb);
+
+ if (git_mutex_lock(&odb->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ locked = false;
+ }
+ length = odb->backends.length;
+ if (locked)
+ git_mutex_unlock(&odb->lock);
+ return length;
}
static int git_odb__error_unsupported_in_backend(const char *action)
int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos)
{
backend_internal *internal;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(odb);
- assert(out && odb);
+
+ if ((error = git_mutex_lock(&odb->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
internal = git_vector_get(&odb->backends, pos);
- if (internal && internal->backend) {
- *out = internal->backend;
- return 0;
+ if (!internal || !internal->backend) {
+ git_mutex_unlock(&odb->lock);
+
+ git_error_set(GIT_ERROR_ODB, "no ODB backend loaded at index %" PRIuZ, pos);
+ return GIT_ENOTFOUND;
}
+ *out = internal->backend;
+ git_mutex_unlock(&odb->lock);
- git_error_set(GIT_ERROR_ODB, "no ODB backend loaded at index %" PRIuZ, pos);
- return GIT_ENOTFOUND;
+ return 0;
}
int git_odb__add_default_backends(
git_odb *db, const char *objects_dir,
bool as_alternates, int alternate_depth)
{
- size_t i;
+ size_t i = 0;
struct stat st;
ino_t inode;
git_odb_backend *loose, *packed;
* a cross-platform workaround for this */
#ifdef GIT_WIN32
GIT_UNUSED(i);
- GIT_UNUSED(st);
+ GIT_UNUSED(&st);
inode = 0;
#else
inode = st.st_ino;
+ if (git_mutex_lock(&db->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return -1;
+ }
for (i = 0; i < db->backends.length; ++i) {
backend_internal *backend = git_vector_get(&db->backends, i);
- if (backend->disk_inode == inode)
+ if (backend->disk_inode == inode) {
+ git_mutex_unlock(&db->lock);
return 0;
+ }
}
+ git_mutex_unlock(&db->lock);
#endif
/* add the loose object backend */
if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 ||
- add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0)
+ add_backend_internal(db, loose, git_odb__loose_priority, as_alternates, inode) < 0)
return -1;
/* add the packed file backend */
if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
- add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, inode) < 0)
+ add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0)
return -1;
+ if (git_mutex_lock(&db->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return -1;
+ }
+ if (!db->cgraph && git_commit_graph_new(&db->cgraph, objects_dir, false) < 0) {
+ git_mutex_unlock(&db->lock);
+ return -1;
+ }
+ git_mutex_unlock(&db->lock);
+
return load_alternates(db, objects_dir, alternate_depth);
}
return git_odb__add_default_backends(odb, path, true, 0);
}
+int git_odb_set_commit_graph(git_odb *odb, git_commit_graph *cgraph)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(odb);
+
+ if ((error = git_mutex_lock(&odb->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock");
+ return error;
+ }
+ git_commit_graph_free(odb->cgraph);
+ odb->cgraph = cgraph;
+ git_mutex_unlock(&odb->lock);
+
+ return error;
+}
+
int git_odb_open(git_odb **out, const char *objects_dir)
{
git_odb *db;
- assert(out && objects_dir);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(objects_dir);
*out = NULL;
static void odb_free(git_odb *db)
{
size_t i;
+ bool locked = true;
+ if (git_mutex_lock(&db->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ locked = false;
+ }
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *backend = internal->backend;
git__free(internal);
}
+ if (locked)
+ git_mutex_unlock(&db->lock);
+ git_commit_graph_free(db->cgraph);
git_vector_free(&db->backends);
git_cache_dispose(&db->own_cache);
+ git_mutex_free(&db->lock);
git__memzero(db, sizeof(*db));
git__free(db);
{
size_t i;
bool found = false;
+ int error;
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->exists != NULL)
found = (bool)b->exists(b, id);
}
+ git_mutex_unlock(&db->lock);
return (int)found;
}
+int git_odb__get_commit_graph_file(git_commit_graph_file **out, git_odb *db)
+{
+ int error = 0;
+ git_commit_graph_file *result = NULL;
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock");
+ return error;
+ }
+ if (!db->cgraph) {
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+ error = git_commit_graph_get_file(&result, db->cgraph);
+ if (error)
+ goto done;
+ *out = result;
+
+done:
+ git_mutex_unlock(&db->lock);
+ return error;
+}
+
static int odb_freshen_1(
git_odb *db,
const git_oid *id,
{
size_t i;
bool found = false;
+ int error;
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
else if (b->exists != NULL)
found = b->exists(b, id);
}
+ git_mutex_unlock(&db->lock);
return (int)found;
}
int git_odb__freshen(git_odb *db, const git_oid *id)
{
- assert(db && id);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(id);
if (odb_freshen_1(db, id, false))
return 1;
{
git_odb_object *object;
- assert(db && id);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(id);
if (git_oid_is_zero(id))
return 0;
int error = GIT_ENOTFOUND, num_found = 0;
git_oid last_found = {{0}}, found;
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ error = GIT_ENOTFOUND;
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
error = b->exists_prefix(&found, b, key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
- if (error)
+ if (error) {
+ git_mutex_unlock(&db->lock);
return error;
+ }
/* make sure found item doesn't introduce ambiguity */
if (num_found) {
- if (git_oid__cmp(&last_found, &found))
+ if (git_oid__cmp(&last_found, &found)) {
+ git_mutex_unlock(&db->lock);
return git_odb__error_ambiguous("multiple matches for prefix");
+ }
} else {
git_oid_cpy(&last_found, &found);
num_found++;
}
}
+ git_mutex_unlock(&db->lock);
if (!num_found)
return GIT_ENOTFOUND;
int error;
git_oid key = {{0}};
- assert(db && short_id);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(short_id);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
{
size_t i;
- assert(db && ids);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(ids);
for (i = 0; i < count; i++) {
git_odb_expand_id *query = &ids[i];
return 0;
}
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
case GIT_ENOTFOUND:
break;
default:
+ git_mutex_unlock(&db->lock);
return error;
}
}
+ git_mutex_unlock(&db->lock);
return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND;
}
int error = GIT_ENOTFOUND;
git_odb_object *object;
- assert(db && id && out && len_p && type_p);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(len_p);
+ GIT_ASSERT_ARG(type_p);
*out = NULL;
return error;
}
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
continue;
- if (error < 0)
+ if (error < 0) {
+ git_mutex_unlock(&db->lock);
return error;
+ }
found = true;
}
}
+ git_mutex_unlock(&db->lock);
if (!found)
return GIT_ENOTFOUND;
{
int error;
- assert(out && db && id);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(id);
if (git_oid_is_zero(id))
return error_null_oid(GIT_ENOTFOUND, "cannot read object");
bool found = false;
git_odb_object *object;
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
continue;
}
- if (error)
+ if (error) {
+ git_mutex_unlock(&db->lock);
goto out;
+ }
git__free(data);
data = raw.data;
error = git_odb__error_ambiguous(buf.ptr);
git_buf_dispose(&buf);
+ git_mutex_unlock(&db->lock);
goto out;
}
found = true;
}
}
+ git_mutex_unlock(&db->lock);
if (!found)
return GIT_ENOTFOUND;
git_oid key = {{0}};
int error;
- assert(out && db);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(db);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload)
{
unsigned int i;
+ git_vector backends = GIT_VECTOR_INIT;
backend_internal *internal;
+ int error = 0;
+
+ /* Make a copy of the backends vector to invoke the callback without holding the lock. */
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ goto cleanup;
+ }
+ error = git_vector_dup(&backends, &db->backends, NULL);
+ git_mutex_unlock(&db->lock);
+
+ if (error < 0)
+ goto cleanup;
- git_vector_foreach(&db->backends, i, internal) {
+ git_vector_foreach(&backends, i, internal) {
git_odb_backend *b = internal->backend;
- int error = b->foreach(b, cb, payload);
+ error = b->foreach(b, cb, payload);
if (error != 0)
- return error;
+ goto cleanup;
}
- return 0;
+cleanup:
+ git_vector_free(&backends);
+
+ return error;
}
int git_odb_write(
int error;
git_odb_stream *stream;
- assert(oid && db);
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(db);
if ((error = git_odb_hash(oid, data, len, type)) < 0)
return error;
if (git_odb__freshen(db, oid))
return 0;
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
for (i = 0, error = GIT_ERROR; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->write != NULL)
error = b->write(b, oid, data, len, type);
}
+ git_mutex_unlock(&db->lock);
if (!error || error == GIT_PASSTHROUGH)
return 0;
int error = GIT_ERROR;
git_hash_ctx *ctx = NULL;
- assert(stream && db);
+ GIT_ASSERT_ARG(stream);
+ GIT_ASSERT_ARG(db);
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ error = GIT_ERROR;
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
error = init_fake_wstream(stream, b, size, type);
}
}
+ git_mutex_unlock(&db->lock);
if (error < 0) {
if (error == GIT_PASSTHROUGH)
size_t i, reads = 0;
int error = GIT_ERROR;
- assert(stream && db);
+ GIT_ASSERT_ARG(stream);
+ GIT_ASSERT_ARG(db);
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ error = GIT_ERROR;
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
error = b->readstream(stream, len, type, b, oid);
}
}
+ git_mutex_unlock(&db->lock);
if (error == GIT_PASSTHROUGH)
error = 0;
size_t i, writes = 0;
int error = GIT_ERROR;
- assert(out && db);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(db);
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ error = GIT_ERROR;
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
error = b->writepack(out, b, db, progress_cb, progress_payload);
}
}
+ git_mutex_unlock(&db->lock);
if (error == GIT_PASSTHROUGH)
error = 0;
return error;
}
+int git_odb_write_multi_pack_index(git_odb *db)
+{
+ size_t i, writes = 0;
+ int error = GIT_ERROR;
+
+ GIT_ASSERT_ARG(db);
+
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ /* we don't write in alternates! */
+ if (internal->is_alternate)
+ continue;
+
+ if (b->writemidx != NULL) {
+ ++writes;
+ error = b->writemidx(b);
+ }
+ }
+
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ if (error < 0 && !writes)
+ error = git_odb__error_unsupported_in_backend("write multi-pack-index");
+
+ return error;
+}
+
void *git_odb_backend_data_alloc(git_odb_backend *backend, size_t len)
{
GIT_UNUSED(backend);
int git_odb_refresh(struct git_odb *db)
{
size_t i;
- assert(db);
+ int error;
+
+ GIT_ASSERT_ARG(db);
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->refresh != NULL) {
int error = b->refresh(b);
- if (error < 0)
+ if (error < 0) {
+ git_mutex_unlock(&db->lock);
return error;
+ }
}
}
+ if (db->cgraph)
+ git_commit_graph_refresh(db->cgraph);
+ git_mutex_unlock(&db->lock);
return 0;
}
#include "git2/odb.h"
#include "git2/oid.h"
#include "git2/types.h"
+#include "git2/sys/commit_graph.h"
-#include "vector.h"
#include "cache.h"
-#include "posix.h"
+#include "commit_graph.h"
#include "filter.h"
+#include "posix.h"
+#include "vector.h"
#define GIT_OBJECTS_DIR "objects/"
#define GIT_OBJECT_DIR_MODE 0777
#define GIT_OBJECT_FILE_MODE 0444
+#define GIT_ODB_DEFAULT_LOOSE_PRIORITY 1
+#define GIT_ODB_DEFAULT_PACKED_PRIORITY 2
+
extern bool git_odb__strict_hash_verification;
/* DO NOT EXPORT */
/* EXPORT */
struct git_odb {
git_refcount rc;
+ git_mutex lock; /* protects backends */
git_vector backends;
git_cache own_cache;
+ git_commit_graph *cgraph;
unsigned int do_fsync :1;
};
git_odb_object **out, size_t *len_p, git_object_t *type_p,
git_odb *db, const git_oid *id);
+/*
+ * Attempt to get the ODB's commit-graph file. This object is still owned by
+ * the ODB. If the repository does not contain a commit-graph, it will return
+ * GIT_ENOTFOUND.
+ */
+int git_odb__get_commit_graph_file(git_commit_graph_file **out, git_odb *odb);
+
/* freshen an entry in the object database */
int git_odb__freshen(git_odb *db, const git_oid *id);
goto done;
}
- assert(decompressed >= head_len);
+ GIT_ASSERT(decompressed >= head_len);
body_len = decompressed - head_len;
if (body_len)
int error;
git_buf obj = GIT_BUF_INIT;
- assert(out && loc);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(loc);
if (git_buf_oom(loc))
return -1;
ssize_t obj_len;
int fd, error;
- assert(out && loc);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(loc);
if (git_buf_oom(loc))
return -1;
git_rawobj raw;
int error;
- assert(backend && oid);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(oid);
raw.len = 0;
raw.type = GIT_OBJECT_INVALID;
git_rawobj raw;
int error = 0;
- assert(backend && oid);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(oid);
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
error = git_odb__error_notfound("no matching loose object",
{
int error = 0;
- assert(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ);
+ GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ);
if (len == GIT_OID_HEXSZ) {
/* We can fall back to regular read method */
git_buf object_path = GIT_BUF_INIT;
git_rawobj raw;
- assert(backend && short_oid);
+ GIT_ASSERT_ARG(backend && short_oid);
if ((error = locate_object_short_oid(&object_path, out_oid,
(loose_backend *)backend, short_oid, len)) == 0 &&
git_buf object_path = GIT_BUF_INIT;
int error;
- assert(backend && oid);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(oid);
error = locate_object(&object_path, (loose_backend *)backend, oid);
git_buf object_path = GIT_BUF_INIT;
int error;
- assert(backend && out && short_id && len >= GIT_OID_MINPREFIXLEN);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(short_id);
+ GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN);
error = locate_object_short_oid(
&object_path, out, (loose_backend *)backend, short_id, len);
struct foreach_state state;
loose_backend *backend = (loose_backend *) _backend;
- assert(backend && cb);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(cb);
objects_dir = backend->objects_dir;
size_t hdrlen;
int error;
- assert(_backend);
+ GIT_ASSERT_ARG(_backend);
backend = (loose_backend *)_backend;
*stream_out = NULL;
obj_hdr hdr;
int error = 0;
- assert(stream_out && len_out && type_out && _backend && oid);
+ GIT_ASSERT_ARG(stream_out);
+ GIT_ASSERT_ARG(len_out);
+ GIT_ASSERT_ARG(type_out);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(oid);
backend = (loose_backend *)_backend;
*stream_out = NULL;
static void loose_backend__free(git_odb_backend *_backend)
{
- loose_backend *backend;
- assert(_backend);
- backend = (loose_backend *)_backend;
-
- git__free(backend);
+ git__free(_backend);
}
int git_odb_backend_loose(
loose_backend *backend;
size_t objects_dirlen, alloclen;
- assert(backend_out && objects_dir);
+ GIT_ASSERT_ARG(backend_out);
+ GIT_ASSERT_ARG(objects_dir);
objects_dirlen = strlen(objects_dir);
{
struct memory_packer_db *db;
- assert(out);
+ GIT_ASSERT_ARG(out);
db = git__calloc(1, sizeof(struct memory_packer_db));
GIT_ERROR_CHECK_ALLOC(db);
#include "git2/repository.h"
#include "git2/indexer.h"
#include "git2/sys/odb_backend.h"
+#include "delta.h"
#include "futils.h"
#include "hash.h"
-#include "odb.h"
-#include "delta.h"
+#include "midx.h"
#include "mwindow.h"
+#include "odb.h"
#include "pack.h"
#include "git2/odb_backend.h"
struct pack_backend {
git_odb_backend parent;
+ git_midx_file *midx;
+ git_vector midx_packs;
git_vector packs;
struct git_pack_file *last_found;
char *pack_folder;
* Initialization of the Pack Backend
* --------------------------------------------------
*
- * # git_odb_backend_pack
- * | Creates the pack backend structure, initializes the
- * | callback pointers to our default read() and exist() methods,
- * | and tries to preload all the known packfiles in the ODB.
+ * # git_odb_backend_pack
+ * | Creates the pack backend structure, initializes the
+ * | callback pointers to our default read() and exist() methods,
+ * | and tries to find the `pack` folder, if it exists. ODBs without a `pack`
+ * | folder are ignored altogether. If there is a `pack` folder, it tries to
+ * | preload all the known packfiles in the ODB.
* |
- * |-# packfile_load_all
- * | Tries to find the `pack` folder, if it exists. ODBs without
- * | a pack folder are ignored altogether. If there's a `pack` folder
- * | we run a `dirent` callback through every file in the pack folder
- * | to find our packfiles. The packfiles are then sorted according
- * | to a sorting callback.
- * |
- * |-# packfile_load__cb
- * | | This callback is called from `dirent` with every single file
- * | | inside the pack folder. We find the packs by actually locating
- * | | their index (ends in ".idx"). From that index, we verify that
- * | | the corresponding packfile exists and is valid, and if so, we
- * | | add it to the pack list.
- * | |
- * | |-# packfile_check
- * | Make sure that there's a packfile to back this index, and store
- * | some very basic information regarding the packfile itself,
- * | such as the full path, the size, and the modification time.
- * | We don't actually open the packfile to check for internal consistency.
- * |
- * |-# packfile_sort__cb
- * Sort all the preloaded packs according to some specific criteria:
- * we prioritize the "newer" packs because it's more likely they
- * contain the objects we are looking for, and we prioritize local
- * packs over remote ones.
+ * |-# pack_backend__refresh
+ * | The `multi-pack-index` is loaded if it exists and is valid.
+ * | Then we run a `dirent` callback through every file in the pack folder,
+ * | even those present in `multi-pack-index`. The unindexed packfiles are
+ * | then sorted according to a sorting callback.
+ * |
+ * |-# refresh_multi_pack_index
+ * | Detect the presence of the `multi-pack-index` file. If it needs to be
+ * | refreshed, frees the old copy and tries to load the new one, together
+ * | with all the packfiles it indexes. If the process fails, fall back to
+ * | the old behavior, as if the `multi-pack-index` file was not there.
+ * |
+ * |-# packfile_load__cb
+ * | | This callback is called from `dirent` with every single file
+ * | | inside the pack folder. We find the packs by actually locating
+ * | | their index (ends in ".idx"). From that index, we verify that
+ * | | the corresponding packfile exists and is valid, and if so, we
+ * | | add it to the pack list.
+ * | |
+ * | # git_mwindow_get_pack
+ * | Make sure that there's a packfile to back this index, and store
+ * | some very basic information regarding the packfile itself,
+ * | such as the full path, the size, and the modification time.
+ * | We don't actually open the packfile to check for internal consistency.
+ * |
+ * |-# packfile_sort__cb
+ * Sort all the preloaded packs according to some specific criteria:
+ * we prioritize the "newer" packs because it's more likely they
+ * contain the objects we are looking for, and we prioritize local
+ * packs over remote ones.
*
*
*
* A standard packed `exist` query for an OID
* --------------------------------------------------
*
- * # pack_backend__exists
- * | Check if the given SHA1 oid exists in any of the packs
- * | that have been loaded for our ODB.
+ * # pack_backend__exists / pack_backend__exists_prefix
+ * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the
+ * | packs that have been loaded for our ODB.
* |
- * |-# pack_entry_find
- * | Iterate through all the packs that have been preloaded
- * | (starting by the pack where the latest object was found)
- * | to try to find the OID in one of them.
- * |
- * |-# pack_entry_find1
- * | Check the index of an individual pack to see if the SHA1
- * | OID can be found. If we can find the offset to that SHA1
- * | inside of the index, that means the object is contained
- * | inside of the packfile and we can stop searching.
- * | Before returning, we verify that the packfile behing the
- * | index we are searching still exists on disk.
- * |
- * |-# pack_entry_find_offset
- * | | Mmap the actual index file to disk if it hasn't been opened
- * | | yet, and run a binary search through it to find the OID.
- * | | See <http://book.git-scm.com/7_the_packfile.html> for specifics
- * | | on the Packfile Index format and how do we find entries in it.
- * | |
- * | |-# pack_index_open
- * | | Guess the name of the index based on the full path to the
- * | | packfile, open it and verify its contents. Only if the index
- * | | has not been opened already.
- * | |
- * | |-# pack_index_check
- * | Mmap the index file and do a quick run through the header
- * | to guess the index version (right now we support v1 and v2),
- * | and to verify that the size of the index makes sense.
- * |
- * |-# packfile_open
- * See `packfile_open` in Chapter 3
+ * |-# pack_entry_find / pack_entry_find_prefix
+ * | If there is a multi-pack-index present, search the SHA1 oid in that
+ * | index first. If it is not found there, iterate through all the unindexed
+ * | packs that have been preloaded (starting by the pack where the latest
+ * | object was found) to try to find the OID in one of them.
+ * |
+ * |-# git_midx_entry_find
+ * | Search for the SHA1 oid in the multi-pack-index. See
+ * | <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
+ * | for specifics on the multi-pack-index format and how do we find
+ * | entries in it.
+ * |
+ * |-# git_pack_entry_find
+ * | Check the index of an individual unindexed pack to see if the SHA1
+ * | OID can be found. If we can find the offset to that SHA1 inside of the
+ * | index, that means the object is contained inside of the packfile and
+ * | we can stop searching. Before returning, we verify that the
+ * | packfile behing the index we are searching still exists on disk.
+ * |
+ * |-# pack_entry_find_offset
+ * | Mmap the actual index file to disk if it hasn't been opened
+ * | yet, and run a binary search through it to find the OID.
+ * | See <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
+ * | for specifics on the Packfile Index format and how do we find
+ * | entries in it.
+ * |
+ * |-# pack_index_open
+ * | Guess the name of the index based on the full path to the
+ * | packfile, open it and verify its contents. Only if the index
+ * | has not been opened already.
+ * |
+ * |-# pack_index_check
+ * Mmap the index file and do a quick run through the header
+ * to guess the index version (right now we support v1 and v2),
+ * and to verify that the size of the index makes sense.
*
*
*
* Chapter 3: The neverending story...
* A standard packed `lookup` query for an OID
* --------------------------------------------------
- * TODO
+ *
+ * # pack_backend__read / pack_backend__read_prefix
+ * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the
+ * | packs that have been loaded for our ODB. If it does, open the packfile and
+ * | read from it.
+ * |
+ * |-# git_packfile_unpack
+ * Armed with a packfile and the offset within it, we can finally unpack
+ * the object pointed at by the SHA1 oid. This involves mmapping part of
+ * the `.pack` file, and uncompressing the object within it (if it is
+ * stored in the undelfitied representation), or finding a base object and
+ * applying some deltas to its uncompressed representation (if it is stored
+ * in the deltified representation). See
+ * <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
+ * for specifics on the Packfile format and how do we read from it.
*
*/
static int packfile_load__cb(void *_data, git_buf *path);
+static int packfile_byname_search_cmp(const void *path, const void *pack_entry);
+
static int pack_entry_find(struct git_pack_entry *e,
struct pack_backend *backend, const git_oid *oid);
*
***********************************************************/
+static int packfile_byname_search_cmp(const void *path_, const void *p_)
+{
+ const git_buf *path = (const git_buf *)path_;
+ const struct git_pack_file *p = (const struct git_pack_file *)p_;
+
+ return strncmp(p->pack_name, git_buf_cstr(path), git_buf_len(path));
+}
+
static int packfile_sort__cb(const void *a_, const void *b_)
{
const struct git_pack_file *a = a_;
struct pack_backend *backend = data;
struct git_pack_file *pack;
const char *path_str = git_buf_cstr(path);
- size_t i, cmp_len = git_buf_len(path);
+ git_buf index_prefix = GIT_BUF_INIT;
+ size_t cmp_len = git_buf_len(path);
int error;
if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0)
return 0; /* not an index */
cmp_len -= strlen(".idx");
+ git_buf_attach_notowned(&index_prefix, path_str, cmp_len);
- for (i = 0; i < backend->packs.length; ++i) {
- struct git_pack_file *p = git_vector_get(&backend->packs, i);
-
- if (strncmp(p->pack_name, path_str, cmp_len) == 0)
- return 0;
- }
+ if (git_vector_search2(NULL, &backend->midx_packs, packfile_byname_search_cmp, &index_prefix) == 0)
+ return 0;
+ if (git_vector_search2(NULL, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0)
+ return 0;
error = git_mwindow_get_pack(&pack, path->ptr);
}
-static int pack_entry_find_inner(
- struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *oid,
- struct git_pack_file *last_found)
+static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
{
+ struct git_pack_file *last_found = backend->last_found, *p;
+ git_midx_entry midx_entry;
size_t i;
+ if (backend->midx &&
+ git_midx_entry_find(&midx_entry, backend->midx, oid, GIT_OID_HEXSZ) == 0 &&
+ midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
+ e->offset = midx_entry.offset;
+ git_oid_cpy(&e->sha1, &midx_entry.sha1);
+ e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
+ return 0;
+ }
+
if (last_found &&
git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0)
return 0;
- for (i = 0; i < backend->packs.length; ++i) {
- struct git_pack_file *p;
-
- p = git_vector_get(&backend->packs, i);
+ git_vector_foreach(&backend->packs, i, p) {
if (p == last_found)
continue;
}
}
- return -1;
-}
-
-static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
-{
- struct git_pack_file *last_found = backend->last_found;
-
- if (backend->last_found &&
- git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)
- return 0;
-
- if (!pack_entry_find_inner(e, backend, oid, last_found))
- return 0;
-
return git_odb__error_notfound(
"failed to find pack entry", oid, GIT_OID_HEXSZ);
}
size_t i;
git_oid found_full_oid = {{0}};
bool found = false;
- struct git_pack_file *last_found = backend->last_found;
+ struct git_pack_file *last_found = backend->last_found, *p;
+ git_midx_entry midx_entry;
+
+ if (backend->midx) {
+ error = git_midx_entry_find(&midx_entry, backend->midx, short_oid, len);
+ if (error == GIT_EAMBIGUOUS)
+ return error;
+ if (!error && midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
+ e->offset = midx_entry.offset;
+ git_oid_cpy(&e->sha1, &midx_entry.sha1);
+ e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
+ git_oid_cpy(&found_full_oid, &e->sha1);
+ found = true;
+ }
+ }
if (last_found) {
error = git_pack_entry_find(e, last_found, short_oid, len);
if (error == GIT_EAMBIGUOUS)
return error;
if (!error) {
+ if (found && git_oid_cmp(&e->sha1, &found_full_oid))
+ return git_odb__error_ambiguous("found multiple pack entries");
git_oid_cpy(&found_full_oid, &e->sha1);
found = true;
}
}
- for (i = 0; i < backend->packs.length; ++i) {
- struct git_pack_file *p;
-
- p = git_vector_get(&backend->packs, i);
+ git_vector_foreach(&backend->packs, i, p) {
if (p == last_found)
continue;
return 0;
}
+/***********************************************************
+ *
+ * MULTI-PACK-INDEX SUPPORT
+ *
+ * Functions needed to support the multi-pack-index.
+ *
+ ***********************************************************/
+
+/*
+ * Remove the multi-pack-index, and move all midx_packs to packs.
+ */
+static int remove_multi_pack_index(struct pack_backend *backend)
+{
+ size_t i, j = git_vector_length(&backend->packs);
+ struct pack_backend *p;
+ int error = git_vector_size_hint(
+ &backend->packs,
+ j + git_vector_length(&backend->midx_packs));
+ if (error < 0)
+ return error;
+
+ git_vector_foreach(&backend->midx_packs, i, p)
+ git_vector_set(NULL, &backend->packs, j++, p);
+ git_vector_clear(&backend->midx_packs);
+
+ git_midx_free(backend->midx);
+ backend->midx = NULL;
+
+ return 0;
+}
+
+/*
+ * Loads a single .pack file referred to by the multi-pack-index. These must
+ * match the order in which they are declared in the multi-pack-index file,
+ * since these files are referred to by their index.
+ */
+static int process_multi_pack_index_pack(
+ struct pack_backend *backend,
+ size_t i,
+ const char *packfile_name)
+{
+ int error;
+ struct git_pack_file *pack;
+ size_t found_position;
+ git_buf pack_path = GIT_BUF_INIT, index_prefix = GIT_BUF_INIT;
+
+ error = git_buf_joinpath(&pack_path, backend->pack_folder, packfile_name);
+ if (error < 0)
+ return error;
+
+ /* This is ensured by midx_parse_packfile_name() */
+ if (git_buf_len(&pack_path) <= strlen(".idx") || git__suffixcmp(git_buf_cstr(&pack_path), ".idx") != 0)
+ return git_odb__error_notfound("midx file contained a non-index", NULL, 0);
+
+ git_buf_attach_notowned(&index_prefix, git_buf_cstr(&pack_path), git_buf_len(&pack_path) - strlen(".idx"));
+
+ if (git_vector_search2(&found_position, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0) {
+ /* Pack was found in the packs list. Moving it to the midx_packs list. */
+ git_buf_dispose(&pack_path);
+ git_vector_set(NULL, &backend->midx_packs, i, git_vector_get(&backend->packs, found_position));
+ git_vector_remove(&backend->packs, found_position);
+ return 0;
+ }
+
+ /* Pack was not found. Allocate a new one. */
+ error = git_mwindow_get_pack(&pack, git_buf_cstr(&pack_path));
+ git_buf_dispose(&pack_path);
+ if (error < 0)
+ return error;
+
+ git_vector_set(NULL, &backend->midx_packs, i, pack);
+ return 0;
+}
+
+/*
+ * Reads the multi-pack-index. If this fails for whatever reason, the
+ * multi-pack-index object is freed, and all the packfiles that are related to
+ * it are moved to the unindexed packfiles vector.
+ */
+static int refresh_multi_pack_index(struct pack_backend *backend)
+{
+ int error;
+ git_buf midx_path = GIT_BUF_INIT;
+ const char *packfile_name;
+ size_t i;
+
+ error = git_buf_joinpath(&midx_path, backend->pack_folder, "multi-pack-index");
+ if (error < 0)
+ return error;
+
+ /*
+ * Check whether the multi-pack-index has changed. If it has, close any
+ * old multi-pack-index and move all the packfiles to the unindexed
+ * packs. This is done to prevent losing any open packfiles in case
+ * refreshing the new multi-pack-index fails, or the file is deleted.
+ */
+ if (backend->midx) {
+ if (!git_midx_needs_refresh(backend->midx, git_buf_cstr(&midx_path))) {
+ git_buf_dispose(&midx_path);
+ return 0;
+ }
+ error = remove_multi_pack_index(backend);
+ if (error < 0) {
+ git_buf_dispose(&midx_path);
+ return error;
+ }
+ }
+
+ error = git_midx_open(&backend->midx, git_buf_cstr(&midx_path));
+ git_buf_dispose(&midx_path);
+ if (error < 0)
+ return error;
+
+ git_vector_resize_to(&backend->midx_packs, git_vector_length(&backend->midx->packfile_names));
+
+ git_vector_foreach(&backend->midx->packfile_names, i, packfile_name) {
+ error = process_multi_pack_index_pack(backend, i, packfile_name);
+ if (error < 0) {
+ /*
+ * Something failed during reading multi-pack-index.
+ * Restore the state of backend as if the
+ * multi-pack-index was never there, and move all
+ * packfiles that have been processed so far to the
+ * unindexed packs.
+ */
+ git_vector_resize_to(&backend->midx_packs, i);
+ remove_multi_pack_index(backend);
+ return error;
+ }
+ }
+
+ return 0;
+}
/***********************************************************
*
if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
- git_buf_sets(&path, backend->pack_folder);
+ if (refresh_multi_pack_index(backend) < 0) {
+ /*
+ * It is okay if this fails. We will just not use the
+ * multi-pack-index in this case.
+ */
+ git_error_clear();
+ }
/* reload all packs */
+ git_buf_sets(&path, backend->pack_folder);
error = git_path_direach(&path, 0, packfile_load__cb, backend);
git_buf_dispose(&path);
struct git_pack_entry e;
int error;
- assert(len_p && type_p && backend && oid);
+ GIT_ASSERT_ARG(len_p);
+ GIT_ASSERT_ARG(type_p);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(oid);
if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
return error;
struct pack_backend *backend;
unsigned int i;
- assert(_backend && cb);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(cb);
+
backend = (struct pack_backend *)_backend;
/* Make sure we know about the packfiles */
- if ((error = pack_backend__refresh(_backend)) < 0)
+ if ((error = pack_backend__refresh(_backend)) != 0)
return error;
+ if (backend->midx && (error = git_midx_foreach_entry(backend->midx, cb, data)) != 0)
+ return error;
git_vector_foreach(&backend->packs, i, p) {
if ((error = git_pack_foreach_entry(p, cb, data)) != 0)
return error;
{
struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
- assert(writepack);
+ GIT_ASSERT_ARG(writepack);
return git_indexer_append(writepack->indexer, data, size, stats);
}
{
struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
- assert(writepack);
+ GIT_ASSERT_ARG(writepack);
return git_indexer_commit(writepack->indexer, stats);
}
static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
{
- struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
+ struct pack_writepack *writepack;
- assert(writepack);
+ if (!_writepack)
+ return;
+
+ writepack = (struct pack_writepack *)_writepack;
git_indexer_free(writepack->indexer);
git__free(writepack);
struct pack_backend *backend;
struct pack_writepack *writepack;
- assert(out && _backend);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(_backend);
*out = NULL;
return 0;
}
+static int get_idx_path(
+ git_buf *idx_path,
+ struct pack_backend *backend,
+ struct git_pack_file *p)
+{
+ size_t path_len;
+ int error;
+
+ error = git_path_prettify(idx_path, p->pack_name, backend->pack_folder);
+ if (error < 0)
+ return error;
+ path_len = git_buf_len(idx_path);
+ if (path_len <= strlen(".pack") || git__suffixcmp(git_buf_cstr(idx_path), ".pack") != 0)
+ return git_odb__error_notfound("packfile does not end in .pack", NULL, 0);
+ path_len -= strlen(".pack");
+ error = git_buf_splice(idx_path, path_len, strlen(".pack"), ".idx", strlen(".idx"));
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int pack_backend__writemidx(git_odb_backend *_backend)
+{
+ struct pack_backend *backend;
+ git_midx_writer *w = NULL;
+ struct git_pack_file *p;
+ size_t i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(_backend);
+
+ backend = (struct pack_backend *)_backend;
+
+ error = git_midx_writer_new(&w, backend->pack_folder);
+ if (error < 0)
+ return error;
+
+ git_vector_foreach(&backend->midx_packs, i, p) {
+ git_buf idx_path = GIT_BUF_INIT;
+ error = get_idx_path(&idx_path, backend, p);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_add(w, git_buf_cstr(&idx_path));
+ git_buf_dispose(&idx_path);
+ if (error < 0)
+ goto cleanup;
+ }
+ git_vector_foreach(&backend->packs, i, p) {
+ git_buf idx_path = GIT_BUF_INIT;
+ error = get_idx_path(&idx_path, backend, p);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_add(w, git_buf_cstr(&idx_path));
+ git_buf_dispose(&idx_path);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /*
+ * Invalidate the previous midx before writing the new one.
+ */
+ error = remove_multi_pack_index(backend);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_commit(w);
+ if (error < 0)
+ goto cleanup;
+ error = refresh_multi_pack_index(backend);
+
+cleanup:
+ git_midx_writer_free(w);
+ return error;
+}
+
static void pack_backend__free(git_odb_backend *_backend)
{
struct pack_backend *backend;
+ struct git_pack_file *p;
size_t i;
- assert(_backend);
+ if (!_backend)
+ return;
backend = (struct pack_backend *)_backend;
- for (i = 0; i < backend->packs.length; ++i) {
- struct git_pack_file *p = git_vector_get(&backend->packs, i);
+ git_vector_foreach(&backend->midx_packs, i, p)
+ git_mwindow_put_pack(p);
+ git_vector_foreach(&backend->packs, i, p)
git_mwindow_put_pack(p);
- }
+ git_midx_free(backend->midx);
+ git_vector_free(&backend->midx_packs);
git_vector_free(&backend->packs);
git__free(backend->pack_folder);
git__free(backend);
struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
GIT_ERROR_CHECK_ALLOC(backend);
+ if (git_vector_init(&backend->midx_packs, 0, NULL) < 0) {
+ git__free(backend);
+ return -1;
+ }
if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
+ git_vector_free(&backend->midx_packs);
git__free(backend);
return -1;
}
backend->parent.refresh = &pack_backend__refresh;
backend->parent.foreach = &pack_backend__foreach;
backend->parent.writepack = &pack_backend__writepack;
+ backend->parent.writemidx = &pack_backend__writemidx;
backend->parent.freshen = &pack_backend__freshen;
backend->parent.free = &pack_backend__free;
#include "git2/oid.h"
#include "repository.h"
-#include "global.h"
+#include "threadstate.h"
#include <string.h>
#include <limits.h>
size_t p;
int v;
- assert(out && str);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(str);
if (!length)
return oid_error_invalid("too short");
char *git_oid_tostr_s(const git_oid *oid)
{
- char *str = GIT_GLOBAL->oid_fmt;
+ char *str = GIT_THREADSTATE->oid_fmt;
git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid);
return str;
}
{
git_oid_shorten *os;
- assert((size_t)((int)min_length) == min_length);
+ GIT_ASSERT_ARG_WITH_RETVAL((size_t)((int)min_length) == min_length, NULL);
os = git__calloc(1, sizeof(git_oid_shorten));
if (os == NULL)
#include "git2/oidarray.h"
#include "array.h"
-void git_oidarray_free(git_oidarray *arr)
+void git_oidarray_dispose(git_oidarray *arr)
{
git__free(arr->ids);
}
git_oid_cpy(&arr->ids[(arr->count-1)-i], &tmp);
}
}
+
+#ifndef GIT_DEPRECATE_HARD
+
+void git_oidarray_free(git_oidarray *arr)
+{
+ git_oidarray_dispose(arr);
+}
+
+#endif
#include "iterator.h"
#include "netops.h"
#include "pack.h"
-#include "thread-utils.h"
+#include "thread.h"
#include "tree.h"
#include "util.h"
#include "revwalk.h"
};
#ifdef GIT_THREADS
-
-#define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) do { \
- int result = git_mutex_##op(&(pb)->mtx); \
- assert(!result); \
- GIT_UNUSED(result); \
- } while (0)
-
+# define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) git_mutex_##op(&(pb)->mtx)
#else
-
-#define GIT_PACKBUILDER__MUTEX_OP(pb,mtx,op) GIT_UNUSED(pb)
-
-#endif /* GIT_THREADS */
+# define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) git__noop()
+#endif
#define git_packbuilder__cache_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, lock)
#define git_packbuilder__cache_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, unlock)
unsigned int git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n)
{
- assert(pb);
+ GIT_ASSERT_ARG(pb);
#ifdef GIT_THREADS
pb->nr_threads = n;
#else
GIT_UNUSED(n);
- assert(1 == pb->nr_threads);
+ GIT_ASSERT(pb->nr_threads == 1);
#endif
return pb->nr_threads;
size_t newsize;
int ret;
- assert(pb && oid);
+ GIT_ASSERT_ARG(pb);
+ GIT_ASSERT_ARG(oid);
/* If the object already exists in the hash table, then we don't
* have any work to do */
double current_time = git__timer();
double elapsed = current_time - pb->last_progress_report_time;
- if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ if (elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
pb->last_progress_report_time = current_time;
ret = pb->progress_cb(
}
/* Write header */
- hdr_len = git_packfile__object_header(hdr, data_len, type);
-
- if ((error = write_cb(hdr, hdr_len, cb_data)) < 0 ||
- (error = git_hash_update(&pb->ctx, hdr, hdr_len)) < 0)
+ if ((error = git_packfile__object_header(&hdr_len, hdr, data_len, type)) < 0 ||
+ (error = write_cb(hdr, hdr_len, cb_data)) < 0 ||
+ (error = git_hash_update(&pb->ctx, hdr, hdr_len)) < 0)
goto done;
if (type == GIT_OBJECT_REF_DELTA) {
return 0;
}
-static git_pobject **compute_write_order(git_packbuilder *pb)
+static int compute_write_order(git_pobject ***out, git_packbuilder *pb)
{
size_t i, wo_end, last_untagged;
git_pobject **wo;
+ *out = NULL;
+
+ if (!pb->nr_objects)
+ return 0;
+
if ((wo = git__mallocarray(pb->nr_objects, sizeof(*wo))) == NULL)
- return NULL;
+ return -1;
for (i = 0; i < pb->nr_objects; i++) {
git_pobject *po = pb->object_list + i;
*/
if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0) {
git__free(wo);
- return NULL;
+ return -1;
}
/*
if (wo_end != pb->nr_objects) {
git__free(wo);
git_error_set(GIT_ERROR_INVALID, "invalid write order");
- return NULL;
+ return -1;
}
- return wo;
+ *out = wo;
+ return 0;
}
static int write_pack(git_packbuilder *pb,
struct git_pack_header ph;
git_oid entry_oid;
size_t i = 0;
- int error = 0;
+ int error;
- write_order = compute_write_order(pb);
- if (write_order == NULL)
- return -1;
+ if ((error = compute_write_order(&write_order, pb)) < 0)
+ return error;
if (!git__is_uint32(pb->nr_objects)) {
git_error_set(GIT_ERROR_INVALID, "too many objects");
- return -1;
+ error = -1;
+ goto done;
}
/* Write pack header */
}
}
- git_packbuilder__cache_lock(pb);
+ GIT_ASSERT(git_packbuilder__cache_lock(pb) == 0);
+
if (trg_object->delta_data) {
git__free(trg_object->delta_data);
- assert(pb->delta_cache_size >= trg_object->delta_size);
+ GIT_ASSERT(pb->delta_cache_size >= trg_object->delta_size);
pb->delta_cache_size -= trg_object->delta_size;
trg_object->delta_data = NULL;
}
bool overflow = git__add_sizet_overflow(
&pb->delta_cache_size, pb->delta_cache_size, delta_size);
- git_packbuilder__cache_unlock(pb);
+ GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0);
if (overflow) {
git__free(delta_buf);
GIT_ERROR_CHECK_ALLOC(trg_object->delta_data);
} else {
/* create delta when writing the pack */
- git_packbuilder__cache_unlock(pb);
+ GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0);
git__free(delta_buf);
}
double current_time = git__timer();
double elapsed = current_time - pb->last_progress_report_time;
- if (force || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ if (force || elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
pb->last_progress_report_time = current_time;
ret = pb->progress_cb(
struct unpacked *n = array + idx;
size_t max_depth, j, best_base = SIZE_MAX;
- git_packbuilder__progress_lock(pb);
+ GIT_ASSERT(git_packbuilder__progress_lock(pb) == 0);
if (!*list_size) {
- git_packbuilder__progress_unlock(pb);
+ GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0);
break;
}
po = *list++;
(*list_size)--;
- git_packbuilder__progress_unlock(pb);
+ GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0);
mem_usage -= free_unpacked(n);
n->object = po;
po->z_delta_size = zbuf.size;
git_buf_clear(&zbuf);
- git_packbuilder__cache_lock(pb);
+ GIT_ASSERT(git_packbuilder__cache_lock(pb) == 0);
pb->delta_cache_size -= po->delta_size;
pb->delta_cache_size += po->z_delta_size;
- git_packbuilder__cache_unlock(pb);
+ GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0);
}
/*
; /* TODO */
}
- git_packbuilder__progress_lock(me->pb);
+ GIT_ASSERT_WITH_RETVAL(git_packbuilder__progress_lock(me->pb) == 0, NULL);
me->working = 0;
git_cond_signal(&me->pb->progress_cond);
- git_packbuilder__progress_unlock(me->pb);
+ GIT_ASSERT_WITH_RETVAL(git_packbuilder__progress_unlock(me->pb) == 0, NULL);
if (git_mutex_lock(&me->mutex)) {
git_error_set(GIT_ERROR_THREAD, "unable to lock packfile condition mutex");
int ret, active_threads = 0;
if (!pb->nr_threads)
- pb->nr_threads = git_online_cpus();
+ pb->nr_threads = git__online_cpus();
if (pb->nr_threads <= 1) {
find_deltas(pb, list, &list_size, window, depth);
* 'working' flag from 1 -> 0. This indicates that it is
* ready to receive more work using our work-stealing
* algorithm. */
- git_packbuilder__progress_lock(pb);
+ GIT_ASSERT(git_packbuilder__progress_lock(pb) == 0);
for (;;) {
for (i = 0; !target && i < pb->nr_threads; i++)
if (!p[i].working)
target->list_size = sub_size;
target->remaining = sub_size;
target->working = 1;
- git_packbuilder__progress_unlock(pb);
+ GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0);
if (git_mutex_lock(&target->mutex)) {
git_error_set(GIT_ERROR_THREAD, "unable to lock packfile condition mutex");
int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb)
{
+ int error;
+
+ if ((error = git_buf_sanitize(buf)) < 0)
+ return error;
+
PREPARE_PACK;
- git_buf_sanitize(buf);
+
return write_pack(pb, &write_pack_buf, buf);
}
git_object *obj;
int error;
- assert(pb && id);
+ GIT_ASSERT_ARG(pb);
+ GIT_ASSERT_ARG(id);
if ((error = git_object_lookup(&obj, pb->repo, id, GIT_OBJECT_ANY)) < 0)
return error;
git_oid id;
struct walk_object *obj;
- assert(pb && walk);
+ GIT_ASSERT_ARG(pb);
+ GIT_ASSERT_ARG(walk);
if ((error = mark_edges_uninteresting(pb, walk->user_input)) < 0)
return error;
#include "mwindow.h"
#include "odb.h"
#include "oid.h"
+#include "oidarray.h"
/* Option to bypass checking existence of '.keep' files */
bool git_disable_pack_keep_file_checks = false;
-static int packfile_open(struct git_pack_file *p);
-static off64_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);
+static int packfile_open_locked(struct git_pack_file *p);
+static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t n);
static int packfile_unpack_compressed(
git_rawobj *obj,
struct git_pack_file *p,
if (!e)
return NULL;
- git_atomic_inc(&e->refcount);
+ git_atomic32_inc(&e->refcount);
memcpy(&e->raw, source, sizeof(git_rawobj));
return e;
git_pack_cache_entry *e = (git_pack_cache_entry *)o;
if (e != NULL) {
- assert(e->refcount.val == 0);
git__free(e->raw.data);
git__free(e);
}
return NULL;
if ((entry = git_offmap_get(cache->entries, offset)) != NULL) {
- git_atomic_inc(&entry->refcount);
+ git_atomic32_inc(&entry->refcount);
entry->last_usage = cache->use_ctr++;
}
git_mutex_unlock(&cache->lock);
git_pack_cache_entry *entry;
git_offmap_foreach(cache->entries, offset, entry, {
- if (entry && entry->refcount.val == 0) {
+ if (entry && git_atomic32_get(&entry->refcount) == 0) {
cache->memory_used -= entry->raw.len;
git_offmap_delete(cache->entries, offset);
free_cache_object(entry);
}
}
-static int pack_index_check(const char *path, struct git_pack_file *p)
+/* Run with the packfile lock held */
+static int pack_index_check_locked(const char *path, struct git_pack_file *p)
{
struct git_pack_idx_header *hdr;
uint32_t version, nr, i, *index;
return 0;
}
-static int pack_index_open(struct git_pack_file *p)
+/* Run with the packfile lock held */
+static int pack_index_open_locked(struct git_pack_file *p)
{
int error = 0;
size_t name_len;
- git_buf idx_name;
+ git_buf idx_name = GIT_BUF_INIT;
if (p->index_version > -1)
- return 0;
+ goto cleanup;
+ /* checked by git_pack_file alloc */
name_len = strlen(p->pack_name);
- assert(name_len > strlen(".pack")); /* checked by git_pack_file alloc */
+ GIT_ASSERT(name_len > strlen(".pack"));
- if (git_buf_init(&idx_name, name_len) < 0)
- return -1;
+ if ((error = git_buf_init(&idx_name, name_len)) < 0)
+ goto cleanup;
git_buf_put(&idx_name, p->pack_name, name_len - strlen(".pack"));
git_buf_puts(&idx_name, ".idx");
if (git_buf_oom(&idx_name)) {
- git_buf_dispose(&idx_name);
- return -1;
- }
-
- if ((error = git_mutex_lock(&p->lock)) < 0) {
- git_buf_dispose(&idx_name);
- return error;
+ error = -1;
+ goto cleanup;
}
if (p->index_version == -1)
- error = pack_index_check(idx_name.ptr, p);
+ error = pack_index_check_locked(idx_name.ptr, p);
+cleanup:
git_buf_dispose(&idx_name);
- git_mutex_unlock(&p->lock);
-
return error;
}
off64_t offset,
unsigned int *left)
{
- if (p->mwf.fd == -1 && packfile_open(p) < 0)
+ unsigned char *pack_data = NULL;
+
+ if (git_mutex_lock(&p->lock) < 0) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock packfile");
+ return NULL;
+ }
+ if (git_mutex_lock(&p->mwf.lock) < 0) {
+ git_mutex_unlock(&p->lock);
+ git_error_set(GIT_ERROR_THREAD, "unable to lock packfile");
return NULL;
+ }
+
+ if (p->mwf.fd == -1 && packfile_open_locked(p) < 0)
+ goto cleanup;
/* Since packfiles end in a hash of their content and it's
* pointless to ask for an offset into the middle of that
* around.
*/
if (offset > (p->mwf.size - 20))
- return NULL;
+ goto cleanup;
if (offset < 0)
- return NULL;
+ goto cleanup;
+
+ pack_data = git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
- return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
+cleanup:
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+ return pack_data;
}
/*
* - each byte afterwards: low seven bits are size continuation,
* with the high bit being "size continues"
*/
-size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_object_t type)
+int git_packfile__object_header(size_t *out, unsigned char *hdr, size_t size, git_object_t type)
{
unsigned char *hdr_base;
unsigned char c;
- assert(type >= GIT_OBJECT_COMMIT && type <= GIT_OBJECT_REF_DELTA);
+ GIT_ASSERT_ARG(type >= GIT_OBJECT_COMMIT && type <= GIT_OBJECT_REF_DELTA);
/* TODO: add support for chunked objects; see git.git 6c0d19b1 */
}
*hdr++ = c;
- return (hdr - hdr_base);
+ *out = (hdr - hdr_base);
+ return 0;
}
int git_packfile_unpack_header(
size_t *size_p,
git_object_t *type_p,
- git_mwindow_file *mwf,
+ struct git_pack_file *p,
git_mwindow **w_curs,
off64_t *curpos)
{
unsigned char *base;
unsigned int left;
unsigned long used;
- int ret;
+ int error;
+
+ if ((error = git_mutex_lock(&p->lock)) < 0)
+ return error;
+ if ((error = git_mutex_lock(&p->mwf.lock)) < 0) {
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if (p->mwf.fd == -1 && (error = packfile_open_locked(p)) < 0) {
+ git_mutex_unlock(&p->lock);
+ git_mutex_unlock(&p->mwf.lock);
+ return error;
+ }
/* pack_window_open() assures us we have [base, base + 20) available
* as a range that we can look at at. (Its actually the hash
* the maximum deflated object size is 2^137, which is just
* insane, so we know won't exceed what we have been given.
*/
-/* base = pack_window_open(p, w_curs, *curpos, &left); */
- base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left);
+ base = git_mwindow_open(&p->mwf, w_curs, *curpos, 20, &left);
+ git_mutex_unlock(&p->lock);
+ git_mutex_unlock(&p->mwf.lock);
if (base == NULL)
return GIT_EBUFS;
- ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
+ error = packfile_unpack_header1(&used, size_p, type_p, base, left);
git_mwindow_close(w_curs);
- if (ret == GIT_EBUFS)
- return ret;
- else if (ret < 0)
+ if (error == GIT_EBUFS)
+ return error;
+ else if (error < 0)
return packfile_error("header length is zero");
*curpos += used;
off64_t base_offset;
int error;
- error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
+ error = git_mutex_lock(&p->lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ return error;
+ }
+ error = git_mutex_lock(&p->mwf.lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if (p->mwf.fd == -1 && (error = packfile_open_locked(p)) < 0) {
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+
+ error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
if (error < 0)
return error;
while (type == GIT_OBJECT_OFS_DELTA || type == GIT_OBJECT_REF_DELTA) {
curpos = base_offset;
- error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
+ error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
if (error < 0)
return error;
if (type != GIT_OBJECT_OFS_DELTA && type != GIT_OBJECT_REF_DELTA)
elem->base_key = obj_offset;
- error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
-
+ error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
if (error < 0)
goto on_error;
size_t stack_size = 0, elem_pos, alloclen;
git_object_t base_type;
+ error = git_mutex_lock(&p->lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ return error;
+ }
+ error = git_mutex_lock(&p->mwf.lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if (p->mwf.fd == -1)
+ error = packfile_open_locked(p);
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+ if (error < 0)
+ return error;
+
/*
* TODO: optionally check the CRC on the packfile
*/
GIT_ERROR_CHECK_ALLOC(obj->data);
memcpy(obj->data, data, obj->len + 1);
- git_atomic_dec(&cached->refcount);
+ git_atomic32_dec(&cached->refcount);
goto cleanup;
}
}
if (cached) {
- git_atomic_dec(&cached->refcount);
+ git_atomic32_dec(&cached->refcount);
cached = NULL;
}
if (error < 0) {
git__free(obj->data);
if (cached)
- git_atomic_dec(&cached->refcount);
+ git_atomic32_dec(&cached->refcount);
}
if (elem)
do {
size_t bytes = buffer_len - total;
- unsigned int window_len;
+ unsigned int window_len, consumed;
unsigned char *in;
if ((in = pack_window_open(p, mwindow, *position, &window_len)) == NULL) {
git_mwindow_close(mwindow);
- if (!bytes)
- break;
+ consumed = window_len - (unsigned int)zstream.in_len;
- *position += window_len - zstream.in_len;
+ if (!bytes && !consumed) {
+ git_error_set(GIT_ERROR_ZLIB, "error inflating zlib stream");
+ error = -1;
+ goto out;
+ }
+
+ *position += consumed;
total += bytes;
} while (!git_zstream_eos(&zstream));
off64_t base_offset;
git_oid unused;
- assert(delta_base_out);
+ GIT_ASSERT_ARG(delta_base_out);
base_info = pack_window_open(p, w_curs, *curpos, &left);
/* Assumption: the only reason this would fail is because the file is too small */
*
***********************************************************/
-void git_packfile_close(struct git_pack_file *p, bool unlink_packfile)
+void git_packfile_free(struct git_pack_file *p, bool unlink_packfile)
{
+ bool locked = true;
+
+ if (!p)
+ return;
+
+ cache_free(&p->bases);
+
+ if (git_mutex_lock(&p->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile");
+ locked = false;
+ }
if (p->mwf.fd >= 0) {
- git_mwindow_free_all_locked(&p->mwf);
+ git_mwindow_free_all(&p->mwf);
p_close(p->mwf.fd);
p->mwf.fd = -1;
}
+ if (locked)
+ git_mutex_unlock(&p->lock);
if (unlink_packfile)
p_unlink(p->pack_name);
-}
-
-void git_packfile_free(struct git_pack_file *p)
-{
- if (!p)
- return;
-
- cache_free(&p->bases);
-
- git_packfile_close(p, false);
pack_index_free(p);
git__free(p->bad_object_sha1);
- git_mutex_free(&p->lock);
git_mutex_free(&p->bases.lock);
+ git_mutex_free(&p->mwf.lock);
+ git_mutex_free(&p->lock);
git__free(p);
}
-static int packfile_open(struct git_pack_file *p)
+/* Run with the packfile and mwf locks held */
+static int packfile_open_locked(struct git_pack_file *p)
{
struct stat st;
struct git_pack_header hdr;
git_oid sha1;
unsigned char *idx_sha1;
- if (p->index_version == -1 && pack_index_open(p) < 0)
+ if (pack_index_open_locked(p) < 0)
return git_odb__error_notfound("failed to open packfile", NULL, 0);
- /* if mwf opened by another thread, return now */
- if (git_mutex_lock(&p->lock) < 0)
- return packfile_error("failed to get lock for open");
-
- if (p->mwf.fd >= 0) {
- git_mutex_unlock(&p->lock);
+ if (p->mwf.fd >= 0)
return 0;
- }
/* TODO: open with noatime */
p->mwf.fd = git_futils_open_ro(p->pack_name);
if (p->mwf.fd < 0)
goto cleanup;
- if (p_fstat(p->mwf.fd, &st) < 0 ||
- git_mwindow_file_register(&p->mwf) < 0)
+ if (p_fstat(p->mwf.fd, &st) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not stat packfile");
goto cleanup;
+ }
/* If we created the struct before we had the pack we lack size. */
if (!p->mwf.size) {
/* Verify the pack matches its index. */
if (p->num_objects != ntohl(hdr.hdr_entries) ||
- p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1 ||
- p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < 0)
+ p_pread(p->mwf.fd, sha1.id, GIT_OID_RAWSZ, p->mwf.size - GIT_OID_RAWSZ) < 0)
goto cleanup;
idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) != 0)
goto cleanup;
- git_mutex_unlock(&p->lock);
+ if (git_mwindow_file_register(&p->mwf) < 0)
+ goto cleanup;
+
return 0;
cleanup:
p_close(p->mwf.fd);
p->mwf.fd = -1;
- git_mutex_unlock(&p->lock);
-
return -1;
}
p->mtime = (git_time_t)st.st_mtime;
p->index_version = -1;
- if (git_mutex_init(&p->lock)) {
+ if (git_mutex_init(&p->lock) < 0) {
git_error_set(GIT_ERROR_OS, "failed to initialize packfile mutex");
git__free(p);
return -1;
}
+ if (git_mutex_init(&p->mwf.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to initialize packfile window mutex");
+ git_mutex_free(&p->lock);
+ git__free(p);
+ return -1;
+ }
+
if (cache_init(&p->bases) < 0) {
+ git_mutex_free(&p->mwf.lock);
+ git_mutex_free(&p->lock);
git__free(p);
return -1;
}
*
***********************************************************/
-static off64_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
+static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t n)
{
- const unsigned char *index = p->index_map.data;
- const unsigned char *end = index + p->index_map.len;
+ const unsigned char *index, *end;
+ uint32_t off32;
+
+ index = p->index_map.data;
+ end = index + p->index_map.len;
index += 4 * 256;
- if (p->index_version == 1) {
+ if (p->index_version == 1)
return ntohl(*((uint32_t *)(index + 24 * n)));
- } else {
- uint32_t off;
- index += 8 + p->num_objects * (20 + 4);
- off = ntohl(*((uint32_t *)(index + 4 * n)));
- if (!(off & 0x80000000))
- return off;
- index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
-
- /* Make sure we're not being sent out of bounds */
- if (index >= end - 8)
- return -1;
- return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
- ntohl(*((uint32_t *)(index + 4)));
- }
+ index += 8 + p->num_objects * (20 + 4);
+ off32 = ntohl(*((uint32_t *)(index + 4 * n)));
+ if (!(off32 & 0x80000000))
+ return off32;
+ index += p->num_objects * 4 + (off32 & 0x7fffffff) * 8;
+
+ /* Make sure we're not being sent out of bounds */
+ if (index >= end - 8)
+ return -1;
+
+ return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
+ ntohl(*((uint32_t *)(index + 4)));
}
static int git__memcmp4(const void *a, const void *b) {
git_odb_foreach_cb cb,
void *data)
{
- const unsigned char *index = p->index_map.data, *current;
+ const unsigned char *index, *current;
uint32_t i;
int error = 0;
+ git_array_oid_t oids = GIT_ARRAY_INIT;
+ git_oid *oid;
- if (index == NULL) {
- if ((error = pack_index_open(p)) < 0)
- return error;
+ if (git_mutex_lock(&p->lock) < 0)
+ return packfile_error("failed to get lock for git_pack_foreach_entry");
- assert(p->index_map.data);
+ if ((error = pack_index_open_locked(p)) < 0) {
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
- index = p->index_map.data;
+ if (!p->index_map.data) {
+ git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
+ git_mutex_unlock(&p->lock);
+ return -1;
}
- if (p->index_version > 1) {
+ index = p->index_map.data;
+
+ if (p->index_version > 1)
index += 8;
- }
index += 4 * 256;
if (p->oids == NULL) {
git_vector offsets, oids;
- if ((error = git_vector_init(&oids, p->num_objects, NULL)))
+ if ((error = git_vector_init(&oids, p->num_objects, NULL))) {
+ git_mutex_unlock(&p->lock);
return error;
+ }
- if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4)))
+ if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4))) {
+ git_mutex_unlock(&p->lock);
return error;
+ }
if (p->index_version > 1) {
const unsigned char *off = index + 24 * p->num_objects;
p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids);
}
- for (i = 0; i < p->num_objects; i++)
- if ((error = cb(p->oids[i], data)) != 0)
- return git_error_set_after_callback(error);
+ /* We need to copy the OIDs to another array before we relinquish the lock to avoid races. */
+ git_array_init_to_size(oids, p->num_objects);
+ if (!oids.ptr) {
+ git_mutex_unlock(&p->lock);
+ git_array_clear(oids);
+ GIT_ERROR_CHECK_ARRAY(oids);
+ }
+ for (i = 0; i < p->num_objects; i++) {
+ oid = git_array_alloc(oids);
+ if (!oid) {
+ git_mutex_unlock(&p->lock);
+ git_array_clear(oids);
+ GIT_ERROR_CHECK_ALLOC(oid);
+ }
+ git_oid_cpy(oid, p->oids[i]);
+ }
+
+ git_mutex_unlock(&p->lock);
+ git_array_foreach(oids, i, oid) {
+ if ((error = cb(oid, data)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ git_array_clear(oids);
+ return error;
+}
+
+int git_pack_foreach_entry_offset(
+ struct git_pack_file *p,
+ git_pack_foreach_entry_offset_cb cb,
+ void *data)
+{
+ const unsigned char *index;
+ off64_t current_offset;
+ const git_oid *current_oid;
+ uint32_t i;
+ int error = 0;
+
+ if (git_mutex_lock(&p->lock) < 0)
+ return packfile_error("failed to get lock for git_pack_foreach_entry_offset");
+
+ index = p->index_map.data;
+ if (index == NULL) {
+ if ((error = pack_index_open_locked(p)) < 0)
+ goto cleanup;
+
+ if (!p->index_map.data) {
+ git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
+ goto cleanup;
+ }
+
+ index = p->index_map.data;
+ }
+
+ if (p->index_version > 1)
+ index += 8;
+
+ index += 4 * 256;
+
+ /* all offsets should have been validated by pack_index_check_locked */
+ if (p->index_version > 1) {
+ const unsigned char *offsets = index + 24 * p->num_objects;
+ const unsigned char *large_offset_ptr;
+ const unsigned char *large_offsets = index + 28 * p->num_objects;
+ const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - 20;
+ for (i = 0; i < p->num_objects; i++) {
+ current_offset = ntohl(*(const uint32_t *)(offsets + 4 * i));
+ if (current_offset & 0x80000000) {
+ large_offset_ptr = large_offsets + (current_offset & 0x7fffffff) * 8;
+ if (large_offset_ptr >= large_offsets_end) {
+ error = packfile_error("invalid large offset");
+ goto cleanup;
+ }
+ current_offset = (((off64_t)ntohl(*((uint32_t *)(large_offset_ptr + 0)))) << 32) |
+ ntohl(*((uint32_t *)(large_offset_ptr + 4)));
+ }
+ current_oid = (const git_oid *)(index + 20 * i);
+ if ((error = cb(current_oid, current_offset, data)) != 0) {
+ error = git_error_set_after_callback(error);
+ goto cleanup;
+ }
+ }
+ } else {
+ for (i = 0; i < p->num_objects; i++) {
+ current_offset = ntohl(*(const uint32_t *)(index + 24 * i));
+ current_oid = (const git_oid *)(index + 24 * i + 4);
+ if ((error = cb(current_oid, current_offset, data)) != 0) {
+ error = git_error_set_after_callback(error);
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ git_mutex_unlock(&p->lock);
return error;
}
int pos, found = 0;
off64_t offset;
const unsigned char *current = 0;
+ int error = 0;
*offset_out = 0;
- if (p->index_version == -1) {
- int error;
+ if (git_mutex_lock(&p->lock) < 0)
+ return packfile_error("failed to get lock for pack_entry_find_offset");
+
+ if ((error = pack_index_open_locked(p)) < 0)
+ goto cleanup;
- if ((error = pack_index_open(p)) < 0)
- return error;
- assert(p->index_map.data);
+ if (!p->index_map.data) {
+ git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
+ goto cleanup;
}
index = p->index_map.data;
}
}
- if (!found)
- return git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
- if (found > 1)
- return git_odb__error_ambiguous("found multiple offsets for pack entry");
+ if (!found) {
+ error = git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
+ goto cleanup;
+ }
+ if (found > 1) {
+ error = git_odb__error_ambiguous("found multiple offsets for pack entry");
+ goto cleanup;
+ }
- if ((offset = nth_packed_object_offset(p, pos)) < 0) {
+ if ((offset = nth_packed_object_offset_locked(p, pos)) < 0) {
git_error_set(GIT_ERROR_ODB, "packfile index is corrupt");
- return -1;
+ error = -1;
+ goto cleanup;
}
*offset_out = offset;
}
#endif
- return 0;
+cleanup:
+ git_mutex_unlock(&p->lock);
+ return error;
}
int git_pack_entry_find(
git_oid found_oid;
int error;
- assert(p);
+ GIT_ASSERT_ARG(p);
if (len == GIT_OID_HEXSZ && p->num_bad_objects) {
unsigned i;
if (error < 0)
return error;
+ error = git_mutex_lock(&p->lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ return error;
+ }
+ error = git_mutex_lock(&p->mwf.lock);
+ if (error < 0) {
+ git_mutex_unlock(&p->lock);
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ return error;
+ }
+
/* we found a unique entry in the index;
* make sure the packfile backing the index
* still exists on disk */
- if (p->mwf.fd == -1 && (error = packfile_open(p)) < 0)
+ if (p->mwf.fd == -1)
+ error = packfile_open_locked(p);
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+ if (error < 0)
return error;
e->offset = offset;
#include "oidmap.h"
#include "zstream.h"
+/**
+ * Function type for callbacks from git_pack_foreach_entry_offset.
+ */
+typedef int git_pack_foreach_entry_offset_cb(
+ const git_oid *id,
+ off64_t offset,
+ void *payload);
+
#define GIT_PACK_FILE_MODE 0444
#define PACK_SIGNATURE 0x5041434b /* "PACK" */
typedef struct git_pack_cache_entry {
size_t last_usage; /* enough? */
- git_atomic refcount;
+ git_atomic32 refcount;
git_rawobj raw;
} git_pack_cache_entry;
struct git_pack_file {
git_mwindow_file mwf;
git_map index_map;
- git_mutex lock; /* protect updates to mwf and index_map */
- git_atomic refcount;
+ git_mutex lock; /* protect updates to index_map */
+ git_atomic32 refcount;
uint32_t num_objects;
uint32_t num_bad_objects;
git_mwindow *mw;
} git_packfile_stream;
-size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_object_t type);
+int git_packfile__object_header(size_t *out, unsigned char *hdr, size_t size, git_object_t type);
int git_packfile__name(char **out, const char *path);
int git_packfile_unpack_header(
size_t *size_p,
git_object_t *type_p,
- git_mwindow_file *mwf,
+ struct git_pack_file *p,
git_mwindow **w_curs,
off64_t *curpos);
git_object_t type,
off64_t delta_obj_offset);
-void git_packfile_close(struct git_pack_file *p, bool unlink_packfile);
-void git_packfile_free(struct git_pack_file *p);
+void git_packfile_free(struct git_pack_file *p, bool unlink_packfile);
int git_packfile_alloc(struct git_pack_file **pack_out, const char *path);
int git_pack_entry_find(
struct git_pack_file *p,
git_odb_foreach_cb cb,
void *data);
+/**
+ * Similar to git_pack_foreach_entry, but:
+ * - It also provides the offset of the object within the
+ * packfile.
+ * - It does not sort the objects in any order.
+ * - It retains the lock while invoking the callback.
+ */
+int git_pack_foreach_entry_offset(
+ struct git_pack_file *p,
+ git_pack_foreach_entry_offset_cb cb,
+ void *data);
#endif
{
size_t out;
- assert(patch);
+ GIT_ASSERT_ARG(patch);
out = patch->content_size;
const git_diff_delta *git_patch_get_delta(const git_patch *patch)
{
- assert(patch);
+ GIT_ASSERT_ARG_WITH_RETVAL(patch, NULL);
return patch->delta;
}
size_t git_patch_num_hunks(const git_patch *patch)
{
- assert(patch);
+ GIT_ASSERT_ARG(patch);
return git_array_size(patch->hunks);
}
size_t hunk_idx)
{
git_patch_hunk *hunk;
- assert(patch);
+ GIT_ASSERT_ARG(patch);
hunk = git_array_get(patch->hunks, hunk_idx);
int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
{
git_patch_hunk *hunk;
- assert(patch);
+ GIT_ASSERT_ARG(patch);
if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
return patch_error_outofrange("hunk");
git_patch_hunk *hunk;
git_diff_line *line;
- assert(patch);
+ GIT_ASSERT_ARG(patch);
if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
if (out) *out = NULL;
return 0;
}
+git_repository *git_patch_owner(const git_patch *patch)
+{
+ return patch->repo;
+}
+
int git_patch_from_diff(git_patch **out, git_diff *diff, size_t idx)
{
- assert(out && diff && diff->patch_fn);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+ GIT_ASSERT_ARG(diff->patch_fn);
return diff->patch_fn(out, diff, idx);
}
patch_generated_with_delta *pd;
git_xdiff_output xo;
- assert(out);
+ GIT_ASSERT_ARG(out);
*out = NULL;
if ((error = patch_generated_with_delta_alloc(
GIT_UNUSED(hunk_);
hunk = git_array_last(patch->base.hunks);
- assert(hunk); /* programmer error if no hunk is available */
+ GIT_ASSERT(hunk); /* programmer error if no hunk is available */
line = git_array_alloc(patch->base.lines);
GIT_ERROR_CHECK_ALLOC(line);
size_t start, used;
int error = 0;
- assert(out && ctx);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ctx);
*out = NULL;
return 0;
}
-const char *git_path_topdir(const char *path)
-{
- size_t len;
- ssize_t i;
-
- assert(path);
- len = strlen(path);
-
- if (!len || path[len - 1] != '/')
- return NULL;
-
- for (i = (ssize_t)len - 2; i >= 0; --i)
- if (path[i] == '/')
- break;
-
- return &path[i + 1];
-}
-
int git_path_root(const char *path)
{
int offset = 0, prefix_len;
static void path_trim_slashes(git_buf *path)
{
int ceiling = git_path_root(path->ptr) + 1;
- assert(ceiling >= 0);
+
+ if (ceiling < 0)
+ return;
while (path->size > (size_t)ceiling) {
if (path->ptr[path->size-1] != '/')
{
ssize_t root;
- assert(path && path_out);
+ GIT_ASSERT_ARG(path_out);
+ GIT_ASSERT_ARG(path);
root = (ssize_t)git_path_root(path);
{
char buf[GIT_PATH_MAX];
- assert(path && path_out);
+ GIT_ASSERT_ARG(path_out);
+ GIT_ASSERT_ARG(path);
/* construct path if needed */
if (base != NULL && git_path_root(path) < 0) {
return git_buf_oom(path) ? -1 : 0;
}
-void git_path_string_to_dir(char* path, size_t size)
+void git_path_string_to_dir(char *path, size_t size)
{
size_t end = strlen(path);
int git__percent_decode(git_buf *decoded_out, const char *input)
{
int len, hi, lo, i;
- assert(decoded_out && input);
+
+ GIT_ASSERT_ARG(decoded_out);
+ GIT_ASSERT_ARG(input);
len = (int)strlen(input);
git_buf_clear(decoded_out);
{
int offset;
- assert(local_path_out && file_url);
+ GIT_ASSERT_ARG(local_path_out);
+ GIT_ASSERT_ARG(file_url);
if ((offset = local_file_url_prefixlen(file_url)) < 0 ||
file_url[offset] == '\0' || file_url[offset] == '/')
ssize_t stop = 0, scan;
char oldc = '\0';
- assert(path && cb);
+ GIT_ASSERT_ARG(path);
+ GIT_ASSERT_ARG(cb);
if (ceiling != NULL) {
if (git__prefixcmp(path->ptr, ceiling) == 0)
bool git_path_exists(const char *path)
{
- assert(path);
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
return p_access(path, F_OK) == 0;
}
{
struct stat st;
- assert(path);
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
if (p_stat(path, &st) < 0)
return false;
{
struct stat st;
- assert(path);
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
if (p_lstat(path, &st) < 0)
return false;
return _check_dir_contents(base, file, &git_path_isfile);
}
-int git_path_find_dir(git_buf *dir, const char *path, const char *base)
+int git_path_find_dir(git_buf *dir)
{
- int error = git_path_join_unrooted(dir, path, base, NULL);
+ int error = 0;
+ char buf[GIT_PATH_MAX];
- if (!error) {
- char buf[GIT_PATH_MAX];
- if (p_realpath(dir->ptr, buf) != NULL)
- error = git_buf_sets(dir, buf);
- }
+ if (p_realpath(dir->ptr, buf) != NULL)
+ error = git_buf_sets(dir, buf);
/* call dirname if this is not a directory */
if (!error) /* && git_path_isdir(dir->ptr) == false) */
if (is_win7_or_later < 0)
is_win7_or_later = git_has_win32_version(6, 1, 0);
- assert(diriter && path);
+ GIT_ASSERT_ARG(diriter);
+ GIT_ASSERT_ARG(path);
memset(diriter, 0, sizeof(git_path_diriter));
diriter->handle = INVALID_HANDLE_VALUE;
size_t *out_len,
git_path_diriter *diriter)
{
- assert(out && out_len && diriter);
-
- assert(diriter->path_utf8.size > diriter->parent_utf8_len);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(out_len);
+ GIT_ASSERT_ARG(diriter);
+ GIT_ASSERT(diriter->path_utf8.size > diriter->parent_utf8_len);
*out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1];
*out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1;
size_t *out_len,
git_path_diriter *diriter)
{
- assert(out && out_len && diriter);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(out_len);
+ GIT_ASSERT_ARG(diriter);
*out = diriter->path_utf8.ptr;
*out_len = diriter->path_utf8.size;
int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
{
- assert(out && diriter);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diriter);
return git_win32__file_attribute_to_stat(out,
(WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current,
const char *path,
unsigned int flags)
{
- assert(diriter && path);
+ GIT_ASSERT_ARG(diriter);
+ GIT_ASSERT_ARG(path);
memset(diriter, 0, sizeof(git_path_diriter));
bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
int error = 0;
- assert(diriter);
+ GIT_ASSERT_ARG(diriter);
errno = 0;
size_t *out_len,
git_path_diriter *diriter)
{
- assert(out && out_len && diriter);
-
- assert(diriter->path.size > diriter->parent_len);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(out_len);
+ GIT_ASSERT_ARG(diriter);
+ GIT_ASSERT(diriter->path.size > diriter->parent_len);
*out = &diriter->path.ptr[diriter->parent_len+1];
*out_len = diriter->path.size - diriter->parent_len - 1;
size_t *out_len,
git_path_diriter *diriter)
{
- assert(out && out_len && diriter);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(out_len);
+ GIT_ASSERT_ARG(diriter);
*out = diriter->path.ptr;
*out_len = diriter->path.size;
int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
{
- assert(out && diriter);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diriter);
return git_path_lstat(diriter->path.ptr, out);
}
char *dup;
int error;
- assert(contents && path);
+ GIT_ASSERT_ARG(contents);
+ GIT_ASSERT_ARG(path);
if ((error = git_path_diriter_init(&iter, path, flags)) < 0)
return error;
if ((error = git_path_diriter_fullpath(&name, &name_len, &iter)) < 0)
break;
- assert(name_len > prefix_len);
+ GIT_ASSERT(name_len > prefix_len);
dup = git__strndup(name + prefix_len, name_len - prefix_len);
GIT_ERROR_CHECK_ALLOC(dup);
static int32_t next_hfs_char(const char **in, size_t *len)
{
while (*len) {
- int32_t codepoint;
- int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint);
+ uint32_t codepoint;
+ int cp_len = git_utf8_iterate(&codepoint, *in, *len);
if (cp_len < 0)
return -1;
* the ASCII range, which is perfectly fine, because the
* git folder name can only be composed of ascii characters
*/
- return git__tolower(codepoint);
+ return git__tolower((int)codepoint);
}
return 0; /* NULL byte -- end of string */
}
return flags;
}
-bool git_path_isvalid(
+bool git_path_validate(
git_repository *repo,
const char *path,
uint16_t mode,
return verify_component(repo, start, (c - start), mode, flags);
}
+#ifdef GIT_WIN32
+GIT_INLINE(bool) should_validate_longpaths(git_repository *repo)
+{
+ int longpaths = 0;
+
+ if (repo &&
+ git_repository__configmap_lookup(&longpaths, repo, GIT_CONFIGMAP_LONGPATHS) < 0)
+ longpaths = 0;
+
+ return (longpaths == 0);
+}
+
+#else
+
+GIT_INLINE(bool) should_validate_longpaths(git_repository *repo)
+{
+ GIT_UNUSED(repo);
+
+ return false;
+}
+#endif
+
+int git_path_validate_workdir(git_repository *repo, const char *path)
+{
+ if (should_validate_longpaths(repo))
+ return git_path_validate_filesystem(path, strlen(path));
+
+ return 0;
+}
+
+int git_path_validate_workdir_with_len(
+ git_repository *repo,
+ const char *path,
+ size_t path_len)
+{
+ if (should_validate_longpaths(repo))
+ return git_path_validate_filesystem(path, path_len);
+
+ return 0;
+}
+
+int git_path_validate_workdir_buf(git_repository *repo, git_buf *path)
+{
+ return git_path_validate_workdir_with_len(repo, path->ptr, path->size);
+}
+
int git_path_normalize_slashes(git_buf *out, const char *path)
{
int error;
*/
extern size_t git_path_basename_offset(git_buf *buffer);
-extern const char *git_path_topdir(const char *path);
-
/**
* Find offset to root of path if path has one.
*
/**
* Ensure string has a trailing '/' if there is space for it.
*/
-extern void git_path_string_to_dir(char* path, size_t size);
+extern void git_path_string_to_dir(char *path, size_t size);
/**
* Taken from git.git; returns nonzero if the given path is "." or "..".
* appends the trailing '/'. If the path does not exist, it is
* treated like a regular filename.
*/
-extern int git_path_find_dir(git_buf *dir, const char *path, const char *base);
+extern int git_path_find_dir(git_buf *dir);
/**
* Resolve relative references within a path.
#define GIT_PATH_REJECT_INDEX_DEFAULTS \
GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT
-/*
- * Determine whether a path is a valid git path or not - this must not contain
+/**
+ * Validate a "bare" git path. This ensures that the given path is legal
+ * to place in the index or a tree. This should be checked by mechanisms
+ * like `git_index_add` and `git_treebuilder_insert` when taking user
+ * data, and by `git_checkout` before constructing on-disk paths.
+ *
+ * This will ensure that a git path does not contain any "unsafe" components,
* a '.' or '..' component, or a component that is ".git" (in any case).
*
+ * (Note: if you take or construct an on-disk path -- a workdir path,
+ * a path to a git repository or a reference name that could be a loose
+ * ref -- you should _also_ validate that with `git_path_validate_workdir`.)
+ *
* `repo` is optional. If specified, it will be used to determine the short
* path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified),
* in addition to the default of "git~1".
*/
-extern bool git_path_isvalid(
+extern bool git_path_validate(
git_repository *repo,
const char *path,
uint16_t mode,
unsigned int flags);
+/**
+ * Validate an on-disk path, taking into account that it will have a
+ * suffix appended (eg, `.lock`).
+ */
+GIT_INLINE(int) git_path_validate_filesystem_with_suffix(
+ const char *path,
+ size_t path_len,
+ size_t suffix_len)
+{
+#ifdef GIT_WIN32
+ size_t path_chars, total_chars;
+
+ path_chars = git_utf8_char_length(path, path_len);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&total_chars, path_chars, suffix_len) ||
+ total_chars > MAX_PATH) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%s'", path);
+ return -1;
+ }
+ return 0;
+#else
+ GIT_UNUSED(path);
+ GIT_UNUSED(path_len);
+ GIT_UNUSED(suffix_len);
+ return 0;
+#endif
+}
+
+/**
+ * Validate an path on the filesystem. This ensures that the given
+ * path is valid for the operating system/platform; for example, this
+ * will ensure that the given absolute path is smaller than MAX_PATH on
+ * Windows.
+ *
+ * For paths within the working directory, you should use ensure that
+ * `core.longpaths` is obeyed. Use `git_path_validate_workdir`.
+ */
+GIT_INLINE(int) git_path_validate_filesystem(
+ const char *path,
+ size_t path_len)
+{
+ return git_path_validate_filesystem_with_suffix(path, path_len, 0);
+}
+
+/**
+ * Validate a path relative to the repo's worktree. This ensures that
+ * the given working tree path is valid for the operating system/platform.
+ * This will ensure that an absolute path is smaller than MAX_PATH on
+ * Windows, while keeping `core.longpaths` configuration settings in mind.
+ *
+ * This should be checked by mechamisms like `git_checkout` after
+ * contructing on-disk paths and before trying to write them.
+ *
+ * If the repository is null, no repository configuration is applied.
+ */
+extern int git_path_validate_workdir(
+ git_repository *repo,
+ const char *path);
+extern int git_path_validate_workdir_with_len(
+ git_repository *repo,
+ const char *path,
+ size_t path_len);
+extern int git_path_validate_workdir_buf(
+ git_repository *repo,
+ git_buf *buf);
+
/**
* Convert any backslashes into slashes
*/
#include "git2/pathspec.h"
#include "git2/diff.h"
-#include "buf_text.h"
#include "attr_file.h"
#include "iterator.h"
#include "repository.h"
const char *scan;
if (!pathspec || !pathspec->count ||
- git_buf_text_common_prefix(&prefix, pathspec) < 0)
+ git_buf_common_prefix(&prefix, pathspec->strings, pathspec->count) < 0)
return NULL;
/* diff prefix will only be leading non-wildcards */
return NULL;
}
- git_buf_text_unescape(&prefix);
+ git_buf_unescape(&prefix);
return git_buf_detach(&prefix);
}
bool no_fnmatch = (flags & GIT_PATHSPEC_NO_GLOB) != 0;
bool casefold = (flags & GIT_PATHSPEC_IGNORE_CASE) != 0;
- assert(ps && path);
+ GIT_ASSERT_ARG(ps);
+ GIT_ASSERT_ARG(path);
return (0 != git_pathspec__match(
&ps->pathspec, path, no_fnmatch, casefold, NULL, NULL));
git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
int error = 0;
- assert(repo);
+ GIT_ASSERT_ARG(repo);
iter_opts.flags = pathspec_match_iter_flags(flags);
git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
int error = 0;
- assert(index);
+ GIT_ASSERT_ARG(index);
iter_opts.flags = pathspec_match_iter_flags(flags);
git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
int error = 0;
- assert(tree);
+ GIT_ASSERT_ARG(tree);
iter_opts.flags = pathspec_match_iter_flags(flags);
const git_diff_delta *delta, **match;
git_bitvec used_patterns;
- assert(diff);
+ GIT_ASSERT_ARG(diff);
if (git_bitvec_init(&used_patterns, patterns->length) < 0)
return -1;
int git_pool_init(git_pool *pool, size_t item_size)
{
- assert(pool);
- assert(item_size >= 1);
+ GIT_ASSERT_ARG(pool);
+ GIT_ASSERT_ARG(item_size >= 1);
memset(pool, 0, sizeof(git_pool));
pool->item_size = item_size;
int git_pool_init(git_pool *pool, size_t item_size)
{
- assert(pool);
- assert(item_size >= 1);
+ GIT_ASSERT_ARG(pool);
+ GIT_ASSERT_ARG(item_size >= 1);
memset(pool, 0, sizeof(git_pool));
pool->item_size = item_size;
{
char *ptr = NULL;
- assert(pool && str && pool->item_size == sizeof(char));
+ GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(str, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
if (n == SIZE_MAX)
return NULL;
char *git_pool_strdup(git_pool *pool, const char *str)
{
- assert(pool && str && pool->item_size == sizeof(char));
+ GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(str, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
+
return git_pool_strndup(pool, str, strlen(str));
}
void *ptr;
size_t len_a, len_b, total;
- assert(pool && pool->item_size == sizeof(char));
+ GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
len_a = a ? strlen(a) : 0;
len_b = b ? strlen(b) : 0;
{
mode_t mode = 0;
+ #ifdef GIT_DEBUG_STRICT_OPEN
+ if (strstr(path, "//") != NULL) {
+ errno = EACCES;
+ return -1;
+ }
+ #endif
+
if (flags & O_CREAT) {
va_list arg_list;
{
char *cwd_buffer;
- assert(buffer_out && size > 0);
+ GIT_ASSERT_ARG(buffer_out);
+ GIT_ASSERT_ARG(size > 0);
cwd_buffer = getcwd(buffer_out, size);
while (cnt) {
ssize_t r;
#ifdef GIT_WIN32
- assert((size_t)((unsigned int)cnt) == cnt);
+ GIT_ASSERT((size_t)((unsigned int)cnt) == cnt);
r = write(fd, b, (unsigned int)cnt);
#else
r = write(fd, b, cnt);
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset)
{
+ const char *ptr;
+ size_t remaining_len;
+
GIT_MMAP_VALIDATE(out, len, prot, flags);
- out->data = NULL;
- out->len = 0;
+ /* writes cannot be emulated without handling pagefaults since write happens by
+ * writing to mapped memory */
+ if (prot & GIT_PROT_WRITE) {
+ git_error_set(GIT_ERROR_OS, "trying to map %s-writeable",
+ ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED) ? "shared": "private");
+ return -1;
+ }
- if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) {
- git_error_set(GIT_ERROR_OS, "trying to map shared-writeable");
+ if (!git__is_ssizet(len)) {
+ errno = EINVAL;
return -1;
}
+ out->len = 0;
out->data = git__malloc(len);
GIT_ERROR_CHECK_ALLOC(out->data);
- if (!git__is_ssizet(len) ||
- (p_lseek(fd, offset, SEEK_SET) < 0) ||
- (p_read(fd, out->data, len) != (ssize_t)len)) {
- git_error_set(GIT_ERROR_OS, "mmap emulation failed");
- return -1;
+ remaining_len = len;
+ ptr = (const char *)out->data;
+ while (remaining_len > 0) {
+ ssize_t nb;
+ HANDLE_EINTR(nb, p_pread(fd, (void *)ptr, remaining_len, offset));
+ if (nb <= 0) {
+ git_error_set(GIT_ERROR_OS, "mmap emulation failed");
+ git__free(out->data);
+ out->data = NULL;
+ return -1;
+ }
+
+ ptr += nb;
+ offset += nb;
+ remaining_len -= nb;
}
out->len = len;
int p_munmap(git_map *map)
{
- assert(map != NULL);
+ GIT_ASSERT_ARG(map);
git__free(map->data);
+ /* Initializing will help debug use-after-free */
+ map->len = 0;
+ map->data = NULL;
+
return 0;
}
#define EAFNOSUPPORT (INT_MAX-1)
#endif
+/* Compiler independent macro to handle signal interrpted system calls */
+#define HANDLE_EINTR(result, x) do { \
+ result = (x); \
+ } while (result == -1 && errno == EINTR);
+
+
/* Provide a 64-bit size for offsets. */
#if defined(_MSC_VER)
extern ssize_t p_read(git_file fd, void *buf, size_t cnt);
extern int p_write(git_file fd, const void *buf, size_t cnt);
+extern ssize_t p_pread(int fd, void *data, size_t size, off64_t offset);
+extern ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset);
+
#define p_close(fd) close(fd)
#define p_umask(m) umask(m)
{
tree_reader *reader;
- assert(out && tree);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(tree);
reader = git__calloc(1, sizeof(tree_reader));
GIT_ERROR_CHECK_ALLOC(reader);
git_oid id;
int error;
- if ((error = git_buf_joinpath(&path,
- git_repository_workdir(reader->repo), filename)) < 0)
+ if ((error = git_repository_workdir_path(&path, reader->repo, filename)) < 0)
goto done;
if ((error = p_lstat(path.ptr, &st)) < 0) {
workdir_reader *reader;
int error;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
reader = git__calloc(1, sizeof(workdir_reader));
GIT_ERROR_CHECK_ALLOC(reader);
index_reader *reader;
int error;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
reader = git__calloc(1, sizeof(index_reader));
GIT_ERROR_CHECK_ALLOC(reader);
git_reader *reader,
const char *filename)
{
- assert(out && reader && filename);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(reader);
+ GIT_ASSERT_ARG(filename);
return reader->read(out, out_id, out_filemode, reader, filename);
}
{
git_rebase_operation *operation;
- assert((type == GIT_REBASE_OPERATION_EXEC) == !id);
- assert((type == GIT_REBASE_OPERATION_EXEC) == !!exec);
+ GIT_ASSERT_WITH_RETVAL((type == GIT_REBASE_OPERATION_EXEC) == !id, NULL);
+ GIT_ASSERT_WITH_RETVAL((type == GIT_REBASE_OPERATION_EXEC) == !!exec, NULL);
if ((operation = git_array_alloc(rebase->operations)) == NULL)
return NULL;
size_t state_path_len;
int error;
- assert(repo);
+ GIT_ASSERT_ARG(repo);
if ((error = rebase_check_versions(given_opts)) < 0)
return error;
bool inmemory = (given_opts && given_opts->inmemory);
int error;
- assert(repo && (upstream || onto));
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(upstream || onto);
*out = NULL;
{
int error;
- assert(out && rebase);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(rebase);
if ((error = rebase_movenext(rebase)) < 0)
return error;
git_index **out,
git_rebase *rebase)
{
- assert(out && rebase && rebase->index);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(rebase);
+ GIT_ASSERT_ARG(rebase->index);
GIT_REFCOUNT_INC(rebase->index);
*out = rebase->index;
return 0;
}
+#ifndef GIT_DEPRECATE_HARD
+static int create_signed(
+ git_oid *out,
+ git_rebase *rebase,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ git_tree *tree,
+ size_t parent_count,
+ const git_commit **parents)
+{
+ git_buf commit_content = GIT_BUF_INIT,
+ commit_signature = GIT_BUF_INIT,
+ signature_field = GIT_BUF_INIT;
+ int error;
+
+ git_error_clear();
+
+ if ((error = git_commit_create_buffer(&commit_content,
+ rebase->repo, author, committer, message_encoding,
+ message, tree, parent_count, parents)) < 0)
+ goto done;
+
+ error = rebase->options.signing_cb(&commit_signature,
+ &signature_field, commit_content.ptr,
+ rebase->options.payload);
+
+ if (error) {
+ if (error != GIT_PASSTHROUGH)
+ git_error_set_after_callback_function(error, "signing_cb");
+
+ goto done;
+ }
+
+ error = git_commit_create_with_signature(out, rebase->repo,
+ commit_content.ptr,
+ commit_signature.size > 0 ? commit_signature.ptr : NULL,
+ signature_field.size > 0 ? signature_field.ptr : NULL);
+
+done:
+ git_buf_dispose(&commit_signature);
+ git_buf_dispose(&signature_field);
+ git_buf_dispose(&commit_content);
+ return error;
+}
+#endif
+
static int rebase_commit__create(
git_commit **out,
git_rebase *rebase,
git_commit *current_commit = NULL, *commit = NULL;
git_tree *parent_tree = NULL, *tree = NULL;
git_oid tree_id, commit_id;
- git_buf commit_content = GIT_BUF_INIT, commit_signature = GIT_BUF_INIT,
- signature_field = GIT_BUF_INIT;
- const char *signature_field_string = NULL,
- *commit_signature_string = NULL;
int error;
operation = git_array_get(rebase->operations, rebase->current);
message = git_commit_message(current_commit);
}
- if ((error = git_commit_create_buffer(&commit_content, rebase->repo, author, committer,
- message_encoding, message, tree, 1, (const git_commit **)&parent_commit)) < 0)
- goto done;
+ git_error_clear();
+ error = GIT_PASSTHROUGH;
- if (rebase->options.signing_cb) {
- git_error_clear();
- error = git_error_set_after_callback_function(rebase->options.signing_cb(
- &commit_signature, &signature_field, git_buf_cstr(&commit_content),
- rebase->options.payload), "commit signing_cb failed");
- if (error == GIT_PASSTHROUGH) {
- git_buf_dispose(&commit_signature);
- git_buf_dispose(&signature_field);
- git_error_clear();
- error = GIT_OK;
- } else if (error < 0)
- goto done;
- }
+ if (rebase->options.commit_create_cb) {
+ error = rebase->options.commit_create_cb(&commit_id,
+ author, committer, message_encoding, message,
+ tree, 1, (const git_commit **)&parent_commit,
+ rebase->options.payload);
- if (git_buf_is_allocated(&commit_signature)) {
- assert(git_buf_contains_nul(&commit_signature));
- commit_signature_string = git_buf_cstr(&commit_signature);
+ git_error_set_after_callback_function(error,
+ "commit_create_cb");
}
-
- if (git_buf_is_allocated(&signature_field)) {
- assert(git_buf_contains_nul(&signature_field));
- signature_field_string = git_buf_cstr(&signature_field);
+#ifndef GIT_DEPRECATE_HARD
+ else if (rebase->options.signing_cb) {
+ error = create_signed(&commit_id, rebase, author,
+ committer, message_encoding, message, tree,
+ 1, (const git_commit **)&parent_commit);
}
+#endif
- if ((error = git_commit_create_with_signature(&commit_id, rebase->repo,
- git_buf_cstr(&commit_content), commit_signature_string,
- signature_field_string)))
+ if (error == GIT_PASSTHROUGH)
+ error = git_commit_create(&commit_id, rebase->repo, NULL,
+ author, committer, message_encoding, message,
+ tree, 1, (const git_commit **)&parent_commit);
+
+ if (error)
goto done;
if ((error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0)
if (error < 0)
git_commit_free(commit);
- git_buf_dispose(&commit_signature);
- git_buf_dispose(&signature_field);
- git_buf_dispose(&commit_content);
git_commit_free(current_commit);
git_tree_free(parent_tree);
git_tree_free(tree);
int error;
operation = git_array_get(rebase->operations, rebase->current);
- assert(operation);
+ GIT_ASSERT(operation);
if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 ||
(error = git_repository_head(&head, rebase->repo)) < 0 ||
git_commit *commit = NULL;
int error = 0;
- assert(rebase->index);
- assert(rebase->last_commit);
- assert(rebase->current < rebase->operations.size);
+ GIT_ASSERT_ARG(rebase->index);
+ GIT_ASSERT_ARG(rebase->last_commit);
+ GIT_ASSERT_ARG(rebase->current < rebase->operations.size);
if ((error = rebase_commit__create(&commit, rebase, rebase->index,
rebase->last_commit, author, committer, message_encoding, message)) < 0)
{
int error;
- assert(rebase && committer);
+ GIT_ASSERT_ARG(rebase);
+ GIT_ASSERT_ARG(committer);
if (rebase->inmemory)
error = rebase_commit_inmemory(
git_commit *orig_head_commit = NULL;
int error;
- assert(rebase);
+ GIT_ASSERT_ARG(rebase);
if (rebase->inmemory)
return 0;
{
int error = 0;
- assert(rebase);
+ GIT_ASSERT_ARG(rebase);
if (rebase->inmemory)
return 0;
}
const char *git_rebase_orig_head_name(git_rebase *rebase) {
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL);
return rebase->orig_head_name;
}
const git_oid *git_rebase_orig_head_id(git_rebase *rebase) {
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL);
return &rebase->orig_head_id;
}
const char *git_rebase_onto_name(git_rebase *rebase) {
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL);
return rebase->onto_name;
}
size_t git_rebase_operation_entrycount(git_rebase *rebase)
{
- assert(rebase);
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, 0);
return git_array_size(rebase->operations);
}
size_t git_rebase_operation_current(git_rebase *rebase)
{
- assert(rebase);
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, 0);
return rebase->started ? rebase->current : GIT_REBASE_NO_OPERATION;
}
git_rebase_operation *git_rebase_operation_byindex(git_rebase *rebase, size_t idx)
{
- assert(rebase);
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL);
return git_array_get(rebase->operations, idx);
}
{
git_refdb *db;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
db = git__calloc(1, sizeof(*db));
GIT_ERROR_CHECK_ALLOC(db);
git_refdb *db;
git_refdb_backend *dir;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
*out = NULL;
int git_refdb_compress(git_refdb *db)
{
- assert(db);
+ GIT_ASSERT_ARG(db);
if (db->backend->compress)
return db->backend->compress(db->backend);
int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
{
- assert(exists && refdb && refdb->backend);
+ GIT_ASSERT_ARG(exists);
+ GIT_ASSERT_ARG(refdb);
+ GIT_ASSERT_ARG(refdb->backend);
return refdb->backend->exists(exists, refdb->backend, ref_name);
}
git_reference *ref;
int error;
- assert(db && db->backend && out && ref_name);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref_name);
error = db->backend->lookup(&ref, db->backend, ref_name);
if (error < 0)
int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target)
{
- assert(db && db->backend);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
GIT_REFCOUNT_INC(db);
ref->db = db;
{
int error;
- assert(db && db->backend);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
+
error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message);
if (error < 0)
return error;
int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *old_id, const char *old_target)
{
- assert(db && db->backend);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
+
return db->backend->del(db->backend, ref_name, old_id, old_target);
}
{
int error;
- assert(db && db->backend);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
return error;
int git_refdb_has_log(git_refdb *db, const char *refname)
{
- assert(db && refname);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(refname);
return db->backend->has_log(db->backend, refname);
}
int git_refdb_ensure_log(git_refdb *db, const char *refname)
{
- assert(db && refname);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(refname);
return db->backend->ensure_log(db->backend, refname);
}
int git_refdb_lock(void **payload, git_refdb *db, const char *refname)
{
- assert(payload && db && refname);
+ GIT_ASSERT_ARG(payload);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(refname);
if (!db->backend->lock) {
git_error_set(GIT_ERROR_REFERENCE, "backend does not support locking");
int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message)
{
- assert(db);
+ GIT_ASSERT_ARG(db);
return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message);
}
static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
+GIT_INLINE(int) loose_path(
+ git_buf *out,
+ const char *base,
+ const char *refname)
+{
+ if (git_buf_joinpath(out, base, refname) < 0)
+ return -1;
+
+ return git_path_validate_filesystem_with_suffix(out->ptr, out->size,
+ CONST_STRLEN(".lock"));
+}
+
+GIT_INLINE(int) reflog_path(
+ git_buf *out,
+ git_repository *repo,
+ const char *refname)
+{
+ const char *base;
+ int error;
+
+ base = (strcmp(refname, GIT_HEAD_FILE) == 0) ? repo->gitdir :
+ repo->commondir;
+
+ if ((error = git_buf_joinpath(out, base, GIT_REFLOG_DIR)) < 0)
+ return error;
+
+ return loose_path(out, out->ptr, refname);
+}
+
static int packref_cmp(const void *a_, const void *b_)
{
const struct packref *a = a_, *b = b_;
*/
if (error <= 0) {
if (error == GIT_ENOTFOUND) {
- git_sortedcache_clear(backend->refcache, true);
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, true));
git_error_clear();
error = 0;
}
/* At this point, refresh the packed refs from the loaded buffer. */
- git_sortedcache_clear(backend->refcache, false);
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, false));
scan = (char *)packedrefs.ptr;
eof = scan + packedrefs.size;
parse_failed:
git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file");
- git_sortedcache_clear(backend->refcache, false);
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, false));
git_sortedcache_wunlock(backend->refcache);
git_buf_dispose(&packedrefs);
{
int error;
- /* build full path to file */
- if ((error = git_buf_joinpath(buf, base, path)) < 0 ||
- (error = git_futils_readbuffer(buf, buf->ptr)) < 0)
+ if ((error = loose_path(buf, base, path)) < 0 ||
+ (error = git_futils_readbuffer(buf, buf->ptr)) < 0)
git_buf_dispose(buf);
return error;
if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
goto done;
- git_sortedcache_wlock(backend->refcache);
+ if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
+ goto done;
if (!(error = git_sortedcache_upsert(
(void **)&ref, backend->refcache, name))) {
git_buf ref_path = GIT_BUF_INIT;
int error;
- assert(backend);
+ GIT_ASSERT_ARG(backend);
*exists = 0;
- if ((error = git_buf_joinpath(&ref_path, backend->gitpath, ref_name)) < 0)
+ if ((error = loose_path(&ref_path, backend->gitpath, ref_name)) < 0)
goto out;
if (git_path_isfile(ref_path.ptr)) {
refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
int error;
- assert(backend);
+ GIT_ASSERT_ARG(backend);
if (!(error = loose_lookup(out, backend, ref_name)))
return 0;
}
}
- if ((error = git_buf_printf(&path, "%s/", backend->commonpath)) < 0 ||
+ if ((error = git_buf_puts(&path, backend->commonpath)) < 0 ||
(error = git_buf_put(&path, ref_prefix, ref_prefix_len)) < 0) {
git_buf_dispose(&path);
return error;
refdb_fs_iter *iter = NULL;
int error;
- assert(backend);
+ GIT_ASSERT_ARG(backend);
iter = git__calloc(1, sizeof(refdb_fs_iter));
GIT_ERROR_CHECK_ALLOC(iter);
static int reference_path_available(
refdb_fs_backend *backend,
const char *new_ref,
- const char* old_ref,
+ const char *old_ref,
int force)
{
size_t i;
}
}
- git_sortedcache_rlock(backend->refcache);
+ if ((error = git_sortedcache_rlock(backend->refcache)) < 0)
+ return error;
for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
struct packref *ref = git_sortedcache_entry(backend->refcache, i);
git_buf ref_path = GIT_BUF_INIT;
const char *basedir;
- assert(file && backend && name);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(name);
- if (!git_path_isvalid(backend->repo, name, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
+ if (!git_path_validate(backend->repo, name, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", name);
return GIT_EINVALIDSPEC;
}
if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
return error;
- if (git_buf_joinpath(&ref_path, basedir, name) < 0)
- return -1;
+ if ((error = loose_path(&ref_path, basedir, name)) < 0)
+ return error;
filebuf_flags = GIT_FILEBUF_CREATE_LEADING_DIRS;
if (backend->fsync)
static int loose_commit(git_filebuf *file, const git_reference *ref)
{
- assert(file && ref);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(ref);
if (ref->type == GIT_REFERENCE_DIRECT) {
char oid[GIT_OID_HEXSZ + 1];
} else if (ref->type == GIT_REFERENCE_SYMBOLIC) {
git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic);
} else {
- assert(0); /* don't let this happen */
+ GIT_ASSERT(0);
}
return git_filebuf_commit(file);
for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
struct packref *ref = git_sortedcache_entry(refcache, i);
- assert(ref);
+ GIT_ASSERT(ref);
if ((error = packed_find_peel(backend, ref)) < 0)
goto fail;
if (!old_id && !old_target)
return 0;
- if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0)
+ if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0) {
+ if (error == GIT_ENOTFOUND && old_id && git_oid_is_zero(old_id))
+ return 0;
goto out;
+ }
/* If the types don't match, there's no way the values do */
if (old_id && old_ref->type != GIT_REFERENCE_DIRECT) {
git_filebuf file = GIT_FILEBUF_INIT;
int error = 0;
- assert(backend);
+ GIT_ASSERT_ARG(backend);
if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
return error;
return error;
}
-static void refdb_fs_backend__prune_refs(
+static int refdb_fs_backend__prune_refs(
refdb_fs_backend *backend,
const char *ref_name,
const char *prefix)
git_buf relative_path = GIT_BUF_INIT;
git_buf base_path = GIT_BUF_INIT;
size_t commonlen;
+ int error;
- assert(backend && ref_name);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(ref_name);
- if (git_buf_sets(&relative_path, ref_name) < 0)
+ if ((error = git_buf_sets(&relative_path, ref_name)) < 0)
goto cleanup;
git_path_squash_slashes(&relative_path);
git_buf_truncate(&relative_path, commonlen);
- if (prefix) {
- if (git_buf_join3(&base_path, '/', backend->commonpath, prefix, git_buf_cstr(&relative_path)) < 0)
- goto cleanup;
- } else {
- if (git_buf_joinpath(&base_path, backend->commonpath, git_buf_cstr(&relative_path)) < 0)
- goto cleanup;
- }
+ if (prefix)
+ error = git_buf_join3(&base_path, '/',
+ backend->commonpath, prefix,
+ git_buf_cstr(&relative_path));
+ else
+ error = git_buf_joinpath(&base_path,
+ backend->commonpath,
+ git_buf_cstr(&relative_path));
+
+ if (!error)
+ error = git_path_validate_filesystem(base_path.ptr, base_path.size);
+
+ if (error < 0)
+ goto cleanup;
+
+ error = git_futils_rmdir_r(ref_name + commonlen,
+ git_buf_cstr(&base_path),
+ GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT);
- git_futils_rmdir_r(ref_name + commonlen, git_buf_cstr(&base_path), GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT);
+ if (error == GIT_ENOTFOUND)
+ error = 0;
}
cleanup:
git_buf_dispose(&relative_path);
git_buf_dispose(&base_path);
+ return error;
}
static int refdb_fs_backend__delete(
git_filebuf file = GIT_FILEBUF_INIT;
int error = 0;
- assert(backend && ref_name);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(ref_name);
if ((error = loose_lock(&file, backend, ref_name)) < 0)
return error;
static int loose_delete(refdb_fs_backend *backend, const char *ref_name)
{
- git_buf loose_path = GIT_BUF_INIT;
+ git_buf path = GIT_BUF_INIT;
int error = 0;
- if (git_buf_joinpath(&loose_path, backend->commonpath, ref_name) < 0)
- return -1;
+ if ((error = loose_path(&path, backend->commonpath, ref_name)) < 0)
+ return error;
- error = p_unlink(loose_path.ptr);
+ error = p_unlink(path.ptr);
if (error < 0 && errno == ENOENT)
error = GIT_ENOTFOUND;
else if (error != 0)
error = -1;
- git_buf_dispose(&loose_path);
+ git_buf_dispose(&path);
return error;
}
cleanup:
git_filebuf_cleanup(file);
if (error == 0)
- refdb_fs_backend__prune_refs(backend, ref_name, "");
+ error = refdb_fs_backend__prune_refs(backend, ref_name, "");
return error;
}
git_filebuf file = GIT_FILEBUF_INIT;
int error;
- assert(backend);
+ GIT_ASSERT_ARG(backend);
if ((error = reference_path_available(
backend, new_name, old_name, force)) < 0 ||
int error;
refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
- assert(backend);
+ GIT_ASSERT_ARG(backend);
if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */
(error = packed_loadloose(backend)) < 0 || /* add all the loose refs */
{
refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
- assert(backend);
+ if (!backend)
+ return;
git_sortedcache_free(backend->refcache);
git__free(backend->gitpath);
GIT_MKDIR_PATH, NULL) < 0)
goto done;
- /* Return root of the namespaced gitpath, i.e. without the trailing '/refs' */
+ /* Return root of the namespaced gitpath, i.e. without the trailing 'refs' */
git_buf_rtruncate_at_char(&path, '/');
+ git_buf_putc(&path, '/');
out = git_buf_detach(&path);
done:
return p_close(fd);
}
-GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
-{
- if (strcmp(name, GIT_HEAD_FILE) == 0)
- return git_buf_join3(path, '/', repo->gitdir, GIT_REFLOG_DIR, name);
- return git_buf_join3(path, '/', repo->commondir, GIT_REFLOG_DIR, name);
-}
-
static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
{
refdb_fs_backend *backend;
git_buf path = GIT_BUF_INIT;
int error;
- assert(_backend && name);
+ GIT_ASSERT_ARG(_backend && name);
backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
repo = backend->repo;
- if ((error = retrieve_reflog_path(&path, repo, name)) < 0)
+ if ((error = reflog_path(&path, repo, name)) < 0)
return error;
error = create_new_reflog_file(git_buf_cstr(&path));
int ret = 0;
git_buf path = GIT_BUF_INIT;
- if (retrieve_reflog_path(&path, repo, name) < 0)
+ if (reflog_path(&path, repo, name) < 0)
goto cleanup;
ret = git_path_isfile(git_buf_cstr(&path));
{
refdb_fs_backend *backend;
- assert(_backend && name);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(name);
backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
git_repository *repo;
refdb_fs_backend *backend;
- assert(out && _backend && name);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(name);
backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
repo = backend->repo;
if (reflog_alloc(&log, name) < 0)
return -1;
- if (retrieve_reflog_path(&log_path, repo, name) < 0)
+ if (reflog_path(&log_path, repo, name) < 0)
goto cleanup;
error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
repo = backend->repo;
- if (!git_path_isvalid(backend->repo, refname, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
+ if (!git_path_validate(backend->repo, refname, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", refname);
return GIT_EINVALIDSPEC;
}
- if (retrieve_reflog_path(&log_path, repo, refname) < 0)
+ if (reflog_path(&log_path, repo, refname) < 0)
return -1;
if (!git_path_isfile(git_buf_cstr(&log_path))) {
git_buf log = GIT_BUF_INIT;
git_filebuf fbuf = GIT_FILEBUF_INIT;
- assert(_backend && reflog);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(reflog);
backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0)
goto cleanup;
- if ((error = retrieve_reflog_path(&path, repo, ref->name)) < 0)
+ if ((error = reflog_path(&path, repo, ref->name)) < 0)
goto cleanup;
if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) &&
git_repository *repo;
refdb_fs_backend *backend;
- assert(_backend && old_name && new_name);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(old_name);
+ GIT_ASSERT_ARG(new_name);
backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
repo = backend->repo;
if (git_buf_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0)
return -1;
- if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0)
- return -1;
+ if ((error = loose_path(&old_path, git_buf_cstr(&temp_path), old_name)) < 0)
+ return error;
- if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
- return -1;
+ if ((error = loose_path(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized))) < 0)
+ return error;
if (!git_path_exists(git_buf_cstr(&old_path))) {
error = GIT_ENOTFOUND;
* - a/b -> a/b/c
* - a/b/c/d -> a/b/c
*/
- if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
- return -1;
+ if ((error = loose_path(&temp_path, git_buf_cstr(&temp_path), "temp_reflog")) < 0)
+ return error;
if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) {
error = -1;
git_buf path = GIT_BUF_INIT;
int error;
- assert(_backend && name);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(name);
- if ((error = retrieve_reflog_path(&path, backend->repo, name)) < 0)
+ if ((error = reflog_path(&path, backend->repo, name)) < 0)
goto out;
if (!git_path_exists(path.ptr))
if ((error = p_unlink(path.ptr)) < 0)
goto out;
- refdb_fs_backend__prune_refs(backend, name, GIT_REFLOG_DIR);
+ error = refdb_fs_backend__prune_refs(backend, name, GIT_REFLOG_DIR);
out:
git_buf_dispose(&path);
git_refdb *refdb;
int error;
- assert(reflog && repo && name);
+ GIT_ASSERT_ARG(reflog);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return error;
{
git_refdb *db;
- assert(reflog && reflog->db);
+ GIT_ASSERT_ARG(reflog);
+ GIT_ASSERT_ARG(reflog->db);
db = reflog->db;
return db->backend->reflog_write(db->backend, reflog);
const git_reflog_entry *previous;
git_reflog_entry *entry;
- assert(reflog && new_oid && committer);
+ GIT_ASSERT_ARG(reflog);
+ GIT_ASSERT_ARG(new_oid);
+ GIT_ASSERT_ARG(committer);
entry = git__calloc(1, sizeof(git_reflog_entry));
GIT_ERROR_CHECK_ALLOC(entry);
size_t git_reflog_entrycount(git_reflog *reflog)
{
- assert(reflog);
+ GIT_ASSERT_ARG_WITH_RETVAL(reflog, 0);
return reflog->entries.length;
}
-const git_reflog_entry * git_reflog_entry_byindex(const git_reflog *reflog, size_t idx)
+const git_reflog_entry *git_reflog_entry_byindex(const git_reflog *reflog, size_t idx)
{
- assert(reflog);
+ GIT_ASSERT_ARG_WITH_RETVAL(reflog, NULL);
if (idx >= reflog->entries.length)
return NULL;
&reflog->entries, reflog_inverse_index(idx, reflog->entries.length));
}
-const git_oid * git_reflog_entry_id_old(const git_reflog_entry *entry)
+const git_oid *git_reflog_entry_id_old(const git_reflog_entry *entry)
{
- assert(entry);
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
return &entry->oid_old;
}
-const git_oid * git_reflog_entry_id_new(const git_reflog_entry *entry)
+const git_oid *git_reflog_entry_id_new(const git_reflog_entry *entry)
{
- assert(entry);
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
return &entry->oid_cur;
}
-const git_signature * git_reflog_entry_committer(const git_reflog_entry *entry)
+const git_signature *git_reflog_entry_committer(const git_reflog_entry *entry)
{
- assert(entry);
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
return entry->committer;
}
-const char * git_reflog_entry_message(const git_reflog_entry *entry)
+const char *git_reflog_entry_message(const git_reflog_entry *entry)
{
- assert(entry);
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
return entry->msg;
}
{
git_reference *ref;
- assert(name && target);
+ GIT_ASSERT_ARG_WITH_RETVAL(name, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(target, NULL);
ref = alloc_ref(name);
if (!ref)
{
git_reference *ref;
- assert(name && oid);
+ GIT_ASSERT_ARG_WITH_RETVAL(name, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(oid, NULL);
ref = alloc_ref(name);
if (!ref)
size_t namelen, reflen;
git_reference *rewrite = NULL;
- assert(ptr_to_ref && name);
+ GIT_ASSERT_ARG_WITH_RETVAL(ptr_to_ref, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(name, NULL);
namelen = strlen(name);
git_refdb *refdb;
int error = 0;
- assert(ref_out && repo && name);
+ GIT_ASSERT_ARG(ref_out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
if ((error = reference_normalize_for_repo(normalized, repo, name, true)) < 0 ||
(error = git_repository_refdb__weakptr(&refdb, repo)) < 0 ||
int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
{
- int error = 0, i;
+ int error = 0, i, valid;
bool fallbackmode = true, foundvalid = false;
git_reference *ref;
git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT;
- static const char* formatters[] = {
+ static const char *formatters[] = {
"%s",
GIT_REFS_DIR "%s",
GIT_REFS_TAGS_DIR "%s",
git_buf_clear(&refnamebuf);
- if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0)
+ if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0 ||
+ (error = git_reference_name_is_valid(&valid, git_buf_cstr(&refnamebuf))) < 0)
goto cleanup;
- if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) {
+ if (!valid) {
error = GIT_EINVALIDSPEC;
continue;
}
*/
git_reference_t git_reference_type(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG(ref);
return ref->type;
}
const char *git_reference_name(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
return ref->name;
}
git_repository *git_reference_owner(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
return ref->db->repo;
}
const git_oid *git_reference_target(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
if (ref->type != GIT_REFERENCE_DIRECT)
return NULL;
const git_oid *git_reference_target_peel(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
if (ref->type != GIT_REFERENCE_DIRECT || git_oid_is_zero(&ref->peel))
return NULL;
const char *git_reference_symbolic_target(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
if (ref->type != GIT_REFERENCE_SYMBOLIC)
return NULL;
git_reference *ref = NULL;
int error = 0;
- assert(repo && name);
- assert(symbolic || signature);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(symbolic || signature);
if (ref_out)
*ref_out = NULL;
return error;
if (oid != NULL) {
- assert(symbolic == NULL);
+ GIT_ASSERT(symbolic == NULL);
if (!git_object__is_valid(repo, oid, GIT_OBJECT_ANY)) {
git_error_set(GIT_ERROR_REFERENCE,
int error;
git_signature *who = NULL;
- assert(id);
+ GIT_ASSERT_ARG(id);
if ((error = git_reference__log_signature(&who, repo)) < 0)
return error;
int error;
git_signature *who = NULL;
- assert(target);
+ GIT_ASSERT_ARG(target);
if ((error = git_reference__log_signature(&who, repo)) < 0)
return error;
int error;
git_repository *repo;
- assert(out && ref && id);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref);
+ GIT_ASSERT_ARG(id);
repo = ref->db->repo;
{
int error;
- assert(out && ref && target);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref);
+ GIT_ASSERT_ARG(target);
if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0)
return error;
git_repository *repo;
int error;
- assert(out && ref);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref);
repo = git_reference_owner(ref);
{
git_vector ref_list;
- assert(array && repo);
+ GIT_ASSERT_ARG(array);
+ GIT_ASSERT_ARG(repo);
array->strings = NULL;
array->count = 0;
size_t i;
char c;
- assert(name && len > 0);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(len > 0);
for (i = 0; i < len; i++)
{
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
#endif
- assert(name);
+ GIT_ASSERT_ARG(name);
process_flags = flags;
current = (char *)name;
goto cleanup;
}
- git_buf_copy_cstr(buffer_out, buffer_size, &buf);
+ if ((error = git_buf_copy_cstr(buffer_out, buffer_size, &buf)) < 0)
+ goto cleanup;
error = 0;
const git_reference *ref2)
{
git_reference_t type1, type2;
- assert(ref1 && ref2);
+
+ GIT_ASSERT_ARG(ref1);
+ GIT_ASSERT_ARG(ref2);
type1 = git_reference_type(ref1);
type2 = git_reference_type(ref2);
int error;
git_refdb *refdb;
- assert(repo && refname);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return error;
int error;
git_refdb *refdb;
- assert(repo && refname);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return error;
int git_reference_is_branch(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG(ref);
return git_reference__is_branch(ref->name);
}
int git_reference_is_remote(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG(ref);
return git_reference__is_remote(ref->name);
}
int git_reference_is_tag(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG(ref);
return git_reference__is_tag(ref->name);
}
int git_reference_is_note(const git_reference *ref)
{
- assert(ref);
+ GIT_ASSERT_ARG(ref);
return git_reference__is_note(ref->name);
}
-static int peel_error(int error, const git_reference *ref, const char* msg)
+static int peel_error(int error, const git_reference *ref, const char *msg)
{
git_error_set(
GIT_ERROR_INVALID,
git_object *target = NULL;
int error;
- assert(ref);
+ GIT_ASSERT_ARG(ref);
if (ref->type == GIT_REFERENCE_DIRECT) {
resolved = ref;
return error;
}
-int git_reference__is_valid_name(const char *refname, unsigned int flags)
+int git_reference__name_is_valid(
+ int *valid,
+ const char *refname,
+ unsigned int flags)
{
- if (git_reference__normalize_name(NULL, refname, flags) < 0) {
- git_error_clear();
- return false;
- }
+ int error;
- return true;
+ GIT_ASSERT(valid && refname);
+
+ *valid = 0;
+
+ error = git_reference__normalize_name(NULL, refname, flags);
+
+ if (!error)
+ *valid = 1;
+ else if (error == GIT_EINVALIDSPEC)
+ error = 0;
+
+ return error;
}
-int git_reference_is_valid_name(const char *refname)
+int git_reference_name_is_valid(int *valid, const char *refname)
{
- return git_reference__is_valid_name(refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL);
+ return git_reference__name_is_valid(valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL);
}
const char *git_reference__shorthand(const char *name)
else if (!git__prefixcmp(name, GIT_REFS_DIR))
return name + strlen(GIT_REFS_DIR);
- /* No shorthands are avaiable, so just return the name */
+ /* No shorthands are available, so just return the name. */
return name;
}
{
int error;
git_reference *tmp_ref;
- assert(unborn && ref && repo);
+
+ GIT_ASSERT_ARG(unborn);
+ GIT_ASSERT_ARG(ref);
+ GIT_ASSERT_ARG(repo);
if (ref->type == GIT_REFERENCE_DIRECT) {
*unborn = 0;
return 0;
}
+
+/* Deprecated functions */
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_reference_is_valid_name(const char *refname)
+{
+ int valid = 0;
+
+ git_reference__name_is_valid(&valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL);
+
+ return valid;
+}
+
+#endif
int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *sig, const char *log_message);
-int git_reference__is_valid_name(const char *refname, unsigned int flags);
+int git_reference__name_is_valid(int *valid, const char *name, unsigned int flags);
int git_reference__is_branch(const char *ref_name);
int git_reference__is_remote(const char *ref_name);
int git_reference__is_tag(const char *ref_name);
size_t llen;
int is_glob = 0;
const char *lhs, *rhs;
- int flags;
+ int valid = 0;
+ unsigned int flags;
- assert(refspec && input);
+ GIT_ASSERT_ARG(refspec);
+ GIT_ASSERT_ARG(input);
memset(refspec, 0x0, sizeof(git_refspec));
refspec->push = !is_fetch;
if (is_fetch) {
/*
- * LHS
- * - empty is allowed; it means HEAD.
- * - otherwise it must be a valid looking ref.
- */
+ * LHS
+ * - empty is allowed; it means HEAD.
+ * - otherwise it must be a valid looking ref.
+ */
if (!*refspec->src)
; /* empty is ok */
- else if (!git_reference__is_valid_name(refspec->src, flags))
+ else if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
+ goto on_error;
+ else if (!valid)
goto invalid;
+
/*
- * RHS
- * - missing is ok, and is same as empty.
- * - empty is ok; it means not to store.
- * - otherwise it must be a valid looking ref.
- */
+ * RHS
+ * - missing is ok, and is same as empty.
+ * - empty is ok; it means not to store.
+ * - otherwise it must be a valid looking ref.
+ */
if (!refspec->dst)
; /* ok */
else if (!*refspec->dst)
; /* ok */
- else if (!git_reference__is_valid_name(refspec->dst, flags))
+ else if (git_reference__name_is_valid(&valid, refspec->dst, flags) < 0)
+ goto on_error;
+ else if (!valid)
goto invalid;
} else {
/*
- * LHS
- * - empty is allowed; it means delete.
- * - when wildcarded, it must be a valid looking ref.
- * - otherwise, it must be an extended SHA-1, but
- * there is no existing way to validate this.
- */
+ * LHS
+ * - empty is allowed; it means delete.
+ * - when wildcarded, it must be a valid looking ref.
+ * - otherwise, it must be an extended SHA-1, but
+ * there is no existing way to validate this.
+ */
if (!*refspec->src)
; /* empty is ok */
else if (is_glob) {
- if (!git_reference__is_valid_name(refspec->src, flags))
+ if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
+ goto on_error;
+ else if (!valid)
goto invalid;
}
else {
; /* anything goes, for now */
}
+
/*
- * RHS
- * - missing is allowed, but LHS then must be a
- * valid looking ref.
- * - empty is not allowed.
- * - otherwise it must be a valid looking ref.
- */
+ * RHS
+ * - missing is allowed, but LHS then must be a
+ * valid looking ref.
+ * - empty is not allowed.
+ * - otherwise it must be a valid looking ref.
+ */
if (!refspec->dst) {
- if (!git_reference__is_valid_name(refspec->src, flags))
+ if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
+ goto on_error;
+ else if (!valid)
goto invalid;
} else if (!*refspec->dst) {
goto invalid;
} else {
- if (!git_reference__is_valid_name(refspec->dst, flags))
+ if (git_reference__name_is_valid(&valid, refspec->dst, flags) < 0)
+ goto on_error;
+ else if (!valid)
goto invalid;
}
return 0;
- invalid:
- git_error_set(
- GIT_ERROR_INVALID,
- "'%s' is not a valid refspec.", input);
- git_refspec__dispose(refspec);
+invalid:
+ git_error_set(GIT_ERROR_INVALID,
+ "'%s' is not a valid refspec.", input);
+ git_refspec__dispose(refspec);
+ return GIT_EINVALIDSPEC;
+
+on_error:
+ git_refspec__dispose(refspec);
return -1;
}
int git_refspec_parse(git_refspec **out_refspec, const char *input, int is_fetch)
{
git_refspec *refspec;
- assert(out_refspec && input);
+ GIT_ASSERT_ARG(out_refspec);
+ GIT_ASSERT_ARG(input);
*out_refspec = NULL;
int git_refspec_force(const git_refspec *refspec)
{
- assert(refspec);
+ GIT_ASSERT_ARG(refspec);
return refspec->force;
}
{
const char *from_star, *to_star;
size_t replacement_len, star_offset;
+ int error;
+
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
- git_buf_sanitize(out);
git_buf_clear(out);
/*
from_star = strchr(from, '*');
to_star = strchr(to, '*');
- assert(from_star && to_star);
+ GIT_ASSERT(from_star && to_star);
/* star offset, both in 'from' and in 'name' */
star_offset = from_star - from;
int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name)
{
- assert(out && spec && name);
- git_buf_sanitize(out);
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
if (!git_refspec_src_matches(spec, name)) {
git_error_set(GIT_ERROR_INVALID, "ref '%s' doesn't match the source", name);
int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name)
{
- assert(out && spec && name);
- git_buf_sanitize(out);
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
if (!git_refspec_dst_matches(spec, name)) {
git_error_set(GIT_ERROR_INVALID, "ref '%s' doesn't match the destination", name);
int git_refspec_is_wildcard(const git_refspec *spec)
{
- assert(spec && spec->src);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(spec->src);
return (spec->src[strlen(spec->src) - 1] == '*');
}
git_direction git_refspec_direction(const git_refspec *spec)
{
- assert(spec);
+ GIT_ASSERT_ARG(spec);
return spec->push;
}
git_remote_head key;
git_refspec *cur;
- const char* formatters[] = {
+ const char *formatters[] = {
GIT_REFS_DIR "%s",
GIT_REFS_TAGS_DIR "%s",
GIT_REFS_HEADS_DIR "%s",
NULL
};
- assert(out && spec && refs);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(refs);
cur = git__calloc(1, sizeof(git_refspec));
GIT_ERROR_CHECK_ALLOC(cur);
static int ensure_remote_name_is_valid(const char *name)
{
- int error = 0;
+ int valid, error;
+
+ error = git_remote_name_is_valid(&valid, name);
- if (!git_remote_is_valid_name(name)) {
+ if (!error && !valid) {
git_error_set(
GIT_ERROR_CONFIG,
"'%s' is not a valid remote name.", name ? name : "(null)");
if ((error = ensure_remote_name_is_valid(name)) < 0)
return error;
- if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0) {
- if (git_error_last()->klass != GIT_ERROR_NOMEMORY)
- error = GIT_EINVALIDSPEC;
-
+ if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0)
return error;
- }
git_refspec__dispose(&spec);
return error;
/*
- * "$^" is a unmatcheable regexp: it will not match anything at all, so
+ * "$^" is an unmatchable regexp: it will not match anything at all, so
* all values will be considered new and we will not replace any
* present value.
*/
const git_remote_create_options dummy_opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
int error = -1;
- assert(out && url);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(url);
if (!opts) {
opts = &dummy_opts;
struct refspec_cb_data data = { NULL };
bool optional_setting_found = false, found;
- assert(out && repo && name);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
if ((error = ensure_remote_name_is_valid(name)) < 0)
return error;
const char *git_remote_name(const git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
return remote->name;
}
git_repository *git_remote_owner(const git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
return remote->repo;
}
const char *git_remote_url(const git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
return remote->url;
}
+int git_remote_set_instance_url(git_remote *remote, const char *url)
+{
+ char *tmp;
+
+ GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(url);
+
+ if ((tmp = git__strdup(url)) == NULL)
+ return -1;
+
+ git__free(remote->url);
+ remote->url = tmp;
+
+ return 0;
+}
+
static int set_url(git_repository *repo, const char *remote, const char *pattern, const char *url)
{
git_config *cfg;
git_buf buf = GIT_BUF_INIT, canonical_url = GIT_BUF_INIT;
int error;
- assert(repo && remote);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(remote);
if ((error = ensure_remote_name_is_valid(remote)) < 0)
return error;
const char *git_remote_pushurl(const git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
return remote->pushurl;
}
-int git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url)
+int git_remote_set_instance_pushurl(git_remote *remote, const char *url)
+{
+ char *tmp;
+
+ GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(url);
+
+ if ((tmp = git__strdup(url)) == NULL)
+ return -1;
+
+ git__free(remote->pushurl);
+ remote->pushurl = tmp;
+
+ return 0;
+}
+
+int git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url)
{
return set_url(repo, remote, CONFIG_PUSHURL_FMT, url);
}
-static int resolve_url(git_buf *resolved_url, const char *url, int direction, const git_remote_callbacks *callbacks)
+static int resolve_url(
+ git_buf *resolved_url,
+ const char *url,
+ int direction,
+ const git_remote_callbacks *callbacks)
{
- int status;
+#ifdef GIT_DEPRECATE_HARD
+ GIT_UNUSED(direction);
+ GIT_UNUSED(callbacks);
+#else
+ int status, error;
if (callbacks && callbacks->resolve_url) {
git_buf_clear(resolved_url);
status = callbacks->resolve_url(resolved_url, url, direction, callbacks->payload);
if (status != GIT_PASSTHROUGH) {
git_error_set_after_callback_function(status, "git_resolve_url_cb");
- git_buf_sanitize(resolved_url);
+
+ if ((error = git_buf_sanitize(resolved_url)) < 0)
+ return error;
+
return status;
}
}
+#endif
return git_buf_sets(resolved_url, url);
}
-int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks)
+int git_remote__urlfordirection(
+ git_buf *url_out,
+ struct git_remote *remote,
+ int direction,
+ const git_remote_callbacks *callbacks)
{
const char *url = NULL;
- assert(remote);
- assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
+ GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
- if (direction == GIT_DIRECTION_FETCH) {
+ if (callbacks && callbacks->remote_ready) {
+ int status = callbacks->remote_ready(remote, direction, callbacks->payload);
+
+ if (status != 0 && status != GIT_PASSTHROUGH) {
+ git_error_set_after_callback_function(status, "git_remote_ready_cb");
+ return status;
+ }
+ }
+
+ if (direction == GIT_DIRECTION_FETCH)
url = remote->url;
- } else if (direction == GIT_DIRECTION_PUSH) {
+ else if (direction == GIT_DIRECTION_PUSH)
url = remote->pushurl ? remote->pushurl : remote->url;
- }
if (!url) {
git_error_set(GIT_ERROR_INVALID,
direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
return GIT_EINVALID;
}
+
return resolve_url(url_out, url, direction, callbacks);
}
git_credential_acquire_cb credentials = NULL;
git_transport_cb transport = NULL;
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (callbacks) {
GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (!remote->transport) {
git_error_set(GIT_ERROR_NET, "this remote has never connected");
return remote->transport->ls(out, size, remote->transport);
}
-int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
+static int lookup_config(char **out, git_config *cfg, const char *name)
{
- git_config *cfg;
git_config_entry *ce = NULL;
- git_buf val = GIT_BUF_INIT;
int error;
- assert(remote);
+ if ((error = git_config__lookup_entry(&ce, cfg, name, false)) < 0)
+ return error;
+
+ if (ce && ce->value) {
+ *out = git__strdup(ce->value);
+ GIT_ERROR_CHECK_ALLOC(*out);
+ } else {
+ error = GIT_ENOTFOUND;
+ }
- if (!proxy_url || !remote->repo)
- return -1;
+ git_config_entry_free(ce);
+ return error;
+}
- *proxy_url = NULL;
+static void url_config_trim(git_net_url *url)
+{
+ size_t len = strlen(url->path);
- if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
- return error;
+ if (url->path[len - 1] == '/') {
+ len--;
+ } else {
+ while (len && url->path[len - 1] != '/')
+ len--;
+ }
- /* Go through the possible sources for proxy configuration, from most specific
- * to least specific. */
+ url->path[len] = '\0';
+}
+
+static int http_proxy_config(char **out, git_remote *remote, git_net_url *url)
+{
+ git_config *cfg = NULL;
+ git_buf buf = GIT_BUF_INIT;
+ git_net_url lookup_url = GIT_NET_URL_INIT;
+ int error;
+
+ if ((error = git_net_url_dup(&lookup_url, url)) < 0)
+ goto done;
+
+ if (remote->repo) {
+ if ((error = git_repository_config(&cfg, remote->repo)) < 0)
+ goto done;
+ } else {
+ if ((error = git_config_open_default(&cfg)) < 0)
+ goto done;
+ }
/* remote.<name>.proxy config setting */
if (remote->name && remote->name[0]) {
- git_buf buf = GIT_BUF_INIT;
+ git_buf_clear(&buf);
- if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
- return error;
+ if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0 ||
+ (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
+ goto done;
+ }
- error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
- git_buf_dispose(&buf);
+ while (true) {
+ git_buf_clear(&buf);
- if (error < 0)
- return error;
+ if ((error = git_buf_puts(&buf, "http.")) < 0 ||
+ (error = git_net_url_fmt(&buf, &lookup_url)) < 0 ||
+ (error = git_buf_puts(&buf, ".proxy")) < 0 ||
+ (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
+ goto done;
- if (ce && ce->value) {
- *proxy_url = git__strdup(ce->value);
- goto found;
- }
+ if (! lookup_url.path[0])
+ break;
+
+ url_config_trim(&lookup_url);
}
- /* http.proxy config setting */
- if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0)
- return error;
+ git_buf_clear(&buf);
- if (ce && ce->value) {
- *proxy_url = git__strdup(ce->value);
- goto found;
- }
+ error = lookup_config(out, cfg, "http.proxy");
+
+done:
+ git_config_free(cfg);
+ git_buf_dispose(&buf);
+ git_net_url_dispose(&lookup_url);
+ return error;
+}
+
+static int http_proxy_env(char **out, git_remote *remote, git_net_url *url)
+{
+ git_buf proxy_env = GIT_BUF_INIT, no_proxy_env = GIT_BUF_INIT;
+ bool use_ssl = (strcmp(url->scheme, "https") == 0);
+ int error;
+
+ GIT_UNUSED(remote);
/* http_proxy / https_proxy environment variables */
- error = git__getenv(&val, use_ssl ? "https_proxy" : "http_proxy");
+ error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy");
/* try uppercase environment variables */
if (error == GIT_ENOTFOUND)
- error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
+ error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
- if (error < 0) {
- if (error == GIT_ENOTFOUND) {
- git_error_clear();
- error = 0;
- }
+ if (error)
+ goto done;
- return error;
- }
+ /* no_proxy/NO_PROXY environment variables */
+ error = git__getenv(&no_proxy_env, "no_proxy");
- *proxy_url = git_buf_detach(&val);
+ if (error == GIT_ENOTFOUND)
+ error = git__getenv(&no_proxy_env, "NO_PROXY");
-found:
- GIT_ERROR_CHECK_ALLOC(*proxy_url);
- git_config_entry_free(ce);
+ if (error && error != GIT_ENOTFOUND)
+ goto done;
+
+ if (!git_net_url_matches_pattern_list(url, no_proxy_env.ptr))
+ *out = git_buf_detach(&proxy_env);
+ else
+ error = GIT_ENOTFOUND;
+
+done:
+ git_buf_dispose(&proxy_env);
+ git_buf_dispose(&no_proxy_env);
+ return error;
+}
+
+int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url)
+{
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(remote);
+
+ *out = NULL;
+
+ /*
+ * Go through the possible sources for proxy configuration,
+ * Examine the various git config options first, then
+ * consult environment variables.
+ */
+ if ((error = http_proxy_config(out, remote, url)) != GIT_ENOTFOUND ||
+ (error = http_proxy_env(out, remote, url)) != GIT_ENOTFOUND)
+ return error;
return 0;
}
const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (!remote->repo) {
git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
unsigned int i;
git_remote_head *remote_ref;
- assert(update_heads && fetchspec_src);
+ GIT_ASSERT_ARG(update_heads);
+ GIT_ASSERT_ARG(fetchspec_src);
*out = NULL;
const char *ref_name;
int error = 0, update;
- assert(out && spec && ref);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(ref);
*out = NULL;
ref_name = git_reference_name(resolved_ref);
}
+ /*
+ * The ref name may be unresolvable - perhaps it's pointing to
+ * something invalid. In this case, there is no remote head for
+ * this ref.
+ */
+ if (!ref_name) {
+ error = 0;
+ goto cleanup;
+ }
+
if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0)
goto cleanup;
unsigned int i = 0;
int error = 0;
- assert(remote);
+ GIT_ASSERT_ARG(remote);
/* no heads, nothing to do */
if (update_heads->length == 0)
if (error == GIT_ENOTFOUND)
continue;
- /* if we did find a source, remove it from the candiates */
+ /* If we did find a source, remove it from the candidates. */
if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0)
goto cleanup;
git_vector *refs,
const char *log_message)
{
- int error = 0, autotag;
+ int error = 0, autotag, valid;
unsigned int i = 0;
git_buf refname = GIT_BUF_INIT;
git_oid old;
git_refspec tagspec;
git_vector update_heads;
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
return -1;
git_buf_clear(&refname);
/* Ignore malformed ref names (which also saves us from tag^{} */
- if (!git_reference_is_valid_name(head->name))
+ if (git_reference_name_is_valid(&valid, head->name) < 0)
+ goto on_error;
+
+ if (!valid)
continue;
/* If we have a tag, see if the auto-follow rules say to update it */
if (error < 0 && error != GIT_ENOTFOUND)
goto on_error;
+ if (!(error || error == GIT_ENOTFOUND)
+ && !spec->force
+ && !git_graph_descendant_of(remote->repo, &head->oid, &old))
+ continue;
+
if (error == GIT_ENOTFOUND) {
memset(&old, 0, GIT_OID_RAWSZ);
git_remote_head *head;
git_refspec *spec, *passive_spec;
size_t i, j, k;
+ int valid;
active = &remote->active_refspecs;
passive = &remote->passive_refspecs;
for (; i < refs->length; i++) {
head = git_vector_get(refs, i);
- if (!git_reference_is_valid_name(head->name))
+ if (git_reference_name_is_valid(&valid, head->name) < 0)
+ return -1;
+
+ if (!valid)
continue;
for (; j < active->length; j++) {
goto out;
}
- /* only try to do opportunisitic updates if the refpec lists differ */
+ /* Only try to do opportunistic updates if the refpec lists differ. */
if (remote->passed_refspecs)
error = opportunistic_updates(remote, callbacks, &refs, reflog_message);
int git_remote_connected(const git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (!remote->transport || !remote->transport->is_connected)
return 0;
int git_remote_stop(git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (remote->transport && remote->transport->cancel)
remote->transport->cancel(remote->transport);
int git_remote_disconnect(git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (git_remote_connected(remote))
remote->transport->close(remote->transport);
const git_indexer_progress *git_remote_stats(git_remote *remote)
{
- assert(remote);
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
return &remote->stats;
}
git_config *config;
int error;
- assert(repo && remote);
+ GIT_ASSERT_ARG(repo && remote);
if ((error = ensure_remote_name_is_valid(remote)) < 0)
return error;
git_vector problem_refspecs = GIT_VECTOR_INIT;
git_remote *remote = NULL;
- assert(out && repo && name && new_name);
+ GIT_ASSERT_ARG(out && repo && name && new_name);
if ((error = git_remote_lookup(&remote, repo, name)) < 0)
return error;
return error;
}
-int git_remote_is_valid_name(
- const char *remote_name)
+int git_remote_name_is_valid(int *valid, const char *remote_name)
{
git_buf buf = GIT_BUF_INIT;
- git_refspec refspec;
- int error = -1;
+ git_refspec refspec = {0};
+ int error;
+
+ GIT_ASSERT(valid);
+
+ *valid = 0;
if (!remote_name || *remote_name == '\0')
return 0;
- git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name);
+ if ((error = git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name)) < 0)
+ goto done;
+
error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true);
+ if (!error)
+ *valid = 1;
+ else if (error == GIT_EINVALIDSPEC)
+ error = 0;
+
+done:
git_buf_dispose(&buf);
git_refspec__dispose(&refspec);
- git_error_clear();
- return error == 0;
+ return error;
}
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname)
prefix_len = strlen("remote.");
dot = strchr(name + prefix_len, '.');
- assert(dot);
+ GIT_ASSERT_ARG_WITH_RETVAL(dot, NULL);
*len_out = dot - name - prefix_len;
return name + prefix_len;
if (strcmp(remote_name, entry->value))
continue;
- branch = name_offset(&branch_len, entry->name);
+ if ((branch = name_offset(&branch_len, entry->name)) == NULL) {
+ error = -1;
+ break;
+ }
git_buf_clear(&buf);
- if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0)
+ if ((error = git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch)) < 0)
break;
if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) {
}
git_buf_clear(&buf);
- if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0)
+ if ((error = git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch)) < 0)
break;
if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) {
{
int error;
- assert(repo && name);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
if ((error = remove_branch_config_related_entries(repo, name)) < 0 ||
(error = remove_remote_tracking(repo, name)) < 0 ||
git_buf local_default = GIT_BUF_INIT;
int error;
- assert(out);
+ GIT_ASSERT_ARG(out);
if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
goto done;
goto done;
}
- git_buf_sanitize(out);
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
/* the first one must be HEAD so if that has the symref info, we're done */
if (heads[0]->symref_target) {
const git_remote_callbacks *cbs = NULL;
git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT;
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (!remote->repo) {
git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
- assert(remote);
+ GIT_ASSERT_ARG(remote);
if (!remote->repo) {
git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
proxy = &opts->proxy_opts;
}
- assert(remote);
-
if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
return error;
git_config_entry *entry;
git_config_iterator *iter;
- assert(config);
- assert(url);
- assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
+ GIT_ASSERT_ARG_WITH_RETVAL(config, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(url, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH, NULL);
/* Add 1 to prefix/suffix length due to the additional escaped dot */
prefix_length = strlen(PREFIX) + 1;
return result.ptr;
}
+
+/* Deprecated functions */
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_remote_is_valid_name(const char *remote_name)
+{
+ int valid = 0;
+
+ git_remote_name_is_valid(&valid, remote_name);
+ return valid;
+}
+
+#endif
#include "refspec.h"
#include "vector.h"
+#include "net.h"
#define GIT_REMOTE_ORIGIN "origin"
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn);
int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
-int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url);
+int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url);
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);
git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname);
GIT_REFCOUNT_INC(odb);
}
- if ((odb = git__swap(repo->_odb, odb)) != NULL) {
+ if ((odb = git_atomic_swap(repo->_odb, odb)) != NULL) {
GIT_REFCOUNT_OWN(odb, NULL);
git_odb_free(odb);
}
GIT_REFCOUNT_INC(refdb);
}
- if ((refdb = git__swap(repo->_refdb, refdb)) != NULL) {
+ if ((refdb = git_atomic_swap(repo->_refdb, refdb)) != NULL) {
GIT_REFCOUNT_OWN(refdb, NULL);
git_refdb_free(refdb);
}
GIT_REFCOUNT_INC(config);
}
- if ((config = git__swap(repo->_config, config)) != NULL) {
+ if ((config = git_atomic_swap(repo->_config, config)) != NULL) {
GIT_REFCOUNT_OWN(config, NULL);
git_config_free(config);
}
GIT_REFCOUNT_INC(index);
}
- if ((index = git__swap(repo->_index, index)) != NULL) {
+ if ((index = git_atomic_swap(repo->_index, index)) != NULL) {
GIT_REFCOUNT_OWN(index, NULL);
git_index_free(index);
}
int git_repository__cleanup(git_repository *repo)
{
- assert(repo);
+ GIT_ASSERT_ARG(repo);
git_repository_submodule_cache_clear(repo);
git_cache_clear(&repo->objects);
git__free(repo);
}
+/* Check if we have a separate commondir (e.g. we have a worktree) */
+static int lookup_commondir(bool *separate, git_buf *commondir, git_buf *repository_path)
+{
+ git_buf common_link = GIT_BUF_INIT;
+ int error;
+
+ /*
+ * If there's no commondir file, the repository path is the
+ * common path, but it needs a trailing slash.
+ */
+ if (!git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) {
+ if ((error = git_buf_set(commondir, repository_path->ptr, repository_path->size)) == 0)
+ error = git_path_to_dir(commondir);
+
+ *separate = false;
+ goto done;
+ }
+
+ *separate = true;
+
+ if ((error = git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE)) < 0 ||
+ (error = git_futils_readbuffer(&common_link, common_link.ptr)) < 0)
+ goto done;
+
+ git_buf_rtrim(&common_link);
+ if (git_path_is_relative(common_link.ptr)) {
+ if ((error = git_buf_joinpath(commondir, repository_path->ptr, common_link.ptr)) < 0)
+ goto done;
+ } else {
+ git_buf_swap(commondir, &common_link);
+ }
+
+ git_buf_dispose(&common_link);
+
+ /* Make sure the commondir path always has a trailing slash */
+ error = git_path_prettify_dir(commondir, commondir->ptr, NULL);
+
+done:
+ return error;
+}
+
+GIT_INLINE(int) validate_repo_path(git_buf *path)
+{
+ /*
+ * The longest static path in a repository (or commondir) is the
+ * packed refs file. (Loose refs may be longer since they
+ * include the reference name, but will be validated when the
+ * path is constructed.)
+ */
+ static size_t suffix_len =
+ CONST_STRLEN("objects/pack/pack-.pack.lock") +
+ GIT_OID_HEXSZ;
+
+ return git_path_validate_filesystem_with_suffix(
+ path->ptr, path->size, suffix_len);
+}
+
/*
* Git repository open methods
*
*/
static int is_valid_repository_path(bool *out, git_buf *repository_path, git_buf *common_path)
{
+ bool separate_commondir = false;
int error;
*out = false;
- /* Check if we have a separate commondir (e.g. we have a
- * worktree) */
- if (git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) {
- git_buf common_link = GIT_BUF_INIT;
-
- if ((error = git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE)) < 0 ||
- (error = git_futils_readbuffer(&common_link, common_link.ptr)) < 0)
- return error;
-
- git_buf_rtrim(&common_link);
- if (git_path_is_relative(common_link.ptr)) {
- if ((error = git_buf_joinpath(common_path, repository_path->ptr, common_link.ptr)) < 0)
- return error;
- } else {
- git_buf_swap(common_path, &common_link);
- }
-
- git_buf_dispose(&common_link);
- }
- else {
- if ((error = git_buf_set(common_path, repository_path->ptr, repository_path->size)) < 0)
- return error;
- }
-
- /* Make sure the commondir path always has a trailing * slash */
- if (git_buf_rfind(common_path, '/') != (ssize_t)common_path->size - 1)
- if ((error = git_buf_putc(common_path, '/')) < 0)
- return error;
+ if ((error = lookup_commondir(&separate_commondir, common_path, repository_path)) < 0)
+ return error;
/* Ensure HEAD file exists */
if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
return 0;
+
/* Check files in common dir */
if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false)
return 0;
if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false)
return 0;
+ /* Ensure the repo (and commondir) are valid paths */
+ if ((error = validate_repo_path(common_path)) < 0 ||
+ (separate_commondir &&
+ (error = validate_repo_path(repository_path)) < 0))
+ return error;
+
*out = true;
return 0;
}
const char *ceil, *sep;
size_t len, max_len = 0, min_len;
- assert(path);
+ GIT_ASSERT_ARG(path);
min_len = (size_t)(git_path_root(path) + 1);
git_buf file = GIT_BUF_INIT;
size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);
- assert(path_out && file_path);
+ GIT_ASSERT_ARG(path_out);
+ GIT_ASSERT_ARG(file_path);
if (git_futils_readbuffer(&file, file_path) < 0)
return -1;
size_t len;
int err;
- assert(repo_out && wt);
+ GIT_ASSERT_ARG(repo_out);
+ GIT_ASSERT_ARG(wt);
*repo_out = NULL;
len = strlen(wt->gitlink_path);
const char *ceiling_dirs)
{
uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
+ int error;
- assert(start_path);
+ GIT_ASSERT_ARG(start_path);
- git_buf_sanitize(out);
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
return find_repo(out, NULL, NULL, NULL, start_path, flags, ceiling_dirs);
}
git_buf config_path = GIT_BUF_INIT;
git_config *cfg = NULL;
- assert(out);
+ GIT_ASSERT_ARG(out);
if ((error = git_config_new(&cfg)) < 0)
return error;
if (!error) {
GIT_REFCOUNT_OWN(config, repo);
- config = git__compare_and_swap(&repo->_config, NULL, config);
- if (config != NULL) {
+ if (git_atomic_compare_and_swap(&repo->_config, NULL, config) != NULL) {
GIT_REFCOUNT_OWN(config, NULL);
git_config_free(config);
}
int git_repository_set_config(git_repository *repo, git_config *config)
{
- assert(repo && config);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(config);
+
set_config(repo, config);
return 0;
}
{
int error = 0;
- assert(repo && out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(out);
- if (repo->_odb == NULL) {
+ *out = git_atomic_load(repo->_odb);
+ if (*out == NULL) {
git_buf odb_path = GIT_BUF_INIT;
git_odb *odb;
return error;
}
- odb = git__compare_and_swap(&repo->_odb, NULL, odb);
- if (odb != NULL) {
+ if (git_atomic_compare_and_swap(&repo->_odb, NULL, odb) != NULL) {
GIT_REFCOUNT_OWN(odb, NULL);
git_odb_free(odb);
}
git_buf_dispose(&odb_path);
+ *out = git_atomic_load(repo->_odb);
}
- *out = repo->_odb;
return error;
}
int git_repository_set_odb(git_repository *repo, git_odb *odb)
{
- assert(repo && odb);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(odb);
+
set_odb(repo, odb);
return 0;
}
{
int error = 0;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
if (repo->_refdb == NULL) {
git_refdb *refdb;
if (!error) {
GIT_REFCOUNT_OWN(refdb, repo);
- refdb = git__compare_and_swap(&repo->_refdb, NULL, refdb);
- if (refdb != NULL) {
+ if (git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb) != NULL) {
GIT_REFCOUNT_OWN(refdb, NULL);
git_refdb_free(refdb);
}
int git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
{
- assert(repo && refdb);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refdb);
+
set_refdb(repo, refdb);
return 0;
}
{
int error = 0;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
if (repo->_index == NULL) {
git_buf index_path = GIT_BUF_INIT;
if (!error) {
GIT_REFCOUNT_OWN(index, repo);
- index = git__compare_and_swap(&repo->_index, NULL, index);
- if (index != NULL) {
+ if (git_atomic_compare_and_swap(&repo->_index, NULL, index) != NULL) {
GIT_REFCOUNT_OWN(index, NULL);
git_index_free(index);
}
int git_repository_set_index(git_repository *repo, git_index *index)
{
- assert(repo);
+ GIT_ASSERT_ARG(repo);
set_index(repo, index);
return 0;
}
return 0;
}
+static const char *builtin_extensions[] = {
+ "noop"
+};
+
+static git_vector user_extensions = GIT_VECTOR_INIT;
+
static int check_valid_extension(const git_config_entry *entry, void *payload)
{
+ git_buf cfg = GIT_BUF_INIT;
+ bool reject;
+ const char *extension;
+ size_t i;
+ int error = 0;
+
GIT_UNUSED(payload);
- if (!strcmp(entry->name, "extensions.noop"))
- return 0;
+ git_vector_foreach (&user_extensions, i, extension) {
+ git_buf_clear(&cfg);
+
+ /*
+ * Users can specify that they don't want to support an
+ * extension with a '!' prefix.
+ */
+ if ((reject = (extension[0] == '!')) == true)
+ extension = &extension[1];
+
+ if ((error = git_buf_printf(&cfg, "extensions.%s", extension)) < 0)
+ goto done;
+
+ if (strcmp(entry->name, cfg.ptr) == 0) {
+ if (reject)
+ goto fail;
+
+ goto done;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) {
+ extension = builtin_extensions[i];
+ if ((error = git_buf_printf(&cfg, "extensions.%s", extension)) < 0)
+ goto done;
+
+ if (strcmp(entry->name, cfg.ptr) == 0)
+ goto done;
+ }
+
+fail:
git_error_set(GIT_ERROR_REPOSITORY, "unsupported extension name %s", entry->name);
- return -1;
+ error = -1;
+
+done:
+ git_buf_dispose(&cfg);
+ return error;
}
static int check_extensions(git_config *config, int version)
return git_config_foreach_match(config, "^extensions\\.", check_valid_extension, NULL);
}
+int git_repository__extensions(char ***out, size_t *out_len)
+{
+ git_vector extensions;
+ const char *builtin, *user;
+ char *extension;
+ size_t i, j;
+
+ if (git_vector_init(&extensions, 8, NULL) < 0)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) {
+ bool match = false;
+
+ builtin = builtin_extensions[i];
+
+ git_vector_foreach (&user_extensions, j, user) {
+ if (user[0] == '!' && strcmp(builtin, &user[1]) == 0) {
+ match = true;
+ break;
+ }
+ }
+
+ if (match)
+ continue;
+
+ if ((extension = git__strdup(builtin)) == NULL ||
+ git_vector_insert(&extensions, extension) < 0)
+ return -1;
+ }
+
+ git_vector_foreach (&user_extensions, i, user) {
+ if (user[0] == '!')
+ continue;
+
+ if ((extension = git__strdup(user)) == NULL ||
+ git_vector_insert(&extensions, extension) < 0)
+ return -1;
+ }
+
+ *out = (char **)git_vector_detach(out_len, NULL, &extensions);
+ return 0;
+}
+
+int git_repository__set_extensions(const char **extensions, size_t len)
+{
+ char *extension;
+ size_t i;
+
+ git_repository__free_extensions();
+
+ for (i = 0; i < len; i++) {
+ if ((extension = git__strdup(extensions[i])) == NULL ||
+ git_vector_insert(&user_extensions, extension) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+void git_repository__free_extensions(void)
+{
+ git_vector_free_deep(&user_extensions);
+}
+
int git_repository_create_head(const char *git_dir, const char *ref_name)
{
git_buf ref_path = GIT_BUF_INIT;
if (given) {
initial_head = given;
} else if ((error = git_config_open_default(&cfg)) >= 0 &&
- (error = git_config_get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0) {
+ (error = git_config_get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0 &&
+ *cfg_branch.ptr) {
initial_head = cfg_branch.ptr;
}
bool is_valid;
int error;
- assert(out && given_repo && opts);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(given_repo);
+ GIT_ASSERT_ARG(opts);
GIT_ERROR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
git_reference *ref = NULL;
int error;
- assert(repo && name);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
if ((error = git_repository_head_for_worktree(&ref, repo, name)) < 0)
goto out;
git_reference *head;
int error;
- assert(head_out);
+ GIT_ASSERT_ARG(head_out);
if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
return error;
git_reference *head = NULL;
int error;
- assert(out && repo && name);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
*out = NULL;
int error;
size_t i;
+ /* apply operation to repository supplied when commondir is empty, implying there's
+ * no linked worktrees to iterate, which can occur when using custom odb/refdb
+ */
+ if (!repo->commondir)
+ return cb(repo, payload);
+
if ((error = git_repository_open(&worktree_repo, repo->commondir)) < 0 ||
(error = cb(worktree_repo, payload) != 0))
goto out;
return 0;
}
-static int at_least_one_cb(const char *refname, void *payload)
-{
- GIT_UNUSED(refname);
- GIT_UNUSED(payload);
- return GIT_PASSTHROUGH;
-}
-
static int repo_contains_no_reference(git_repository *repo)
{
- int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL);
+ git_reference_iterator *iter;
+ const char *refname;
+ int error;
- if (error == GIT_PASSTHROUGH)
- return 0;
+ if ((error = git_reference_iterator_new(&iter, repo)) < 0)
+ return error;
- if (!error)
+ error = git_reference_next_name(&refname, iter);
+ git_reference_iterator_free(iter);
+
+ if (error == GIT_ITEROVER)
return 1;
return error;
git_config *config;
git_config_entry *entry = NULL;
const char *branch;
- int error;
+ int valid, error;
if ((error = git_repository_config__weakptr(&config, repo)) < 0)
return error;
- if ((error = git_config_get_entry(&entry, config, "init.defaultbranch")) == 0) {
+ if ((error = git_config_get_entry(&entry, config, "init.defaultbranch")) == 0 &&
+ *entry->value) {
branch = entry->value;
}
- else if (error == GIT_ENOTFOUND) {
+ else if (!error || error == GIT_ENOTFOUND) {
branch = GIT_BRANCH_DEFAULT;
}
else {
}
if ((error = git_buf_puts(out, GIT_REFS_HEADS_DIR)) < 0 ||
- (error = git_buf_puts(out, branch)) < 0)
+ (error = git_buf_puts(out, branch)) < 0 ||
+ (error = git_reference_name_is_valid(&valid, out->ptr)) < 0)
goto done;
- if (!git_reference_is_valid_name(out->ptr)) {
- git_error_set(GIT_ERROR_INVALID, "the value of init.defaultBranch is not a valid reference name");
+ if (!valid) {
+ git_error_set(GIT_ERROR_INVALID, "the value of init.defaultBranch is not a valid branch name");
error = -1;
}
const char *git_repository_path(const git_repository *repo)
{
- assert(repo);
+ GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
return repo->gitdir;
}
const char *git_repository_workdir(const git_repository *repo)
{
- assert(repo);
+ GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
if (repo->is_bare)
return NULL;
return repo->workdir;
}
+int git_repository_workdir_path(
+ git_buf *out, git_repository *repo, const char *path)
+{
+ int error;
+
+ if (!repo->workdir) {
+ git_error_set(GIT_ERROR_REPOSITORY, "repository has no working directory");
+ return GIT_EBAREREPO;
+ }
+
+ if (!(error = git_buf_joinpath(out, repo->workdir, path)))
+ error = git_path_validate_workdir_buf(repo, out);
+
+ return error;
+}
+
const char *git_repository_commondir(const git_repository *repo)
{
- assert(repo);
+ GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
return repo->commondir;
}
int error = 0;
git_buf path = GIT_BUF_INIT;
- assert(repo && workdir);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(workdir);
if (git_path_prettify_dir(&path, workdir, NULL) < 0)
return -1;
int git_repository_is_bare(const git_repository *repo)
{
- assert(repo);
+ GIT_ASSERT_ARG(repo);
return repo->is_bare;
}
int git_repository_is_worktree(const git_repository *repo)
{
- assert(repo);
+ GIT_ASSERT_ARG(repo);
return repo->is_worktree;
}
int error;
git_config *config;
- assert(repo);
+ GIT_ASSERT_ARG(repo);
if (repo->is_bare)
return 0;
struct stat st;
int error;
- git_buf_sanitize(out);
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0)
return -1;
git_file fd = -1;
uint64_t len;
git_buf full_path = GIT_BUF_INIT;
+ const char *workdir = git_repository_workdir(repo);
- assert(out && path && repo); /* as_path can be NULL */
+ /* as_path can be NULL */
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(path);
+ GIT_ASSERT_ARG(repo);
- /* At some point, it would be nice if repo could be NULL to just
- * apply filter rules defined in system and global files, but for
- * now that is not possible because git_filters_load() needs it.
- */
-
- error = git_path_join_unrooted(
- &full_path, path, git_repository_workdir(repo), NULL);
- if (error < 0)
+ if ((error = git_path_join_unrooted(&full_path, path, workdir, NULL)) < 0 ||
+ (error = git_path_validate_workdir_buf(repo, &full_path)) < 0)
return error;
- if (!as_path)
- as_path = path;
+ /*
+ * NULL as_path means that we should derive it from the
+ * given path.
+ */
+ if (!as_path) {
+ if (workdir && !git__prefixcmp(full_path.ptr, workdir))
+ as_path = full_path.ptr + strlen(workdir);
+ else
+ as_path = "";
+ }
/* passing empty string for "as_path" indicated --no-filters */
if (strlen(as_path) > 0) {
error = git_filter_list_load(
&fl, repo, NULL, as_path,
GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
+
if (error < 0)
return error;
- } else {
- error = 0;
}
/* at this point, error is a count of the number of loaded filters */
git_object *object = NULL, *peeled = NULL;
git_reference *new_head = NULL, *current = NULL;
- assert(repo && id);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(id);
if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0)
return error;
}
int git_repository_set_head(
- git_repository* repo,
- const char* refname)
+ git_repository *repo,
+ const char *refname)
{
git_reference *ref = NULL, *current = NULL, *new_head = NULL;
git_buf log_message = GIT_BUF_INIT;
int error;
- assert(repo && refname);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0)
return error;
}
int git_repository_set_head_detached(
- git_repository* repo,
- const git_oid* commitish)
+ git_repository *repo,
+ const git_oid *commitish)
{
return detach(repo, commitish, NULL);
}
git_repository *repo,
const git_annotated_commit *commitish)
{
- assert(repo && commitish);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(commitish);
return detach(repo, git_annotated_commit_id(commitish), commitish->description);
}
-int git_repository_detach_head(git_repository* repo)
+int git_repository_detach_head(git_repository *repo)
{
git_reference *old_head = NULL, *new_head = NULL, *current = NULL;
git_object *object = NULL;
git_buf log_message = GIT_BUF_INIT;
int error;
- assert(repo);
+ GIT_ASSERT_ARG(repo);
if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0)
return error;
git_buf repo_path = GIT_BUF_INIT;
int state = GIT_REPOSITORY_STATE_NONE;
- assert(repo);
+ GIT_ASSERT_ARG(repo);
if (git_buf_puts(&repo_path, repo->gitdir) < 0)
return -1;
int git_repository_state_cleanup(git_repository *repo)
{
- assert(repo);
+ GIT_ASSERT_ARG(repo);
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
GIT_ERROR_CHECK_ALLOC(tmp_email);
}
- tmp_name = git__swap(repo->ident_name, tmp_name);
- tmp_email = git__swap(repo->ident_email, tmp_email);
+ tmp_name = git_atomic_swap(repo->ident_name, tmp_name);
+ tmp_email = git_atomic_swap(repo->ident_email, tmp_email);
git__free(tmp_name);
git__free(tmp_email);
int git_repository_submodule_cache_all(git_repository *repo)
{
- int error;
-
- assert(repo);
-
- if ((error = git_strmap_new(&repo->submodule_cache)))
- return error;
-
- error = git_submodule__map(repo, repo->submodule_cache);
- return error;
+ GIT_ASSERT_ARG(repo);
+ return git_submodule_cache_init(&repo->submodule_cache, repo);
}
int git_repository_submodule_cache_clear(git_repository *repo)
{
- git_submodule *sm;
- assert(repo);
- if (repo->submodule_cache == NULL) {
- return 0;
- }
- git_strmap_foreach_value(repo->submodule_cache, sm, {
- git_submodule_free(sm);
- });
- git_strmap_free(repo->submodule_cache);
- repo->submodule_cache = 0;
- return 0;
+ int error = 0;
+ GIT_ASSERT_ARG(repo);
+
+ error = git_submodule_cache_free(repo->submodule_cache);
+ repo->submodule_cache = NULL;
+ return error;
}
GIT_CONFIGMAP_PROTECTHFS, /* core.protectHFS */
GIT_CONFIGMAP_PROTECTNTFS, /* core.protectNTFS */
GIT_CONFIGMAP_FSYNCOBJECTFILES, /* core.fsyncObjectFiles */
+ GIT_CONFIGMAP_LONGPATHS, /* core.longpaths */
GIT_CONFIGMAP_CACHE_MAX
} git_configmap_item;
GIT_PROTECTNTFS_DEFAULT = GIT_CONFIGMAP_TRUE,
/* core.fsyncObjectFiles */
GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CONFIGMAP_FALSE,
+ /* core.longpaths */
+ GIT_LONGPATHS_DEFAULT = GIT_CONFIGMAP_FALSE,
} git_configmap_value;
/* internal repository init flags */
unsigned int lru_counter;
- git_atomic attr_session_key;
+ git_atomic32 attr_session_key;
- git_configmap_value configmap_cache[GIT_CONFIGMAP_CACHE_MAX];
+ intptr_t configmap_cache[GIT_CONFIGMAP_CACHE_MAX];
git_strmap *submodule_cache;
};
*/
int git_repository_initialbranch(git_buf *out, git_repository *repo);
+/*
+ * Given a relative `path`, this makes it absolute based on the
+ * repository's working directory. This will perform validation
+ * to ensure that the path is not longer than MAX_PATH on Windows
+ * (unless `core.longpaths` is set in the repo config).
+ */
+int git_repository_workdir_path(git_buf *out, git_repository *repo, const char *path);
+
+int git_repository__extensions(char ***out, size_t *out_len);
+int git_repository__set_extensions(const char **extensions, size_t len);
+void git_repository__free_extensions(void);
+
#endif
int git_reset_default(
git_repository *repo,
const git_object *target,
- const git_strarray* pathspecs)
+ const git_strarray *pathspecs)
{
git_object *commit = NULL;
git_tree *tree = NULL;
int error;
git_index *index = NULL;
- assert(pathspecs != NULL && pathspecs->count > 0);
+ GIT_ASSERT_ARG(pathspecs && pathspecs->count > 0);
memset(&entry, 0, sizeof(git_index_entry));
for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
const git_diff_delta *delta = git_diff_get_delta(diff, i);
- assert(delta->status == GIT_DELTA_ADDED ||
- delta->status == GIT_DELTA_MODIFIED ||
- delta->status == GIT_DELTA_CONFLICTED ||
- delta->status == GIT_DELTA_DELETED);
+ GIT_ASSERT(delta->status == GIT_DELTA_ADDED ||
+ delta->status == GIT_DELTA_MODIFIED ||
+ delta->status == GIT_DELTA_CONFLICTED ||
+ delta->status == GIT_DELTA_DELETED);
error = git_index_conflict_remove(index, delta->old_file.path);
if (error < 0) {
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_buf log_message = GIT_BUF_INIT;
- assert(repo && target);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(target);
if (checkout_opts)
opts = *checkout_opts;
git_tree *parent_tree = NULL, *our_tree = NULL, *revert_tree = NULL;
int parent = 0, error = 0;
- assert(out && repo && revert_commit && our_commit);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(revert_commit);
+ GIT_ASSERT_ARG(our_commit);
if (git_commit_parentcount(revert_commit) > 1) {
if (!mainline)
git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
int error;
- assert(repo && commit);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(commit);
GIT_ERROR_CHECK_VERSION(given_opts, GIT_REVERT_OPTIONS_VERSION, "git_revert_options");
#include "git2.h"
-static int maybe_sha_or_abbrev(git_object** out, git_repository *repo, const char *spec, size_t speclen)
+static int maybe_sha_or_abbrev(git_object **out, git_repository *repo, const char *spec, size_t speclen)
{
git_oid oid;
return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJECT_ANY);
}
-static int maybe_sha(git_object** out, git_repository *repo, const char *spec)
+static int maybe_sha(git_object **out, git_repository *repo, const char *spec)
{
size_t speclen = strlen(spec);
return maybe_sha_or_abbrev(out, repo, spec, speclen);
}
-static int maybe_abbrev(git_object** out, git_repository *repo, const char *spec)
+static int maybe_abbrev(git_object **out, git_repository *repo, const char *spec)
{
size_t speclen = strlen(spec);
return error;
}
-static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository* repo, const char *curly_braces_content)
+static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository *repo, const char *curly_braces_content)
{
bool is_numeric;
int parsed = 0, error = -1;
git_buf identifier = GIT_BUF_INIT;
git_time_t timestamp;
- assert(*out == NULL);
+ GIT_ASSERT(*out == NULL);
if (git_buf_put(&identifier, spec, identifier_len) < 0)
return -1;
{
git_buf_clear(buf);
- assert(spec[*pos] == '^' || spec[*pos] == '@');
+ GIT_ASSERT_ARG(spec[*pos] == '^' || spec[*pos] == '@');
(*pos)++;
{
git_buf_clear(buf);
- assert(spec[*pos] == ':');
+ GIT_ASSERT_ARG(spec[*pos] == ':');
(*pos)++;
int parsed, accumulated;
char kind = spec[*pos];
- assert(spec[*pos] == '^' || spec[*pos] == '~');
+ GIT_ASSERT_ARG(spec[*pos] == '^' || spec[*pos] == '~');
accumulated = 0;
bool should_return_reference = true;
- assert(object_out && reference_out && repo && spec);
+ GIT_ASSERT_ARG(object_out);
+ GIT_ASSERT_ARG(reference_out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(spec);
*object_out = NULL;
*reference_out = NULL;
const char *dotdot;
int error = 0;
- assert(revspec && repo && spec);
+ GIT_ASSERT_ARG(revspec);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(spec);
memset(revspec, 0x0, sizeof(*revspec));
if ((dotdot = strstr(spec, "..")) != NULL) {
char *lstr;
const char *rstr;
- revspec->flags = GIT_REVPARSE_RANGE;
+ revspec->flags = GIT_REVSPEC_RANGE;
/*
* Following git.git, don't allow '..' because it makes command line
lstr = git__substrdup(spec, dotdot - spec);
rstr = dotdot + 2;
if (dotdot[2] == '.') {
- revspec->flags |= GIT_REVPARSE_MERGE_BASE;
+ revspec->flags |= GIT_REVSPEC_MERGE_BASE;
rstr++;
}
git__free((void*)lstr);
} else {
- revspec->flags = GIT_REVPARSE_SINGLE;
+ revspec->flags = GIT_REVSPEC_SINGLE;
error = git_revparse_single(&revspec->from, repo, spec);
}
{
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- assert(walk && oid);
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(oid);
return git_revwalk__push_commit(walk, oid, &opts);
}
int git_revwalk_hide(git_revwalk *walk, const git_oid *oid)
{
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- assert(walk && oid);
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(oid);
opts.uninteresting = 1;
return git_revwalk__push_commit(walk, oid, &opts);
git_reference_iterator *iter;
size_t wildcard;
- assert(walk && glob);
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(glob);
if (given_opts)
memcpy(&opts, given_opts, sizeof(opts));
int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
{
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- assert(walk && glob);
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(glob);
return git_revwalk__push_glob(walk, glob, &opts);
}
int git_revwalk_hide_glob(git_revwalk *walk, const char *glob)
{
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- assert(walk && glob);
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(glob);
opts.uninteresting = 1;
return git_revwalk__push_glob(walk, glob, &opts);
int git_revwalk_push_head(git_revwalk *walk)
{
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- assert(walk);
+
+ GIT_ASSERT_ARG(walk);
return git_revwalk__push_ref(walk, GIT_HEAD_FILE, &opts);
}
int git_revwalk_hide_head(git_revwalk *walk)
{
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- assert(walk);
+
+ GIT_ASSERT_ARG(walk);
opts.uninteresting = 1;
return git_revwalk__push_ref(walk, GIT_HEAD_FILE, &opts);
int git_revwalk_push_ref(git_revwalk *walk, const char *refname)
{
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- assert(walk && refname);
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(refname);
return git_revwalk__push_ref(walk, refname, &opts);
}
goto out;
}
- if (revspec.flags & GIT_REVPARSE_MERGE_BASE) {
+ if (revspec.flags & GIT_REVSPEC_MERGE_BASE) {
/* TODO: support "<commit>...<commit>" */
git_error_set(GIT_ERROR_INVALID, "symmetric differences not implemented in revwalk");
error = GIT_EINVALIDSPEC;
int git_revwalk_hide_ref(git_revwalk *walk, const char *refname)
{
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- assert(walk && refname);
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(refname);
+
opts.uninteresting = 1;
return git_revwalk__push_ref(walk, refname, &opts);
}
git_repository *git_revwalk_repository(git_revwalk *walk)
{
- assert(walk);
+ GIT_ASSERT_ARG_WITH_RETVAL(walk, NULL);
+
return walk->repo;
}
int git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode)
{
- assert(walk);
+ GIT_ASSERT_ARG(walk);
if (walk->walking)
git_revwalk_reset(walk);
int error;
git_commit_list_node *next;
- assert(walk && oid);
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(oid);
if (!walk->walking) {
if ((error = prepare_walk(walk)) < 0)
{
git_commit_list_node *commit;
- assert(walk);
+ GIT_ASSERT_ARG(walk);
git_oidmap_foreach_value(walk->commits, commit, {
commit->seen = 0;
git_revwalk_hide_cb hide_cb,
void *payload)
{
- assert(walk);
+ GIT_ASSERT_ARG(walk);
if (walk->walking)
git_revwalk_reset(walk);
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "runtime.h"
+
+static git_runtime_shutdown_fn shutdown_callback[32];
+static git_atomic32 shutdown_callback_count;
+
+static git_atomic32 init_count;
+
+static int init_common(git_runtime_init_fn init_fns[], size_t cnt)
+{
+ size_t i;
+ int ret;
+
+ /* Initialize subsystems that have global state */
+ for (i = 0; i < cnt; i++) {
+ if ((ret = init_fns[i]()) != 0)
+ break;
+ }
+
+ GIT_MEMORY_BARRIER;
+
+ return ret;
+}
+
+static void shutdown_common(void)
+{
+ git_runtime_shutdown_fn cb;
+ int pos;
+
+ for (pos = git_atomic32_get(&shutdown_callback_count);
+ pos > 0;
+ pos = git_atomic32_dec(&shutdown_callback_count)) {
+ cb = git_atomic_swap(shutdown_callback[pos - 1], NULL);
+
+ if (cb != NULL)
+ cb();
+ }
+}
+
+int git_runtime_shutdown_register(git_runtime_shutdown_fn callback)
+{
+ int count = git_atomic32_inc(&shutdown_callback_count);
+
+ if (count > (int)ARRAY_SIZE(shutdown_callback) || count == 0) {
+ git_error_set(GIT_ERROR_INVALID,
+ "too many shutdown callbacks registered");
+ git_atomic32_dec(&shutdown_callback_count);
+ return -1;
+ }
+
+ shutdown_callback[count - 1] = callback;
+
+ return 0;
+}
+
+#if defined(GIT_THREADS) && defined(GIT_WIN32)
+
+/*
+ * On Win32, we use a spinlock to provide locking semantics. This is
+ * lighter-weight than a proper critical section.
+ */
+static volatile LONG init_spinlock = 0;
+
+GIT_INLINE(int) init_lock(void)
+{
+ while (InterlockedCompareExchange(&init_spinlock, 1, 0)) { Sleep(0); }
+ return 0;
+}
+
+GIT_INLINE(int) init_unlock(void)
+{
+ InterlockedExchange(&init_spinlock, 0);
+ return 0;
+}
+
+#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
+
+/*
+ * On POSIX, we need to use a proper mutex for locking. We might prefer
+ * a spinlock here, too, but there's no static initializer for a
+ * pthread_spinlock_t.
+ */
+static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+GIT_INLINE(int) init_lock(void)
+{
+ return pthread_mutex_lock(&init_mutex) == 0 ? 0 : -1;
+}
+
+GIT_INLINE(int) init_unlock(void)
+{
+ return pthread_mutex_unlock(&init_mutex) == 0 ? 0 : -1;
+}
+
+#elif defined(GIT_THREADS)
+# error unknown threading model
+#else
+
+# define init_lock() git__noop()
+# define init_unlock() git__noop()
+
+#endif
+
+int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt)
+{
+ int ret;
+
+ if (init_lock() < 0)
+ return -1;
+
+ /* Only do work on a 0 -> 1 transition of the refcount */
+ if ((ret = git_atomic32_inc(&init_count)) == 1) {
+ if (init_common(init_fns, cnt) < 0)
+ ret = -1;
+ }
+
+ if (init_unlock() < 0)
+ return -1;
+
+ return ret;
+}
+
+int git_runtime_init_count(void)
+{
+ int ret;
+
+ if (init_lock() < 0)
+ return -1;
+
+ ret = git_atomic32_get(&init_count);
+
+ if (init_unlock() < 0)
+ return -1;
+
+ return ret;
+}
+
+int git_runtime_shutdown(void)
+{
+ int ret;
+
+ /* Enter the lock */
+ if (init_lock() < 0)
+ return -1;
+
+ /* Only do work on a 1 -> 0 transition of the refcount */
+ if ((ret = git_atomic32_dec(&init_count)) == 0)
+ shutdown_common();
+
+ /* Exit the lock */
+ if (init_unlock() < 0)
+ return -1;
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_runtime_h__
+#define INCLUDE_runtime_h__
+
+#include "common.h"
+
+typedef int (*git_runtime_init_fn)(void);
+typedef void (*git_runtime_shutdown_fn)(void);
+
+/**
+ * Start up a new runtime. If this is the first time that this
+ * function is called within the context of the current library
+ * or executable, then the given `init_fns` will be invoked. If
+ * it is not the first time, they will be ignored.
+ *
+ * The given initialization functions _may_ register shutdown
+ * handlers using `git_runtime_shutdown_register` to be notified
+ * when the runtime is shutdown.
+ *
+ * @param init_fns The list of initialization functions to call
+ * @param cnt The number of init_fns
+ * @return The number of initializations performed (including this one) or an error
+ */
+int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt);
+
+/*
+ * Returns the number of initializations active (the number of calls to
+ * `git_runtime_init` minus the number of calls sto `git_runtime_shutdown`).
+ * If 0, the runtime is not currently initialized.
+ *
+ * @return The number of initializations performed or an error
+ */
+int git_runtime_init_count(void);
+
+/**
+ * Shut down the runtime. If this is the last shutdown call,
+ * such that there are no remaining `init` calls, then any
+ * shutdown hooks that have been registered will be invoked.
+ *
+ * The number of outstanding initializations will be returned.
+ * If this number is 0, then the runtime is shutdown.
+ *
+ * @return The number of outstanding initializations (after this one) or an error
+ */
+int git_runtime_shutdown(void);
+
+/**
+ * Register a shutdown handler for this runtime. This should be done
+ * by a function invoked by `git_runtime_init` to ensure that the
+ * appropriate locks are taken.
+ *
+ * @param callback The shutdown handler callback
+ * @return 0 or an error code
+ */
+int git_runtime_shutdown_register(git_runtime_shutdown_fn callback);
+
+#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-
-#ifdef GIT_OPENSSL
-# include <openssl/err.h>
-#endif
-
-#ifdef GIT_MBEDTLS
-# include <mbedtls/error.h>
-#endif
-
-#include <git2.h>
-#include "alloc.h"
-#include "sysdir.h"
-#include "cache.h"
-#include "global.h"
-#include "object.h"
-#include "odb.h"
-#include "refs.h"
-#include "index.h"
-#include "transports/smart.h"
-#include "transports/http.h"
-#include "streams/openssl.h"
-#include "streams/mbedtls.h"
-
-int git_libgit2_version(int *major, int *minor, int *rev)
-{
- *major = LIBGIT2_VER_MAJOR;
- *minor = LIBGIT2_VER_MINOR;
- *rev = LIBGIT2_VER_REVISION;
-
- return 0;
-}
-
-int git_libgit2_features(void)
-{
- return 0
-#ifdef GIT_THREADS
- | GIT_FEATURE_THREADS
-#endif
-#ifdef GIT_HTTPS
- | GIT_FEATURE_HTTPS
-#endif
-#if defined(GIT_SSH)
- | GIT_FEATURE_SSH
-#endif
-#if defined(GIT_USE_NSEC)
- | GIT_FEATURE_NSEC
-#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;
-
-static int config_level_to_sysdir(int config_level)
-{
- int val = -1;
-
- switch (config_level) {
- case GIT_CONFIG_LEVEL_SYSTEM:
- val = GIT_SYSDIR_SYSTEM;
- break;
- case GIT_CONFIG_LEVEL_XDG:
- val = GIT_SYSDIR_XDG;
- break;
- case GIT_CONFIG_LEVEL_GLOBAL:
- val = GIT_SYSDIR_GLOBAL;
- break;
- case GIT_CONFIG_LEVEL_PROGRAMDATA:
- val = GIT_SYSDIR_PROGRAMDATA;
- break;
- default:
- git_error_set(
- GIT_ERROR_INVALID, "invalid config path selector %d", config_level);
- }
-
- return val;
-}
-
-extern char *git__user_agent;
-extern char *git__ssl_ciphers;
-
-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:
- if ((error = config_level_to_sysdir(va_arg(ap, int))) >= 0) {
- git_buf *out = va_arg(ap, git_buf *);
- const git_buf *tmp;
-
- git_buf_sanitize(out);
- if ((error = git_sysdir_get(&tmp, error)) < 0)
- break;
-
- error = git_buf_sets(out, tmp->ptr);
- }
- break;
-
- case GIT_OPT_SET_SEARCH_PATH:
- if ((error = config_level_to_sysdir(va_arg(ap, int))) >= 0)
- error = git_sysdir_set(error, 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 *);
- const git_buf *tmp;
-
- git_buf_sanitize(out);
- if ((error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0)
- break;
-
- error = git_buf_sets(out, tmp->ptr);
- }
- 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 *);
- if (file)
- error = git_mbedtls__set_cert_location(file, 0);
- if (error && path)
- error = git_mbedtls__set_cert_location(path, 1);
- }
-#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_buf_sanitize(out);
- error = git_buf_sets(out, git__user_agent);
- }
- 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;
-
- default:
- git_error_set(GIT_ERROR_INVALID, "invalid option key");
- error = -1;
- }
-
- va_end(ap);
-
- return error;
-}
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+extern int git_settings_global_init(void);
+
+extern const char *git_libgit2__user_agent(void);
+extern const char *git_libgit2__ssl_ciphers(void);
{
git_signature *p = NULL;
- assert(name && email);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(email);
*sig_out = NULL;
const char *buf_end;
int error;
- assert(out && buf);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(buf);
*out = NULL;
int offset, hours, mins;
char sign;
- assert(buf && sig);
-
offset = sig->when.offset;
sign = (sig->when.offset < 0 || sig->when.sign == '-') ? '-' : '+';
bool git_signature__equal(const git_signature *one, const git_signature *two)
{
- assert(one && two);
+ GIT_ASSERT_ARG(one);
+ GIT_ASSERT_ARG(two);
return
git__strcmp(one->name, two->name) == 0 &&
#include "util.h"
#include "futils.h"
#include "vector.h"
-#include "thread-utils.h"
+#include "thread.h"
#include "pool.h"
#include "strmap.h"
* may be NULL. The cache makes it easy to load this and check
* if it has been modified since the last load and/or write.
*/
-int git_sortedcache_new(
+GIT_WARN_UNUSED_RESULT int git_sortedcache_new(
git_sortedcache **out,
size_t item_path_offset, /* use offsetof(struct, path-field) macro */
git_sortedcache_free_item_fn free_item,
* - `copy_item` can be NULL to just use memcpy
* - if `lock`, grabs read lock on `src` during copy and releases after
*/
-int git_sortedcache_copy(
+GIT_WARN_UNUSED_RESULT int git_sortedcache_copy(
git_sortedcache **out,
git_sortedcache *src,
bool lock,
*/
/* Lock sortedcache for write */
-int git_sortedcache_wlock(git_sortedcache *sc);
+GIT_WARN_UNUSED_RESULT int git_sortedcache_wlock(git_sortedcache *sc);
/* Unlock sorted cache when done with write */
void git_sortedcache_wunlock(git_sortedcache *sc);
*
* @return 0 if up-to-date, 1 if out-of-date, <0 on error
*/
-int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf);
+GIT_WARN_UNUSED_RESULT int git_sortedcache_lockandload(
+ git_sortedcache *sc, git_buf *buf);
/* Refresh file timestamp after write completes
* You should already be holding the write lock when you call this.
* If `wlock` is true, grabs write lock and releases when done, otherwise
* you should already be holding a write lock when you call this.
*/
-int git_sortedcache_clear(git_sortedcache *sc, bool wlock);
+GIT_WARN_UNUSED_RESULT int git_sortedcache_clear(
+ git_sortedcache *sc, bool wlock);
/* Find and/or insert item, returning pointer to item data.
* You should already be holding the write lock when you call this.
*/
-int git_sortedcache_upsert(
+GIT_WARN_UNUSED_RESULT int git_sortedcache_upsert(
void **out, git_sortedcache *sc, const char *key);
/* Removes entry at pos from cache
*/
/* Lock sortedcache for read */
-int git_sortedcache_rlock(git_sortedcache *sc);
+GIT_WARN_UNUSED_RESULT int git_sortedcache_rlock(git_sortedcache *sc);
/* Unlock sorted cache when done with read */
void git_sortedcache_runlock(git_sortedcache *sc);
return git_buf_oom(out) ? -1 : 0;
}
-static int append_commit_description(git_buf *out, git_commit* commit)
+static int append_commit_description(git_buf *out, git_commit *commit)
{
const char *summary = git_commit_summary(commit);
GIT_ERROR_CHECK_ALLOC(summary);
git_buf msg = GIT_BUF_INIT;
int error;
- assert(out && repo && stasher);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(stasher);
if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
return error;
size_t git_status_list_entrycount(git_status_list *status)
{
- assert(status);
+ GIT_ASSERT_ARG_WITH_RETVAL(status, 0);
return status->paired.length;
}
const git_status_entry *git_status_byindex(git_status_list *status, size_t i)
{
- assert(status);
+ GIT_ASSERT_ARG_WITH_RETVAL(status, NULL);
return git_vector_get(&status->paired, i);
}
struct status_file_info sfi = {0};
git_index *index;
- assert(status_flags && repo && path);
+ GIT_ASSERT_ARG(status_flags);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(path);
if ((error = git_repository_index__weakptr(&index, repo)) < 0)
return error;
int git_status_list_get_perfdata(
git_diff_perfdata *out, const git_status_list *status)
{
- assert(out);
+ GIT_ASSERT_ARG(out);
+
GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
out->stat_calls = 0;
{
size_t i;
- assert(tgt && src);
+ GIT_ASSERT_ARG(tgt);
+ GIT_ASSERT_ARG(src);
memset(tgt, 0, sizeof(*tgt));
#include <ctype.h>
-#include "global.h"
+#include "runtime.h"
#include "stream.h"
#include "streams/socket.h"
#include "netops.h"
}
}
-int git_mbedtls__set_cert_location(const char *path, int is_dir);
-
int git_mbedtls_stream_global_init(void)
{
int loaded = 0;
/* load default certificates */
if (crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
- loaded = (git_mbedtls__set_cert_location(crtpath, 0) == 0);
+ 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(crtpath, 1) == 0);
-
- git__on_shutdown(shutdown_ssl);
+ loaded = (git_mbedtls__set_cert_location(NULL, crtpath) == 0);
- return 0;
+ return git_runtime_shutdown_register(shutdown_ssl);
cleanup:
mbedtls_ctr_drbg_free(ctr_drbg);
char errbuf[512];
int ret = -1;
- assert(error != MBEDTLS_ERR_SSL_WANT_READ);
- assert(error != MBEDTLS_ERR_SSL_WANT_WRITE);
+ GIT_ASSERT(error != MBEDTLS_ERR_SSL_WANT_READ);
+ GIT_ASSERT(error != MBEDTLS_ERR_SSL_WANT_WRITE);
if (error != 0)
mbedtls_strerror( error, errbuf, 512 );
git_stream *stream;
int error;
- assert(out && host && port);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
if ((error = git_socket_stream_new(&stream, host, port)) < 0)
return error;
return error;
}
-int git_mbedtls__set_cert_location(const char *path, int is_dir)
+int git_mbedtls__set_cert_location(const char *file, const char *path)
{
int ret = 0;
char errbuf[512];
mbedtls_x509_crt *cacert;
- assert(path != NULL);
+ GIT_ASSERT_ARG(file || path);
cacert = git__malloc(sizeof(mbedtls_x509_crt));
GIT_ERROR_CHECK_ALLOC(cacert);
mbedtls_x509_crt_init(cacert);
- if (is_dir) {
+ if (file)
+ ret = mbedtls_x509_crt_parse_file(cacert, file);
+ if (ret >= 0 && path)
ret = mbedtls_x509_crt_parse_path(cacert, path);
- } else {
- ret = mbedtls_x509_crt_parse_file(cacert, path);
- }
/* mbedtls_x509_crt_parse_path returns the number of invalid certs on success */
if (ret < 0) {
mbedtls_x509_crt_free(cacert);
extern int git_mbedtls_stream_global_init(void);
#ifdef GIT_MBEDTLS
-extern int git_mbedtls__set_cert_location(const char *path, int is_dir);
+extern int git_mbedtls__set_cert_location(const char *file, const char *path);
extern int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port);
extern int git_mbedtls_stream_wrap(git_stream **out, git_stream *in, const char *host);
*/
#include "streams/openssl.h"
+#include "streams/openssl_legacy.h"
+#include "streams/openssl_dynamic.h"
#ifdef GIT_OPENSSL
#include <ctype.h>
-#include "global.h"
+#include "common.h"
+#include "runtime.h"
+#include "settings.h"
#include "posix.h"
#include "stream.h"
#include "streams/socket.h"
# include <netinet/in.h>
#endif
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/x509v3.h>
-#include <openssl/bio.h>
+#ifndef GIT_OPENSSL_DYNAMIC
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+#endif
SSL_CTX *git__ssl_ctx;
#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
-#if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \
- (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
-# define OPENSSL_LEGACY_API
-#endif
-
-/*
- * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
- * which do not exist in previous versions. We define these inline functions so
- * we can program against the interface instead of littering the implementation
- * with ifdefs. We do the same for OPENSSL_init_ssl.
- */
-#if defined(OPENSSL_LEGACY_API)
-static int OPENSSL_init_ssl(int opts, void *settings)
-{
- GIT_UNUSED(opts);
- GIT_UNUSED(settings);
- SSL_load_error_strings();
- OpenSSL_add_ssl_algorithms();
- return 0;
-}
-
-static BIO_METHOD* BIO_meth_new(int type, const char *name)
-{
- BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
- if (!meth) {
- return NULL;
- }
-
- meth->type = type;
- meth->name = name;
-
- return meth;
-}
-
-static void BIO_meth_free(BIO_METHOD *biom)
-{
- git__free(biom);
-}
-
-static int BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
-{
- biom->bwrite = write;
- return 1;
-}
-
-static int BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
-{
- biom->bread = read;
- return 1;
-}
-
-static int BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
-{
- biom->bputs = puts;
- return 1;
-}
-
-static int BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
-
-{
- biom->bgets = gets;
- return 1;
-}
-
-static int BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
-{
- biom->ctrl = ctrl;
- return 1;
-}
-
-static int BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
-{
- biom->create = create;
- return 1;
-}
-
-static int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
-{
- biom->destroy = destroy;
- return 1;
-}
-
-static int BIO_get_new_index(void)
-{
- /* This exists as of 1.1 so before we'd just have 0 */
- return 0;
-}
-
-static void BIO_set_init(BIO *b, int init)
-{
- b->init = init;
-}
-
-static void BIO_set_data(BIO *a, void *ptr)
-{
- a->ptr = ptr;
-}
-
-static void *BIO_get_data(BIO *a)
-{
- return a->ptr;
-}
-
-static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x)
-{
- return ASN1_STRING_data((ASN1_STRING *)x);
-}
-
-# if defined(GIT_THREADS)
-static git_mutex *openssl_locks;
-
-static void openssl_locking_function(
- int mode, int n, const char *file, int line)
-{
- int lock;
-
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- lock = mode & CRYPTO_LOCK;
-
- if (lock) {
- (void)git_mutex_lock(&openssl_locks[n]);
- } else {
- git_mutex_unlock(&openssl_locks[n]);
- }
-}
-
-static void shutdown_ssl_locking(void)
-{
- int num_locks, i;
-
- num_locks = CRYPTO_num_locks();
- CRYPTO_set_locking_callback(NULL);
-
- for (i = 0; i < num_locks; ++i)
- git_mutex_free(&openssl_locks[i]);
- git__free(openssl_locks);
-}
-# endif /* GIT_THREADS */
-#endif /* OPENSSL_LEGACY_API */
static BIO_METHOD *git_stream_bio_method;
static int init_bio_method(void);
}
#ifdef VALGRIND
-#ifdef OPENSSL_LEGACY_API
-static void *git_openssl_malloc(size_t bytes)
-{
- return git__calloc(1, bytes);
-}
+# if !defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
-static void *git_openssl_realloc(void *mem, size_t size)
-{
- return git__realloc(mem, size);
-}
-
-static void git_openssl_free(void *mem)
-{
- return git__free(mem);
-}
-#else
static void *git_openssl_malloc(size_t bytes, const char *file, int line)
{
GIT_UNUSED(file);
GIT_UNUSED(line);
return git__calloc(1, bytes);
}
-
+
static void *git_openssl_realloc(void *mem, size_t size, const char *file, int line)
{
GIT_UNUSED(file);
GIT_UNUSED(line);
return git__realloc(mem, size);
}
-
+
static void git_openssl_free(void *mem, const char *file, int line)
{
GIT_UNUSED(file);
GIT_UNUSED(line);
- return git__free(mem);
+ git__free(mem);
+}
+# else /* !GIT_OPENSSL_LEGACY && !GIT_OPENSSL_DYNAMIC */
+static void *git_openssl_malloc(size_t bytes)
+{
+ return git__calloc(1, bytes);
}
-#endif
-#endif
-int git_openssl_stream_global_init(void)
+static void *git_openssl_realloc(void *mem, size_t size)
+{
+ return git__realloc(mem, size);
+}
+
+static void git_openssl_free(void *mem)
+{
+ git__free(mem);
+}
+# endif /* !GIT_OPENSSL_LEGACY && !GIT_OPENSSL_DYNAMIC */
+#endif /* VALGRIND */
+
+static int openssl_init(void)
{
long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
const char *ciphers = git_libgit2__ssl_ciphers();
#endif
#ifdef VALGRIND
- /* Swap in our own allocator functions that initialize allocated memory */
- if (!allocators_initialized &&
+ /*
+ * Swap in our own allocator functions that initialize
+ * allocated memory to avoid spurious valgrind warnings.
+ * Don't error on failure; many builds of OpenSSL do not
+ * allow you to set these functions.
+ */
+ if (!allocators_initialized) {
CRYPTO_set_mem_functions(git_openssl_malloc,
git_openssl_realloc,
- git_openssl_free) != 1)
- goto error;
- allocators_initialized = true;
+ git_openssl_free);
+ allocators_initialized = true;
+ }
#endif
OPENSSL_init_ssl(0, NULL);
if (init_bio_method() < 0)
goto error;
- git__on_shutdown(shutdown_ssl);
-
- return 0;
+ return git_runtime_shutdown_register(shutdown_ssl);
error:
git_error_set(GIT_ERROR_NET, "could not initialize openssl: %s",
return -1;
}
-#if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
-static void threadid_cb(CRYPTO_THREADID *threadid)
+/*
+ * When we use dynamic loading, we defer OpenSSL initialization until
+ * it's first used. `openssl_ensure_initialized` will do the work
+ * under a mutex.
+ */
+git_mutex openssl_mutex;
+bool openssl_initialized;
+
+int git_openssl_stream_global_init(void)
{
- GIT_UNUSED(threadid);
- CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
-}
+#ifndef GIT_OPENSSL_DYNAMIC
+ return openssl_init();
+#else
+ if (git_mutex_init(&openssl_mutex) != 0)
+ return -1;
+
+ return 0;
#endif
+}
-int git_openssl_set_locking(void)
+static int openssl_ensure_initialized(void)
{
-#if defined(GIT_THREADS) && defined(OPENSSL_LEGACY_API)
- int num_locks, i;
+#ifdef GIT_OPENSSL_DYNAMIC
+ int error = 0;
- CRYPTO_THREADID_set_callback(threadid_cb);
+ if (git_mutex_lock(&openssl_mutex) != 0)
+ return -1;
- num_locks = CRYPTO_num_locks();
- openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
- GIT_ERROR_CHECK_ALLOC(openssl_locks);
+ if (!openssl_initialized) {
+ if ((error = git_openssl_stream_dynamic_init()) == 0)
+ error = openssl_init();
- for (i = 0; i < num_locks; i++) {
- if (git_mutex_init(&openssl_locks[i]) != 0) {
- git_error_set(GIT_ERROR_SSL, "failed to initialize openssl locks");
- return -1;
- }
+ openssl_initialized = true;
}
- CRYPTO_set_locking_callback(openssl_locking_function);
- git__on_shutdown(shutdown_ssl_locking);
+ error |= git_mutex_unlock(&openssl_mutex);
+ return error;
+
+#else
return 0;
-#elif !defined(OPENSSL_LEGACY_API)
+#endif
+}
+
+#if !defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
+int git_openssl_set_locking(void)
+{
+# ifdef GIT_THREADS
return 0;
-#else
+# else
git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads");
return -1;
-#endif
+# endif
}
+#endif
static int bio_create(BIO *b)
err = SSL_get_error(ssl, error);
- assert(err != SSL_ERROR_WANT_READ);
- assert(err != SSL_ERROR_WANT_WRITE);
+ GIT_ASSERT(err != SSL_ERROR_WANT_READ);
+ GIT_ASSERT(err != SSL_ERROR_WANT_WRITE);
switch (err) {
case SSL_ERROR_WANT_CONNECT:
{
openssl_stream *st;
- assert(out && in && host);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(in);
+ GIT_ASSERT_ARG(host);
st = git__calloc(1, sizeof(openssl_stream));
GIT_ERROR_CHECK_ALLOC(st);
int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host)
{
+ if (openssl_ensure_initialized() < 0)
+ return -1;
+
return openssl_stream_wrap(out, in, host, 0);
}
git_stream *stream = NULL;
int error;
- assert(out && host && port);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
+
+ if (openssl_ensure_initialized() < 0)
+ return -1;
if ((error = git_socket_stream_new(&stream, host, port)) < 0)
return error;
int git_openssl__set_cert_location(const char *file, const char *path)
{
+ if (openssl_ensure_initialized() < 0)
+ return -1;
+
if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) {
char errmsg[256];
#define INCLUDE_streams_openssl_h__
#include "common.h"
+#include "streams/openssl_legacy.h"
+#include "streams/openssl_dynamic.h"
#include "git2/sys/stream.h"
extern int git_openssl_stream_global_init(void);
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+# endif
+
#ifdef GIT_OPENSSL
extern int git_openssl__set_cert_location(const char *file, const char *path);
-
extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
extern int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host);
#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/openssl.h"
+#include "streams/openssl_dynamic.h"
+
+#if defined(GIT_OPENSSL) && defined(GIT_OPENSSL_DYNAMIC)
+
+#include "runtime.h"
+
+#include <dlfcn.h>
+
+unsigned char *(*ASN1_STRING_data)(ASN1_STRING *x);
+const unsigned char *(*ASN1_STRING_get0_data)(const ASN1_STRING *x);
+int (*ASN1_STRING_length)(const ASN1_STRING *x);
+int (*ASN1_STRING_to_UTF8)(unsigned char **out, const ASN1_STRING *in);
+int (*ASN1_STRING_type)(const ASN1_STRING *x);
+
+void *(*BIO_get_data)(BIO *a);
+int (*BIO_get_new_index)(void);
+int (*OPENSSL_init_ssl)(uint64_t opts, const void *settings);
+void (*BIO_meth_free)(BIO_METHOD *biom);
+int (*BIO_meth_set_create)(BIO_METHOD *biom, int (*create) (BIO *));
+int (*BIO_meth_set_ctrl)(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+int (*BIO_meth_set_destroy)(BIO_METHOD *biom, int (*destroy) (BIO *));
+int (*BIO_meth_set_gets)(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+int (*BIO_meth_set_puts)(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+int (*BIO_meth_set_read)(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+int (*BIO_meth_set_write)(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+BIO_METHOD *(*BIO_meth_new)(int type, const char *name);
+BIO *(*BIO_new)(const BIO_METHOD *type);
+void (*BIO_set_data)(BIO *a, void *ptr);
+void (*BIO_set_init)(BIO *a, int init);
+
+void (*CRYPTO_free)(void *ptr, const char *file, int line);
+void *(*CRYPTO_malloc)(size_t num, const char *file, int line);
+int (*CRYPTO_num_locks)(void);
+void (*CRYPTO_set_locking_callback)(void (*func)(int mode, int type, const char *file, int line));
+int (*CRYPTO_set_mem_functions)(void *(*m)(size_t bytes), void *(*r)(void *mem, size_t size), void (*f)(void *mem));
+int (*CRYPTO_THREADID_set_callback)(void (*func)(CRYPTO_THREADID *id));
+void (*CRYPTO_THREADID_set_numeric)(CRYPTO_THREADID *id, unsigned long val);
+
+char *(*ERR_error_string)(unsigned long e, char *buf);
+void (*ERR_error_string_n)(unsigned long e, char *buf, size_t len);
+unsigned long (*ERR_get_error)(void);
+
+int (*SSL_connect)(SSL *ssl);
+long (*SSL_ctrl)(SSL *ssl, int cmd, long arg, void *parg);
+void (*SSL_free)(SSL *ssl);
+int (*SSL_get_error)(SSL *ssl, int ret);
+X509 *(*SSL_get_peer_certificate)(const SSL *ssl);
+long (*SSL_get_verify_result)(const SSL *ssl);
+int (*SSL_library_init)(void);
+void (*SSL_load_error_strings)(void);
+SSL *(*SSL_new)(SSL_CTX *ctx);
+int (*SSL_read)(SSL *ssl, const void *buf, int num);
+void (*SSL_set_bio)(SSL *ssl, BIO *rbio, BIO *wbio);
+int (*SSL_shutdown)(SSL *ssl);
+int (*SSL_write)(SSL *ssl, const void *buf, int num);
+
+long (*SSL_CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
+void (*SSL_CTX_free)(SSL_CTX *ctx);
+SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *method);
+int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str);
+int (*SSL_CTX_set_default_verify_paths)(SSL_CTX *ctx);
+long (*SSL_CTX_set_options)(SSL_CTX *ctx, long options);
+void (*SSL_CTX_set_verify)(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *));
+int (*SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath);
+
+const SSL_METHOD *(*SSLv23_method)(void);
+const SSL_METHOD *(*TLS_method)(void);
+
+ASN1_STRING *(*X509_NAME_ENTRY_get_data)(const X509_NAME_ENTRY *ne);
+X509_NAME_ENTRY *(*X509_NAME_get_entry)(X509_NAME *name, int loc);
+int (*X509_NAME_get_index_by_NID)(X509_NAME *name, int nid, int lastpos);
+void (*X509_free)(X509 *a);
+void *(*X509_get_ext_d2i)(const X509 *x, int nid, int *crit, int *idx);
+X509_NAME *(*X509_get_subject_name)(const X509 *x);
+
+int (*i2d_X509)(X509 *a, unsigned char **ppout);
+
+int (*OPENSSL_sk_num)(const void *sk);
+void *(*OPENSSL_sk_value)(const void *sk, int i);
+void (*OPENSSL_sk_free)(void *sk);
+
+int (*sk_num)(const void *sk);
+void *(*sk_value)(const void *sk, int i);
+void (*sk_free)(void *sk);
+
+void *openssl_handle;
+
+GIT_INLINE(void *) openssl_sym(int *err, const char *name, bool required)
+{
+ void *symbol;
+
+ /* if we've seen an err, noop to retain it */
+ if (*err)
+ return NULL;
+
+
+ if ((symbol = dlsym(openssl_handle, name)) == NULL && required) {
+ const char *msg = dlerror();
+ git_error_set(GIT_ERROR_SSL, "could not load ssl function '%s': %s", name, msg ? msg : "unknown error");
+ *err = -1;
+ }
+
+ return symbol;
+}
+
+static void dynamic_shutdown(void)
+{
+ dlclose(openssl_handle);
+ openssl_handle = NULL;
+}
+
+int git_openssl_stream_dynamic_init(void)
+{
+ int err = 0;
+
+ if ((openssl_handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) {
+ git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
+ return -1;
+ }
+
+ ASN1_STRING_data = (unsigned char *(*)(ASN1_STRING *x))openssl_sym(&err, "ASN1_STRING_data", false);
+ ASN1_STRING_get0_data = (const unsigned char *(*)(const ASN1_STRING *x))openssl_sym(&err, "ASN1_STRING_get0_data", false);
+ ASN1_STRING_length = (int (*)(const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_length", true);
+ ASN1_STRING_to_UTF8 = (int (*)(unsigned char **, const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_to_UTF8", true);
+ ASN1_STRING_type = (int (*)(const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_type", true);
+
+ BIO_get_data = (void *(*)(BIO *))openssl_sym(&err, "BIO_get_data", false);
+ BIO_get_new_index = (int (*)(void))openssl_sym(&err, "BIO_get_new_index", false);
+ BIO_meth_free = (void (*)(BIO_METHOD *))openssl_sym(&err, "BIO_meth_free", false);
+ BIO_meth_new = (BIO_METHOD *(*)(int, const char *))openssl_sym(&err, "BIO_meth_new", false);
+ BIO_meth_set_create = (int (*)(BIO_METHOD *, int (*)(BIO *)))openssl_sym(&err, "BIO_meth_set_create", false);
+ BIO_meth_set_ctrl = (int (*)(BIO_METHOD *, long (*)(BIO *, int, long, void *)))openssl_sym(&err, "BIO_meth_set_ctrl", false);
+ BIO_meth_set_destroy = (int (*)(BIO_METHOD *, int (*)(BIO *)))openssl_sym(&err, "BIO_meth_set_destroy", false);
+ BIO_meth_set_gets = (int (*)(BIO_METHOD *, int (*)(BIO *, char *, int)))openssl_sym(&err, "BIO_meth_set_gets", false);
+ BIO_meth_set_puts = (int (*)(BIO_METHOD *, int (*)(BIO *, const char *)))openssl_sym(&err, "BIO_meth_set_puts", false);
+ BIO_meth_set_read = (int (*)(BIO_METHOD *, int (*)(BIO *, char *, int)))openssl_sym(&err, "BIO_meth_set_read", false);
+ BIO_meth_set_write = (int (*)(BIO_METHOD *, int (*)(BIO *, const char *, int)))openssl_sym(&err, "BIO_meth_set_write", false);
+ BIO_new = (BIO *(*)(const BIO_METHOD *))openssl_sym(&err, "BIO_new", true);
+ BIO_set_data = (void (*)(BIO *a, void *))openssl_sym(&err, "BIO_set_data", false);
+ BIO_set_init = (void (*)(BIO *a, int))openssl_sym(&err, "BIO_set_init", false);
+
+ CRYPTO_free = (void (*)(void *, const char *, int))openssl_sym(&err, "CRYPTO_free", true);
+ CRYPTO_malloc = (void *(*)(size_t, const char *, int))openssl_sym(&err, "CRYPTO_malloc", true);
+ CRYPTO_num_locks = (int (*)(void))openssl_sym(&err, "CRYPTO_num_locks", false);
+ CRYPTO_set_locking_callback = (void (*)(void (*)(int, int, const char *, int)))openssl_sym(&err, "CRYPTO_set_locking_callback", false);
+ CRYPTO_set_mem_functions = (int (*)(void *(*)(size_t), void *(*)(void *, size_t), void (*f)(void *)))openssl_sym(&err, "CRYPTO_set_mem_functions", true);
+
+ CRYPTO_THREADID_set_callback = (int (*)(void (*)(CRYPTO_THREADID *)))openssl_sym(&err, "CRYPTO_THREADID_set_callback", false);
+ CRYPTO_THREADID_set_numeric = (void (*)(CRYPTO_THREADID *, unsigned long))openssl_sym(&err, "CRYPTO_THREADID_set_numeric", false);
+
+ ERR_error_string = (char *(*)(unsigned long, char *))openssl_sym(&err, "ERR_error_string", true);
+ ERR_error_string_n = (void (*)(unsigned long, char *, size_t))openssl_sym(&err, "ERR_error_string_n", true);
+ ERR_get_error = (unsigned long (*)(void))openssl_sym(&err, "ERR_get_error", true);
+
+ OPENSSL_init_ssl = (int (*)(uint64_t opts, const void *settings))openssl_sym(&err, "OPENSSL_init_ssl", false);
+ OPENSSL_sk_num = (int (*)(const void *))openssl_sym(&err, "OPENSSL_sk_num", false);
+ OPENSSL_sk_value = (void *(*)(const void *sk, int i))openssl_sym(&err, "OPENSSL_sk_value", false);
+ OPENSSL_sk_free = (void (*)(void *))openssl_sym(&err, "OPENSSL_sk_free", false);
+
+ sk_num = (int (*)(const void *))openssl_sym(&err, "sk_num", false);
+ sk_value = (void *(*)(const void *sk, int i))openssl_sym(&err, "sk_value", false);
+ sk_free = (void (*)(void *))openssl_sym(&err, "sk_free", false);
+
+ SSL_connect = (int (*)(SSL *))openssl_sym(&err, "SSL_connect", true);
+ SSL_ctrl = (long (*)(SSL *, int, long, void *))openssl_sym(&err, "SSL_ctrl", true);
+ SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get_peer_certificate", true);
+ SSL_library_init = (int (*)(void))openssl_sym(&err, "SSL_library_init", false);
+ SSL_free = (void (*)(SSL *))openssl_sym(&err, "SSL_free", true);
+ SSL_get_error = (int (*)(SSL *, int))openssl_sym(&err, "SSL_get_error", true);
+ SSL_get_verify_result = (long (*)(const SSL *ssl))openssl_sym(&err, "SSL_get_verify_result", true);
+ SSL_load_error_strings = (void (*)(void))openssl_sym(&err, "SSL_load_error_strings", false);
+ SSL_new = (SSL *(*)(SSL_CTX *))openssl_sym(&err, "SSL_new", true);
+ SSL_read = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_read", true);
+ SSL_set_bio = (void (*)(SSL *, BIO *, BIO *))openssl_sym(&err, "SSL_set_bio", true);
+ SSL_shutdown = (int (*)(SSL *ssl))openssl_sym(&err, "SSL_shutdown", true);
+ SSL_write = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_write", true);
+
+ SSL_CTX_ctrl = (long (*)(SSL_CTX *, int, long, void *))openssl_sym(&err, "SSL_CTX_ctrl", true);
+ SSL_CTX_free = (void (*)(SSL_CTX *))openssl_sym(&err, "SSL_CTX_free", true);
+ SSL_CTX_new = (SSL_CTX *(*)(const SSL_METHOD *))openssl_sym(&err, "SSL_CTX_new", true);
+ SSL_CTX_set_cipher_list = (int (*)(SSL_CTX *, const char *))openssl_sym(&err, "SSL_CTX_set_cipher_list", true);
+ SSL_CTX_set_default_verify_paths = (int (*)(SSL_CTX *ctx))openssl_sym(&err, "SSL_CTX_set_default_verify_paths", true);
+ SSL_CTX_set_options = (long (*)(SSL_CTX *, long))openssl_sym(&err, "SSL_CTX_set_options", false);
+ SSL_CTX_set_verify = (void (*)(SSL_CTX *, int, int (*)(int, X509_STORE_CTX *)))openssl_sym(&err, "SSL_CTX_set_verify", true);
+ SSL_CTX_load_verify_locations = (int (*)(SSL_CTX *, const char *, const char *))openssl_sym(&err, "SSL_CTX_load_verify_locations", true);
+
+ SSLv23_method = (const SSL_METHOD *(*)(void))openssl_sym(&err, "SSLv23_method", false);
+ TLS_method = (const SSL_METHOD *(*)(void))openssl_sym(&err, "TLS_method", false);
+
+ X509_NAME_ENTRY_get_data = (ASN1_STRING *(*)(const X509_NAME_ENTRY *))openssl_sym(&err, "X509_NAME_ENTRY_get_data", true);
+ X509_NAME_get_entry = (X509_NAME_ENTRY *(*)(X509_NAME *, int))openssl_sym(&err, "X509_NAME_get_entry", true);
+ X509_NAME_get_index_by_NID = (int (*)(X509_NAME *, int, int))openssl_sym(&err, "X509_NAME_get_index_by_NID", true);
+ X509_free = (void (*)(X509 *))openssl_sym(&err, "X509_free", true);
+ X509_get_ext_d2i = (void *(*)(const X509 *x, int nid, int *crit, int *idx))openssl_sym(&err, "X509_get_ext_d2i", true);
+ X509_get_subject_name = (X509_NAME *(*)(const X509 *))openssl_sym(&err, "X509_get_subject_name", true);
+
+ i2d_X509 = (int (*)(X509 *a, unsigned char **ppout))openssl_sym(&err, "i2d_X509", true);
+
+ if (err)
+ goto on_error;
+
+ /* Add legacy functionality */
+ if (!OPENSSL_init_ssl) {
+ OPENSSL_init_ssl = OPENSSL_init_ssl__legacy;
+
+ if (!SSL_library_init ||
+ !SSL_load_error_strings ||
+ !CRYPTO_num_locks ||
+ !CRYPTO_set_locking_callback ||
+ !CRYPTO_THREADID_set_callback ||
+ !CRYPTO_THREADID_set_numeric) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl initialization functions");
+ goto on_error;
+ }
+ }
+
+ if (!SSL_CTX_set_options)
+ SSL_CTX_set_options = SSL_CTX_set_options__legacy;
+
+ if (TLS_method)
+ SSLv23_method = TLS_method;
+
+ if (!BIO_meth_new) {
+ BIO_meth_new = BIO_meth_new__legacy;
+ BIO_meth_new = BIO_meth_new__legacy;
+ BIO_meth_free = BIO_meth_free__legacy;
+ BIO_meth_set_write = BIO_meth_set_write__legacy;
+ BIO_meth_set_read = BIO_meth_set_read__legacy;
+ BIO_meth_set_puts = BIO_meth_set_puts__legacy;
+ BIO_meth_set_gets = BIO_meth_set_gets__legacy;
+ BIO_meth_set_ctrl = BIO_meth_set_ctrl__legacy;
+ BIO_meth_set_create = BIO_meth_set_create__legacy;
+ BIO_meth_set_destroy = BIO_meth_set_destroy__legacy;
+ BIO_get_new_index = BIO_get_new_index__legacy;
+ BIO_set_data = BIO_set_data__legacy;
+ BIO_set_init = BIO_set_init__legacy;
+ BIO_get_data = BIO_get_data__legacy;
+ }
+
+ if (!ASN1_STRING_get0_data) {
+ if (!ASN1_STRING_data) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl string function");
+ goto on_error;
+ }
+
+ ASN1_STRING_get0_data = ASN1_STRING_get0_data__legacy;
+ }
+
+ if ((!OPENSSL_sk_num && !sk_num) ||
+ (!OPENSSL_sk_value && !sk_value) ||
+ (!OPENSSL_sk_free && !sk_free)) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl stack functions");
+ goto on_error;
+ }
+
+ if (git_runtime_shutdown_register(dynamic_shutdown) != 0)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ dlclose(openssl_handle);
+ return -1;
+}
+
+
+int sk_GENERAL_NAME_num(const GENERAL_NAME *sk)
+{
+ if (OPENSSL_sk_num)
+ return OPENSSL_sk_num(sk);
+ else if (sk_num)
+ return sk_num(sk);
+
+ GIT_ASSERT_WITH_RETVAL(false, 0);
+ return 0;
+}
+
+GENERAL_NAME *sk_GENERAL_NAME_value(const GENERAL_NAME *sk, int i)
+{
+ if (OPENSSL_sk_value)
+ return OPENSSL_sk_value(sk, i);
+ else if (sk_value)
+ return sk_value(sk, i);
+
+ GIT_ASSERT_WITH_RETVAL(false, NULL);
+ return NULL;
+}
+
+void GENERAL_NAMES_free(GENERAL_NAME *sk)
+{
+ if (OPENSSL_sk_free)
+ OPENSSL_sk_free(sk);
+ else if (sk_free)
+ sk_free(sk);
+}
+
+#endif /* GIT_OPENSSL && GIT_OPENSSL_DYNAMIC */
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECC cipher suite support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+/* ====================================================================
+ * Copyright 2005 Nokia. All rights reserved.
+ *
+ * The portions of the attached software ("Contribution") is developed by
+ * Nokia Corporation and is licensed pursuant to the OpenSSL open source
+ * license.
+ *
+ * The Contribution, originally written by Mika Kousa and Pasi Eronen of
+ * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites
+ * support (see RFC 4279) to OpenSSL.
+ *
+ * No patent licenses or other rights except those expressly stated in
+ * the OpenSSL open source license shall be deemed granted or received
+ * expressly, by implication, estoppel, or otherwise.
+ *
+ * No assurances are provided by Nokia that the Contribution does not
+ * infringe the patent or other intellectual property rights of any third
+ * party or that the license provides you with all the necessary rights
+ * to make use of the Contribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN
+ * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA
+ * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY
+ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
+ * OTHERWISE.
+ */
+
+#ifndef INCLUDE_streams_openssl_dynamic_h__
+#define INCLUDE_streams_openssl_dynamic_h__
+
+#ifdef GIT_OPENSSL_DYNAMIC
+
+# define BIO_CTRL_FLUSH 11
+
+# define BIO_TYPE_SOURCE_SINK 0x0400
+
+# define CRYPTO_LOCK 1
+
+# define GEN_DNS 2
+# define GEN_IPADD 7
+
+# define NID_commonName 13
+# define NID_subject_alt_name 85
+
+# define SSL_VERIFY_NONE 0x00
+
+# define SSL_CTRL_OPTIONS 32
+# define SSL_CTRL_MODE 33
+# define SSL_CTRL_SET_TLSEXT_HOSTNAME 55
+
+# define SSL_ERROR_NONE 0
+# define SSL_ERROR_SSL 1
+# define SSL_ERROR_WANT_READ 2
+# define SSL_ERROR_WANT_WRITE 3
+# define SSL_ERROR_WANT_X509_LOOKUP 4
+# define SSL_ERROR_SYSCALL 5
+# define SSL_ERROR_ZERO_RETURN 6
+# define SSL_ERROR_WANT_CONNECT 7
+# define SSL_ERROR_WANT_ACCEPT 8
+
+# define SSL_OP_NO_COMPRESSION 0x00020000L
+# define SSL_OP_NO_SSLv2 0x01000000L
+# define SSL_OP_NO_SSLv3 0x02000000L
+
+# define SSL_MODE_AUTO_RETRY 0x00000004L
+
+# define TLSEXT_NAMETYPE_host_name 0
+
+# define V_ASN1_UTF8STRING 12
+
+# define X509_V_OK 0
+
+/* Most of the OpenSSL types are mercifully opaque, so we can treat them like `void *` */
+typedef struct bio_st BIO;
+typedef struct bio_method_st BIO_METHOD;
+typedef void bio_info_cb;
+typedef void * CRYPTO_EX_DATA;
+typedef void CRYPTO_THREADID;
+typedef void GENERAL_NAMES;
+typedef void SSL;
+typedef void SSL_CTX;
+typedef void SSL_METHOD;
+typedef void X509;
+typedef void X509_NAME;
+typedef void X509_NAME_ENTRY;
+typedef void X509_STORE_CTX;
+
+typedef struct {
+ int length;
+ int type;
+ unsigned char *data;
+ long flags;
+} ASN1_STRING;
+
+typedef struct {
+ int type;
+ union {
+ char *ptr;
+ ASN1_STRING *ia5;
+ } d;
+} GENERAL_NAME;
+
+struct bio_st {
+ BIO_METHOD *method;
+ /* bio, mode, argp, argi, argl, ret */
+ long (*callback) (struct bio_st *, int, const char *, int, long, long);
+ char *cb_arg; /* first argument for the callback */
+ int init;
+ int shutdown;
+ int flags; /* extra storage */
+ int retry_reason;
+ int num;
+ void *ptr;
+ struct bio_st *next_bio; /* used by filter BIOs */
+ struct bio_st *prev_bio; /* used by filter BIOs */
+ int references;
+ unsigned long num_read;
+ unsigned long num_write;
+ CRYPTO_EX_DATA ex_data;
+};
+
+struct bio_method_st {
+ int type;
+ const char *name;
+ int (*bwrite) (BIO *, const char *, int);
+ int (*bread) (BIO *, char *, int);
+ int (*bputs) (BIO *, const char *);
+ int (*bgets) (BIO *, char *, int);
+ long (*ctrl) (BIO *, int, long, void *);
+ int (*create) (BIO *);
+ int (*destroy) (BIO *);
+ long (*callback_ctrl) (BIO *, int, bio_info_cb *);
+};
+
+extern unsigned char *(*ASN1_STRING_data)(ASN1_STRING *x);
+extern const unsigned char *(*ASN1_STRING_get0_data)(const ASN1_STRING *x);
+extern int (*ASN1_STRING_length)(const ASN1_STRING *x);
+extern int (*ASN1_STRING_to_UTF8)(unsigned char **out, const ASN1_STRING *in);
+extern int (*ASN1_STRING_type)(const ASN1_STRING *x);
+
+extern void *(*BIO_get_data)(BIO *a);
+extern int (*BIO_get_new_index)(void);
+extern int (*OPENSSL_init_ssl)(uint64_t opts, const void *settings);
+extern void (*BIO_meth_free)(BIO_METHOD *biom);
+extern int (*BIO_meth_set_create)(BIO_METHOD *biom, int (*create) (BIO *));
+extern int (*BIO_meth_set_ctrl)(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+extern int (*BIO_meth_set_destroy)(BIO_METHOD *biom, int (*destroy) (BIO *));
+extern int (*BIO_meth_set_gets)(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+extern int (*BIO_meth_set_puts)(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+extern int (*BIO_meth_set_read)(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+extern int (*BIO_meth_set_write)(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+extern BIO_METHOD *(*BIO_meth_new)(int type, const char *name);
+extern BIO *(*BIO_new)(const BIO_METHOD *type);
+extern void (*BIO_set_data)(BIO *a, void *ptr);
+extern void (*BIO_set_init)(BIO *a, int init);
+
+extern void (*CRYPTO_free)(void *ptr, const char *file, int line);
+extern void *(*CRYPTO_malloc)(size_t num, const char *file, int line);
+extern int (*CRYPTO_num_locks)(void);
+extern void (*CRYPTO_set_locking_callback)(void (*func)(int mode, int type, const char *file, int line));
+extern int (*CRYPTO_set_mem_functions)(void *(*m)(size_t bytes), void *(*r)(void *mem, size_t size), void (*f)(void *mem));
+extern int (*CRYPTO_THREADID_set_callback)(void (*func)(CRYPTO_THREADID *id));
+extern void (*CRYPTO_THREADID_set_numeric)(CRYPTO_THREADID *id, unsigned long val);
+
+extern char *(*ERR_error_string)(unsigned long e, char *buf);
+extern void (*ERR_error_string_n)(unsigned long e, char *buf, size_t len);
+extern unsigned long (*ERR_get_error)(void);
+
+# define OPENSSL_malloc(num) CRYPTO_malloc(num, __FILE__, __LINE__)
+# define OPENSSL_free(addr) CRYPTO_free(addr, __FILE__, __LINE__)
+
+extern int (*SSL_connect)(SSL *ssl);
+extern long (*SSL_ctrl)(SSL *ssl, int cmd, long arg, void *parg);
+extern void (*SSL_free)(SSL *ssl);
+extern int (*SSL_get_error)(SSL *ssl, int ret);
+extern X509 *(*SSL_get_peer_certificate)(const SSL *ssl);
+extern long (*SSL_get_verify_result)(const SSL *ssl);
+extern int (*SSL_library_init)(void);
+extern void (*SSL_load_error_strings)(void);
+extern SSL *(*SSL_new)(SSL_CTX *ctx);
+extern int (*SSL_read)(SSL *ssl, const void *buf, int num);
+extern void (*SSL_set_bio)(SSL *ssl, BIO *rbio, BIO *wbio);
+extern int (*SSL_shutdown)(SSL *ssl);
+extern int (*SSL_write)(SSL *ssl, const void *buf, int num);
+
+# define SSL_set_tlsext_host_name(s, name) SSL_ctrl((s), SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (char *)(name));
+
+extern long (*SSL_CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
+extern void (*SSL_CTX_free)(SSL_CTX *ctx);
+extern SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *method);
+extern int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str);
+extern int (*SSL_CTX_set_default_verify_paths)(SSL_CTX *ctx);
+extern long (*SSL_CTX_set_options)(SSL_CTX *ctx, long options);
+extern void (*SSL_CTX_set_verify)(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *));
+extern int (*SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath);
+
+# define SSL_CTX_set_mode(ctx, mode) SSL_CTX_ctrl((ctx), SSL_CTRL_MODE, (mode), NULL);
+
+extern const SSL_METHOD *(*SSLv23_method)(void);
+extern const SSL_METHOD *(*TLS_method)(void);
+
+extern ASN1_STRING *(*X509_NAME_ENTRY_get_data)(const X509_NAME_ENTRY *ne);
+extern X509_NAME_ENTRY *(*X509_NAME_get_entry)(X509_NAME *name, int loc);
+extern int (*X509_NAME_get_index_by_NID)(X509_NAME *name, int nid, int lastpos);
+extern void (*X509_free)(X509 *a);
+extern void *(*X509_get_ext_d2i)(const X509 *x, int nid, int *crit, int *idx);
+extern X509_NAME *(*X509_get_subject_name)(const X509 *x);
+
+extern int (*i2d_X509)(X509 *a, unsigned char **ppout);
+
+extern int (*OPENSSL_sk_num)(const void *sk);
+extern void *(*OPENSSL_sk_value)(const void *sk, int i);
+extern void (*OPENSSL_sk_free)(void *sk);
+
+extern int (*sk_num)(const void *sk);
+extern void *(*sk_value)(const void *sk, int i);
+extern void (*sk_free)(void *sk);
+
+extern int sk_GENERAL_NAME_num(const GENERAL_NAME *sk);
+extern GENERAL_NAME *sk_GENERAL_NAME_value(const GENERAL_NAME *sk, int i);
+extern void GENERAL_NAMES_free(GENERAL_NAME *sk);
+
+extern int git_openssl_stream_dynamic_init(void);
+
+#endif /* GIT_OPENSSL_DYNAMIC */
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/openssl.h"
+#include "streams/openssl_legacy.h"
+
+#include "runtime.h"
+#include "git2/sys/openssl.h"
+
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) || defined(GIT_OPENSSL_DYNAMIC)
+
+/*
+ * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
+ * which do not exist in previous versions. We define these inline functions so
+ * we can program against the interface instead of littering the implementation
+ * with ifdefs. We do the same for OPENSSL_init_ssl.
+ */
+
+int OPENSSL_init_ssl__legacy(uint64_t opts, const void *settings)
+{
+ GIT_UNUSED(opts);
+ GIT_UNUSED(settings);
+ SSL_load_error_strings();
+ SSL_library_init();
+ return 0;
+}
+
+BIO_METHOD *BIO_meth_new__legacy(int type, const char *name)
+{
+ BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
+ if (!meth) {
+ return NULL;
+ }
+
+ meth->type = type;
+ meth->name = name;
+
+ return meth;
+}
+
+void BIO_meth_free__legacy(BIO_METHOD *biom)
+{
+ git__free(biom);
+}
+
+int BIO_meth_set_write__legacy(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
+{
+ biom->bwrite = write;
+ return 1;
+}
+
+int BIO_meth_set_read__legacy(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
+{
+ biom->bread = read;
+ return 1;
+}
+
+int BIO_meth_set_puts__legacy(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
+{
+ biom->bputs = puts;
+ return 1;
+}
+
+int BIO_meth_set_gets__legacy(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
+
+{
+ biom->bgets = gets;
+ return 1;
+}
+
+int BIO_meth_set_ctrl__legacy(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
+{
+ biom->ctrl = ctrl;
+ return 1;
+}
+
+int BIO_meth_set_create__legacy(BIO_METHOD *biom, int (*create) (BIO *))
+{
+ biom->create = create;
+ return 1;
+}
+
+int BIO_meth_set_destroy__legacy(BIO_METHOD *biom, int (*destroy) (BIO *))
+{
+ biom->destroy = destroy;
+ return 1;
+}
+
+int BIO_get_new_index__legacy(void)
+{
+ /* This exists as of 1.1 so before we'd just have 0 */
+ return 0;
+}
+
+void BIO_set_init__legacy(BIO *b, int init)
+{
+ b->init = init;
+}
+
+void BIO_set_data__legacy(BIO *a, void *ptr)
+{
+ a->ptr = ptr;
+}
+
+void *BIO_get_data__legacy(BIO *a)
+{
+ return a->ptr;
+}
+
+const unsigned char *ASN1_STRING_get0_data__legacy(const ASN1_STRING *x)
+{
+ return ASN1_STRING_data((ASN1_STRING *)x);
+}
+
+long SSL_CTX_set_options__legacy(SSL_CTX *ctx, long op)
+{
+ return SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, op, NULL);
+}
+
+# if defined(GIT_THREADS)
+static git_mutex *openssl_locks;
+
+static void openssl_locking_function(int mode, int n, const char *file, int line)
+{
+ int lock;
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ lock = mode & CRYPTO_LOCK;
+
+ if (lock)
+ (void)git_mutex_lock(&openssl_locks[n]);
+ else
+ git_mutex_unlock(&openssl_locks[n]);
+}
+
+static void shutdown_ssl_locking(void)
+{
+ int num_locks, i;
+
+ num_locks = CRYPTO_num_locks();
+ CRYPTO_set_locking_callback(NULL);
+
+ for (i = 0; i < num_locks; ++i)
+ git_mutex_free(&openssl_locks[i]);
+ git__free(openssl_locks);
+}
+
+static void threadid_cb(CRYPTO_THREADID *threadid)
+{
+ GIT_UNUSED(threadid);
+ CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
+}
+
+int git_openssl_set_locking(void)
+{
+ int num_locks, i;
+
+#ifndef GIT_THREADS
+ git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads");
+ return -1;
+#endif
+
+#ifdef GIT_OPENSSL_DYNAMIC
+ /*
+ * This function is required on legacy versions of OpenSSL; when building
+ * with dynamically-loaded OpenSSL, we detect whether we loaded it or not.
+ */
+ if (!CRYPTO_set_locking_callback)
+ return 0;
+#endif
+
+ CRYPTO_THREADID_set_callback(threadid_cb);
+
+ num_locks = CRYPTO_num_locks();
+ openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
+ GIT_ERROR_CHECK_ALLOC(openssl_locks);
+
+ for (i = 0; i < num_locks; i++) {
+ if (git_mutex_init(&openssl_locks[i]) != 0) {
+ git_error_set(GIT_ERROR_SSL, "failed to initialize openssl locks");
+ return -1;
+ }
+ }
+
+ CRYPTO_set_locking_callback(openssl_locking_function);
+ return git_runtime_shutdown_register(shutdown_ssl_locking);
+}
+#endif /* GIT_THREADS */
+
+#endif /* GIT_OPENSSL_LEGACY || GIT_OPENSSL_DYNAMIC */
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_streams_openssl_legacy_h__
+#define INCLUDE_streams_openssl_legacy_h__
+
+#include "streams/openssl_dynamic.h"
+
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+
+# if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
+# define GIT_OPENSSL_LEGACY
+# endif
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
+# define OPENSSL_init_ssl OPENSSL_init_ssl__legacy
+# define BIO_meth_new BIO_meth_new__legacy
+# define BIO_meth_free BIO_meth_free__legacy
+# define BIO_meth_set_write BIO_meth_set_write__legacy
+# define BIO_meth_set_read BIO_meth_set_read__legacy
+# define BIO_meth_set_puts BIO_meth_set_puts__legacy
+# define BIO_meth_set_gets BIO_meth_set_gets__legacy
+# define BIO_meth_set_ctrl BIO_meth_set_ctrl__legacy
+# define BIO_meth_set_create BIO_meth_set_create__legacy
+# define BIO_meth_set_destroy BIO_meth_set_destroy__legacy
+# define BIO_get_new_index BIO_get_new_index__legacy
+# define BIO_set_data BIO_set_data__legacy
+# define BIO_set_init BIO_set_init__legacy
+# define BIO_get_data BIO_get_data__legacy
+# define ASN1_STRING_get0_data ASN1_STRING_get0_data__legacy
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) || defined(GIT_OPENSSL_DYNAMIC)
+
+extern int OPENSSL_init_ssl__legacy(uint64_t opts, const void *settings);
+extern BIO_METHOD *BIO_meth_new__legacy(int type, const char *name);
+extern void BIO_meth_free__legacy(BIO_METHOD *biom);
+extern int BIO_meth_set_write__legacy(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+extern int BIO_meth_set_read__legacy(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+extern int BIO_meth_set_puts__legacy(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+extern int BIO_meth_set_gets__legacy(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+extern int BIO_meth_set_ctrl__legacy(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+extern int BIO_meth_set_create__legacy(BIO_METHOD *biom, int (*create) (BIO *));
+extern int BIO_meth_set_destroy__legacy(BIO_METHOD *biom, int (*destroy) (BIO *));
+extern int BIO_get_new_index__legacy(void);
+extern void BIO_set_data__legacy(BIO *a, void *ptr);
+extern void BIO_set_init__legacy(BIO *b, int init);
+extern void *BIO_get_data__legacy(BIO *a);
+extern const unsigned char *ASN1_STRING_get0_data__legacy(const ASN1_STRING *x);
+extern long SSL_CTX_set_options__legacy(SSL_CTX *ctx, long op);
+
+#endif
+
+#endif
#include "streams/registry.h"
-#include "global.h"
+#include "runtime.h"
#include "streams/tls.h"
#include "streams/mbedtls.h"
#include "streams/openssl.h"
if (git_rwlock_init(&stream_registry.lock) < 0)
return -1;
- git__on_shutdown(shutdown_stream_registry);
- return 0;
+ return git_runtime_shutdown_register(shutdown_stream_registry);
}
GIT_INLINE(void) stream_registration_cpy(
git_stream_registration *target;
int error = GIT_ENOTFOUND;
- assert(out);
+ GIT_ASSERT_ARG(out);
switch(type) {
case GIT_STREAM_STANDARD:
target = &stream_registry.tls_callbacks;
break;
default:
- assert(0);
+ git_error_set(GIT_ERROR_INVALID, "invalid stream type");
return -1;
}
int git_stream_register(git_stream_t type, git_stream_registration *registration)
{
- assert(!registration || registration->init);
+ GIT_ASSERT(!registration || registration->init);
GIT_ERROR_CHECK_VERSION(registration, GIT_STREAM_VERSION, "stream_registration");
{
git_socket_stream *st;
- assert(out && host && port);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
st = git__calloc(1, sizeof(git_socket_stream));
GIT_ERROR_CHECK_ALLOC(st);
git_stream_registration custom = {0};
int error;
- assert(out && host && port);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_STANDARD)) == 0)
init = custom.init;
if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
return stransport_error(ret);
- assert(processed < SSIZE_MAX);
+ GIT_ASSERT(processed < SSIZE_MAX);
return (ssize_t)processed;
}
stransport_stream *st;
OSStatus ret;
- assert(out && in && host);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(in);
+ GIT_ASSERT_ARG(host);
st = git__calloc(1, sizeof(stransport_stream));
GIT_ERROR_CHECK_ALLOC(st);
git_stream *stream = NULL;
int error;
- assert(out && host);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
error = git_socket_stream_new(&stream, host, port);
#include "git2/errors.h"
#include "common.h"
-#include "global.h"
#include "streams/registry.h"
#include "streams/tls.h"
#include "streams/mbedtls.h"
git_stream_registration custom = {0};
int error;
- assert(out && host && port);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_TLS)) == 0) {
init = custom.init;
int (*wrap)(git_stream **, git_stream *, const char *) = NULL;
git_stream_registration custom = {0};
- assert(out && in);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(in);
if (git_stream_registry_lookup(&custom, GIT_STREAM_TLS) == 0) {
wrap = custom.wrap;
#include "git2/types.h"
#include "git2/index.h"
#include "buffer.h"
-#include "buf_text.h"
#include "vector.h"
#include "posix.h"
#include "config_backend.h"
return error;
}
+int git_submodule_cache_init(git_strmap **out, git_repository *repo)
+{
+ int error = 0;
+ git_strmap *cache = NULL;
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ if ((error = git_strmap_new(&cache)) < 0)
+ return error;
+ if ((error = git_submodule__map(repo, cache)) < 0) {
+ git_submodule_cache_free(cache);
+ return error;
+ }
+ *out = cache;
+ return error;
+}
+
+int git_submodule_cache_free(git_strmap *cache)
+{
+ git_submodule *sm = NULL;
+ if (cache == NULL)
+ return 0;
+ git_strmap_foreach_value(cache, sm, {
+ git_submodule_free(sm);
+ });
+ git_strmap_free(cache);
+ return 0;
+}
+
int git_submodule_lookup(
git_submodule **out, /* NULL if user only wants to test existence */
git_repository *repo,
const char *name) /* trailing slash is allowed */
+{
+ return git_submodule__lookup_with_cache(out, repo, name, repo->submodule_cache);
+}
+
+int git_submodule__lookup_with_cache(
+ git_submodule **out, /* NULL if user only wants to test existence */
+ git_repository *repo,
+ const char *name, /* trailing slash is allowed */
+ git_strmap *cache)
{
int error;
unsigned int location;
git_submodule *sm;
- assert(repo && name);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
if (repo->is_bare) {
git_error_set(GIT_ERROR_SUBMODULE, "cannot get submodules without a working tree");
return -1;
}
- if (repo->submodule_cache != NULL) {
- if ((sm = git_strmap_get(repo->submodule_cache, name)) != NULL) {
+ if (cache != NULL) {
+ if ((sm = git_strmap_get(cache, name)) != NULL) {
if (out) {
*out = sm;
GIT_REFCOUNT_INC(*out);
/* If it's not configured, we still check if there's a repo at the path */
if (git_repository_workdir(repo)) {
git_buf path = GIT_BUF_INIT;
- if (git_buf_join3(&path,
- '/', git_repository_workdir(repo), name, DOT_GIT) < 0)
+ if (git_buf_join3(&path, '/',
+ git_repository_workdir(repo),
+ name, DOT_GIT) < 0 ||
+ git_path_validate_workdir_buf(NULL, &path) < 0)
return -1;
if (git_path_exists(path.ptr))
git_buf_attach_notowned(&buf, name, strlen(name));
}
- isvalid = git_path_isvalid(repo, buf.ptr, 0, flags);
+ isvalid = git_path_validate(repo, buf.ptr, 0, flags);
git_buf_dispose(&buf);
return isvalid;
int error = 0;
git_index *idx = NULL;
git_tree *head = NULL;
- const char *wd = NULL;
git_buf path = GIT_BUF_INIT;
git_submodule *sm;
git_config *mods = NULL;
+ bool has_workdir;
- assert(repo && map);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(map);
/* get sources that we will need to check */
if (git_repository_index(&idx, repo) < 0)
if (git_repository_head_tree(&head, repo) < 0)
git_error_clear();
- wd = git_repository_workdir(repo);
- if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0)
+ has_workdir = git_repository_workdir(repo) != NULL;
+
+ if (has_workdir &&
+ (error = git_repository_workdir_path(&path, repo, GIT_MODULES_FILE)) < 0)
goto cleanup;
/* add submodule information from .gitmodules */
- if (wd) {
+ if (has_workdir) {
lfc_data data = { 0 };
data.map = map;
data.repo = repo;
goto cleanup;
}
/* shallow scan submodules in work tree as needed */
- if (wd) {
+ if (has_workdir) {
git_strmap_foreach_value(map, sm, {
submodule_load_from_wd_lite(sm);
});
git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
git_repository *subrepo = NULL;
- error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
+ error = git_repository_workdir_path(&workdir, parent_repo, path);
if (error < 0)
goto cleanup;
git_repository *subrepo = NULL;
bool path_occupied;
- assert(repo && url && path);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(path);
/* see if there is already an entry for this submodule */
/* init submodule repository and add origin remote as needed */
- error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
+ error = git_repository_workdir_path(&name, repo, path);
if (error < 0)
goto cleanup;
git_config *cfg = NULL;
git_buf buf = GIT_BUF_INIT;
- assert(out && sm);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(sm);
/* get the configured remote url of the submodule */
if ((error = git_buf_printf(&buf, "submodule.%s.url", sm->name)) < 0 ||
git_submodule_update_options sub_opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
- assert(submodule);
+ GIT_ASSERT_ARG(submodule);
if (given_opts)
memcpy(&sub_opts, given_opts, sizeof(sub_opts));
opts.remote_cb = clone_return_origin;
opts.remote_cb_payload = submodule;
- git_buf_puts(&rel_path, git_repository_workdir(git_submodule_owner(submodule)));
- git_buf_joinpath(&rel_path, git_buf_cstr(&rel_path), git_submodule_path(submodule));
-
- GIT_ERROR_CHECK_ALLOC_BUF(&rel_path);
+ error = git_repository_workdir_path(&rel_path, git_submodule_owner(submodule), git_submodule_path(submodule));
+ if (error < 0)
+ goto cleanup;
error = git_clone__submodule(&clone, git_submodule_url(submodule), git_buf_cstr(&rel_path), &opts);
if (error < 0)
int error;
git_index *index;
- assert(sm);
+ GIT_ASSERT_ARG(sm);
if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
(error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
git_index_entry entry;
struct stat st;
- assert(sm);
+ GIT_ASSERT_ARG(sm);
/* force reload of wd OID by git_submodule_open */
sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
- (error = git_buf_joinpath(
- &path, git_repository_workdir(sm->repo), sm->path)) < 0 ||
- (error = git_submodule_open(&sm_repo, sm)) < 0)
+ (error = git_repository_workdir_path(&path, sm->repo, sm->path)) < 0 ||
+ (error = git_submodule_open(&sm_repo, sm)) < 0)
goto cleanup;
/* read stat information for submodule working directory */
git_repository *git_submodule_owner(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
return submodule->repo;
}
const char *git_submodule_name(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
return submodule->name;
}
const char *git_submodule_path(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
return submodule->path;
}
const char *git_submodule_url(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
return submodule->url;
}
int error = 0;
git_buf normalized = GIT_BUF_INIT;
- assert(out && repo && url);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(url);
- git_buf_sanitize(out);
+ if ((error = git_buf_sanitize(out)) < 0)
+ return error;
/* We do this in all platforms in case someone on Windows created the .gitmodules */
if (strchr(url, '\\')) {
const char *git_submodule_branch(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
return submodule->branch;
}
int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch)
{
-
- assert(repo && name);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
return write_var(repo, name, "branch", branch);
}
int git_submodule_set_url(git_repository *repo, const char *name, const char *url)
{
- assert(repo && name && url);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(url);
return write_var(repo, name, "url", url);
}
const git_oid *git_submodule_index_id(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
return &submodule->index_oid;
const git_oid *git_submodule_head_id(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
return &submodule->head_oid;
const git_oid *git_submodule_wd_id(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
/* load unless we think we have a valid oid */
if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_IGNORE_UNSPECIFIED);
+
return (submodule->ignore < GIT_SUBMODULE_IGNORE_NONE) ?
GIT_SUBMODULE_IGNORE_NONE : submodule->ignore;
}
int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore)
{
- assert(repo && name);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore);
}
git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_UPDATE_NONE);
+
return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
}
int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update)
{
- assert(repo && name);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update);
}
git_submodule_recurse_t git_submodule_fetch_recurse_submodules(
git_submodule *submodule)
{
- assert(submodule);
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_RECURSE_NO);
return submodule->fetch_recurse;
}
int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse)
{
- assert(repo && name);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse);
}
GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
/* Workdir: path to sub-repo working directory */
- error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
+ error = git_repository_workdir_path(&workdir, parent_repo, path);
if (error < 0)
goto cleanup;
git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT;
- assert(sm);
+ GIT_ASSERT_ARG(sm);
if (_update_options)
memcpy(&update_options, _update_options, sizeof(git_submodule_update_options));
unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
const char *wd;
- assert(sm && subrepo);
+ GIT_ASSERT_ARG(sm);
+ GIT_ASSERT_ARG(subrepo);
if (git_repository__ensure_not_bare(
sm->repo, "open submodule repository") < 0)
wd = git_repository_workdir(sm->repo);
- if (git_buf_joinpath(&path, wd, sm->path) < 0 ||
- git_buf_joinpath(&path, path.ptr, DOT_GIT) < 0)
+ if (git_buf_join3(&path, '/', wd, sm->path, DOT_GIT) < 0)
return -1;
sm->flags = sm->flags &
GIT_UNUSED(force);
- assert(sm);
+ GIT_ASSERT_ARG(sm);
if ((error = git_submodule_name_is_valid(sm->repo, sm->name, 0)) <= 0)
/* This should come with a warning, but we've no API for that */
git_submodule *sm;
int error;
- assert(status && repo && name);
+ GIT_ASSERT_ARG(status);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
if ((error = git_submodule_lookup(&sm, repo, name)) < 0)
return error;
int git_submodule_location(unsigned int *location, git_submodule *sm)
{
- assert(location && sm);
+ GIT_ASSERT_ARG(location);
+ GIT_ASSERT_ARG(sm);
return git_submodule__status(
location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
git__free(sm);
}
+int git_submodule_dup(git_submodule **out, git_submodule *source)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(source);
+
+ GIT_REFCOUNT_INC(source);
+
+ *out = source;
+ return 0;
+}
+
void git_submodule_free(git_submodule *sm)
{
if (!sm)
{
git_buf path = GIT_BUF_INIT;
- if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)
+ if (git_repository_workdir_path(&path, sm->repo, sm->path) < 0)
return -1;
if (git_path_isdir(path.ptr))
*/
static int gitmodules_snapshot(git_config **snap, git_repository *repo)
{
- const char *workdir = git_repository_workdir(repo);
git_config *mods = NULL;
git_buf path = GIT_BUF_INIT;
int error;
- if (!workdir)
+ if (git_repository_workdir(repo) == NULL)
return GIT_ENOTFOUND;
- if ((error = git_buf_joinpath(&path, workdir, GIT_MODULES_FILE)) < 0)
+ if ((error = git_repository_workdir_path(&path, repo, GIT_MODULES_FILE)) < 0)
return error;
if ((error = git_config_open_ondisk(&mods, path.ptr)) < 0)
git_repository *repo,
int okay_to_create)
{
- const char *workdir = git_repository_workdir(repo);
git_buf path = GIT_BUF_INIT;
git_config_backend *mods = NULL;
- if (workdir != NULL) {
- if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
+ if (git_repository_workdir(repo) != NULL) {
+ if (git_repository_workdir_path(&path, repo, GIT_MODULES_FILE) != 0)
return NULL;
if (okay_to_create || git_path_isfile(path.ptr)) {
if ((error = git_worktree_open_from_repository(&wt, repo)) < 0)
goto out;
error = git_buf_sets(url, wt->parent_path);
- } else
+ } else {
error = git_buf_sets(url, git_repository_workdir(repo));
+ }
out:
git_remote_free(remote);
git_oid wd_oid;
};
-/* Force revalidation of submodule data cache (alloc as needed) */
-extern int git_submodule_cache_refresh(git_repository *repo);
-
-/* Release all submodules */
-extern void git_submodule_cache_free(git_repository *repo);
-
/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
enum {
GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20),
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20))
-/* Internal lookup does not attempt to refresh cached data */
-extern int git_submodule__lookup(
- git_submodule **out, git_repository *repo, const char *path);
+/* Initialize an external submodule cache for the provided repo. */
+extern int git_submodule_cache_init(git_strmap **out, git_repository *repo);
+
+/* Release the resources of the submodule cache. */
+extern int git_submodule_cache_free(git_strmap *cache);
+
+/* Submodule lookup with an explicit cache */
+extern int git_submodule__lookup_with_cache(
+ git_submodule **out, git_repository *repo, const char *path, git_strmap *cache);
/* Internal status fn returns status and optionally the various OIDs */
extern int git_submodule__status(
#include "sysdir.h"
-#include "global.h"
+#include "runtime.h"
#include "buffer.h"
#include "path.h"
#include <ctype.h>
long buflen;
int error;
- assert(out);
+ GIT_ASSERT_ARG(out);
if ((buflen = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1)
buflen = 1024;
for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); i++)
error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
- git__on_shutdown(git_sysdir_global_shutdown);
-
- return error;
+ return git_runtime_shutdown_register(git_sysdir_global_shutdown);
}
static int git_sysdir_check_selector(git_sysdir_t which)
int git_sysdir_get(const git_buf **out, git_sysdir_t which)
{
- assert(out);
+ GIT_ASSERT_ARG(out);
*out = NULL;
int git_tag_target(git_object **target, const git_tag *t)
{
- assert(t);
+ GIT_ASSERT_ARG(t);
return git_object_lookup(target, t->object.repo, &t->target, t->type);
}
const git_oid *git_tag_target_id(const git_tag *t)
{
- assert(t);
+ GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
return &t->target;
}
git_object_t git_tag_target_type(const git_tag *t)
{
- assert(t);
+ GIT_ASSERT_ARG_WITH_RETVAL(t, GIT_OBJECT_INVALID);
return t->type;
}
const char *git_tag_name(const git_tag *t)
{
- assert(t);
+ GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
return t->tag_name;
}
const char *git_tag_message(const git_tag *t)
{
- assert(t);
+ GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
return t->message;
}
int error;
- assert(repo && tag_name && target);
- assert(!create_tag_annotation || (tagger && message));
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(tag_name);
+ GIT_ASSERT_ARG(target);
+ GIT_ASSERT_ARG(!create_tag_annotation || (tagger && message));
if (git_object_owner(target) != repo) {
git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository");
const git_signature *tagger,
const char *message)
{
- assert(oid && repo && tag_name && target && tagger && message);
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(tag_name);
+ GIT_ASSERT_ARG(target);
+ GIT_ASSERT_ARG(tagger);
+ GIT_ASSERT_ARG(message);
return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
}
git_reference *new_ref = NULL;
git_buf ref_name = GIT_BUF_INIT;
- assert(oid && buffer);
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(buffer);
memset(&tag, 0, sizeof(tag));
{
tag_cb_data data;
- assert(repo && cb);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(cb);
data.cb = cb;
data.cb_data = cb_data;
tag_filter_data filter;
git_vector taglist;
- assert(tag_names && repo && pattern);
+ GIT_ASSERT_ARG(tag_names);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(pattern);
if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
return error;
return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJECT_ANY);
}
+int git_tag_name_is_valid(int *valid, const char *name)
+{
+ git_buf ref_name = GIT_BUF_INIT;
+ int error = 0;
+
+ GIT_ASSERT(valid);
+
+ /*
+ * Discourage tag name starting with dash,
+ * https://github.com/git/git/commit/4f0accd638b8d2
+ */
+ if (!name || name[0] == '-')
+ goto done;
+
+ if ((error = git_buf_puts(&ref_name, GIT_REFS_TAGS_DIR)) < 0 ||
+ (error = git_buf_puts(&ref_name, name)) < 0)
+ goto done;
+
+ error = git_reference_name_is_valid(valid, ref_name.ptr);
+
+done:
+ git_buf_dispose(&ref_name);
+ return error;
+}
+
/* Deprecated Functions */
#ifndef GIT_DEPRECATE_HARD
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "thread-utils.h"
-
-#ifdef _WIN32
-#ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-#endif
-# include <windows.h>
-#elif defined(hpux) || defined(__hpux) || defined(_hpux)
-# include <sys/pstat.h>
-#endif
-
-/*
- * By doing this in two steps we can at least get
- * the function to be somewhat coherent, even
- * with this disgusting nest of #ifdefs.
- */
-#ifndef _SC_NPROCESSORS_ONLN
-# ifdef _SC_NPROC_ONLN
-# define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
-# elif defined _SC_CRAY_NCPU
-# define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
-# endif
-#endif
-
-int git_online_cpus(void)
-{
-#ifdef _SC_NPROCESSORS_ONLN
- long ncpus;
-#endif
-
-#ifdef _WIN32
- SYSTEM_INFO info;
- GetSystemInfo(&info);
-
- if ((int)info.dwNumberOfProcessors > 0)
- return (int)info.dwNumberOfProcessors;
-#elif defined(hpux) || defined(__hpux) || defined(_hpux)
- struct pst_dynamic psd;
-
- if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))
- return (int)psd.psd_proc_cnt;
-#endif
-
-#ifdef _SC_NPROCESSORS_ONLN
- if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)
- return (int)ncpus;
-#endif
-
- return 1;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_thread_utils_h__
-#define INCLUDE_thread_utils_h__
-
-#if defined(GIT_THREADS)
-
-#if defined(__clang__)
-
-# if (__clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 1))
-# error Atomic primitives do not exist on this version of clang; configure libgit2 with -DTHREADSAFE=OFF
-# else
-# define GIT_BUILTIN_ATOMIC
-# endif
-
-#elif defined(__GNUC__)
-
-# if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1))
-# error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DTHREADSAFE=OFF
-# elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
-# define GIT_BUILTIN_ATOMIC
-# else
-# define GIT_BUILTIN_SYNC
-# endif
-
-#endif
-
-#endif /* GIT_THREADS */
-
-/* Common operations even if threading has been disabled */
-typedef struct {
-#if defined(GIT_WIN32)
- volatile long val;
-#else
- volatile int val;
-#endif
-} git_atomic;
-
-#ifdef GIT_ARCH_64
-
-typedef struct {
-#if defined(GIT_WIN32)
- volatile __int64 val;
-#else
- volatile int64_t val;
-#endif
-} git_atomic64;
-
-typedef git_atomic64 git_atomic_ssize;
-
-#define git_atomic_ssize_set git_atomic64_set
-#define git_atomic_ssize_add git_atomic64_add
-#define git_atomic_ssize_get git_atomic64_get
-
-#else
-
-typedef git_atomic git_atomic_ssize;
-
-#define git_atomic_ssize_set git_atomic_set
-#define git_atomic_ssize_add git_atomic_add
-#define git_atomic_ssize_get git_atomic_get
-
-#endif
-
-#ifdef GIT_THREADS
-
-#ifdef GIT_WIN32
-# include "win32/thread.h"
-#else
-# include "unix/pthread.h"
-#endif
-
-GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
-{
-#if defined(GIT_WIN32)
- InterlockedExchange(&a->val, (LONG)val);
-#elif defined(GIT_BUILTIN_ATOMIC)
- __atomic_store_n(&a->val, val, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- __sync_lock_test_and_set(&a->val, val);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(int) git_atomic_inc(git_atomic *a)
-{
-#if defined(GIT_WIN32)
- return InterlockedIncrement(&a->val);
-#elif defined(GIT_BUILTIN_ATOMIC)
- return __atomic_add_fetch(&a->val, 1, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- return __sync_add_and_fetch(&a->val, 1);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
-{
-#if defined(GIT_WIN32)
- return InterlockedExchangeAdd(&a->val, addend);
-#elif defined(GIT_BUILTIN_ATOMIC)
- return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- return __sync_add_and_fetch(&a->val, addend);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(int) git_atomic_dec(git_atomic *a)
-{
-#if defined(GIT_WIN32)
- return InterlockedDecrement(&a->val);
-#elif defined(GIT_BUILTIN_ATOMIC)
- return __atomic_sub_fetch(&a->val, 1, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- return __sync_sub_and_fetch(&a->val, 1);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(int) git_atomic_get(git_atomic *a)
-{
-#if defined(GIT_WIN32)
- return (int)InterlockedCompareExchange(&a->val, 0, 0);
-#elif defined(GIT_BUILTIN_ATOMIC)
- return __atomic_load_n(&a->val, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- return __sync_val_compare_and_swap(&a->val, 0, 0);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(void *) git___compare_and_swap(
- void * volatile *ptr, void *oldval, void *newval)
-{
-#if defined(GIT_WIN32)
- volatile void *foundval;
- foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
- return (foundval == oldval) ? oldval : newval;
-#elif defined(GIT_BUILTIN_ATOMIC)
- bool success = __atomic_compare_exchange(ptr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
- return success ? oldval : newval;
-#elif defined(GIT_BUILTIN_SYNC)
- volatile void *foundval;
- foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
- return (foundval == oldval) ? oldval : newval;
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(volatile void *) git___swap(
- void * volatile *ptr, void *newval)
-{
-#if defined(GIT_WIN32)
- return InterlockedExchangePointer(ptr, newval);
-#elif defined(GIT_BUILTIN_ATOMIC)
- void * volatile foundval;
- __atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST);
- return foundval;
-#elif defined(GIT_BUILTIN_SYNC)
- return __sync_lock_test_and_set(ptr, newval);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(volatile void *) git___load(void * volatile *ptr)
-{
-#if defined(GIT_WIN32)
- void *newval = NULL, *oldval = NULL;
- volatile void *foundval = NULL;
- foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
- return foundval;
-#elif defined(GIT_BUILTIN_ATOMIC)
- return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- return (volatile void *)__sync_val_compare_and_swap(ptr, 0, 0);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-#ifdef GIT_ARCH_64
-
-GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
-{
-#if defined(GIT_WIN32)
- return InterlockedExchangeAdd64(&a->val, addend);
-#elif defined(GIT_BUILTIN_ATOMIC)
- return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- return __sync_add_and_fetch(&a->val, addend);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
-{
-#if defined(GIT_WIN32)
- InterlockedExchange64(&a->val, val);
-#elif defined(GIT_BUILTIN_ATOMIC)
- __atomic_store_n(&a->val, val, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- __sync_lock_test_and_set(&a->val, val);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
-{
-#if defined(GIT_WIN32)
- return (int64_t)InterlockedCompareExchange64(&a->val, 0, 0);
-#elif defined(GIT_BUILTIN_ATOMIC)
- return __atomic_load_n(&a->val, __ATOMIC_SEQ_CST);
-#elif defined(GIT_BUILTIN_SYNC)
- return __sync_val_compare_and_swap(&a->val, 0, 0);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-#endif
-
-#else
-
-#define git_thread unsigned int
-#define git_thread_create(thread, start_routine, arg) 0
-#define git_thread_join(id, status) (void)0
-
-/* Pthreads Mutex */
-#define git_mutex unsigned int
-GIT_INLINE(int) git_mutex_init(git_mutex *mutex) \
- { GIT_UNUSED(mutex); return 0; }
-GIT_INLINE(int) git_mutex_lock(git_mutex *mutex) \
- { GIT_UNUSED(mutex); return 0; }
-#define git_mutex_unlock(a) (void)0
-#define git_mutex_free(a) (void)0
-
-/* Pthreads condition vars */
-#define git_cond unsigned int
-#define git_cond_init(c, a) (void)0
-#define git_cond_free(c) (void)0
-#define git_cond_wait(c, l) (void)0
-#define git_cond_signal(c) (void)0
-#define git_cond_broadcast(c) (void)0
-
-/* Pthreads rwlock */
-#define git_rwlock unsigned int
-#define git_rwlock_init(a) 0
-#define git_rwlock_rdlock(a) 0
-#define git_rwlock_rdunlock(a) (void)0
-#define git_rwlock_wrlock(a) 0
-#define git_rwlock_wrunlock(a) (void)0
-#define git_rwlock_free(a) (void)0
-#define GIT_RWLOCK_STATIC_INIT 0
-
-
-GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
-{
- a->val = val;
-}
-
-GIT_INLINE(int) git_atomic_inc(git_atomic *a)
-{
- return ++a->val;
-}
-
-GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
-{
- a->val += addend;
- return a->val;
-}
-
-GIT_INLINE(int) git_atomic_dec(git_atomic *a)
-{
- return --a->val;
-}
-
-GIT_INLINE(int) git_atomic_get(git_atomic *a)
-{
- return (int)a->val;
-}
-
-GIT_INLINE(void *) git___compare_and_swap(
- void * volatile *ptr, void *oldval, void *newval)
-{
- if (*ptr == oldval)
- *ptr = newval;
- else
- oldval = newval;
- return oldval;
-}
-
-GIT_INLINE(volatile void *) git___swap(
- void * volatile *ptr, void *newval)
-{
- volatile void *old = *ptr;
- *ptr = newval;
- return old;
-}
-
-#ifdef GIT_ARCH_64
-
-GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
-{
- a->val += addend;
- return a->val;
-}
-
-GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
-{
- a->val = val;
-}
-
-GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
-{
- return (int64_t)a->val;
-}
-
-#endif
-
-#endif
-
-/* Atomically replace oldval with newval
- * @return oldval if it was replaced or newval if it was not
- */
-#define git__compare_and_swap(P,O,N) \
- git___compare_and_swap((void * volatile *)P, O, N)
-
-#define git__swap(ptr, val) (void *)git___swap((void * volatile *)&ptr, val)
-
-#define git__load(ptr) (void *)git___load((void * volatile *)&ptr)
-
-extern int git_online_cpus(void);
-
-#if defined(GIT_THREADS)
-
-# if defined(GIT_WIN32)
-# define GIT_MEMORY_BARRIER MemoryBarrier()
-# elif defined(GIT_BUILTIN_ATOMIC)
-# define GIT_MEMORY_BARRIER __atomic_thread_fence(__ATOMIC_SEQ_CST)
-# elif defined(GIT_BUILTIN_SYNC)
-# define GIT_MEMORY_BARRIER __sync_synchronize()
-# endif
-
-#else
-
-# define GIT_MEMORY_BARRIER /* noop */
-
-#endif
-
-#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#if !defined(GIT_THREADS)
+
+#define TLSDATA_MAX 16
+
+typedef struct {
+ void *value;
+ void (GIT_SYSTEM_CALL *destroy_fn)(void *);
+} tlsdata_value;
+
+static tlsdata_value tlsdata_values[TLSDATA_MAX];
+static int tlsdata_cnt = 0;
+
+int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
+{
+ if (tlsdata_cnt >= TLSDATA_MAX)
+ return -1;
+
+ tlsdata_values[tlsdata_cnt].value = NULL;
+ tlsdata_values[tlsdata_cnt].destroy_fn = destroy_fn;
+
+ *key = tlsdata_cnt;
+ tlsdata_cnt++;
+
+ return 0;
+}
+
+int git_tlsdata_set(git_tlsdata_key key, void *value)
+{
+ if (key < 0 || key > tlsdata_cnt)
+ return -1;
+
+ tlsdata_values[key].value = value;
+ return 0;
+}
+
+void *git_tlsdata_get(git_tlsdata_key key)
+{
+ if (key < 0 || key > tlsdata_cnt)
+ return NULL;
+
+ return tlsdata_values[key].value;
+}
+
+int git_tlsdata_dispose(git_tlsdata_key key)
+{
+ void *value;
+ void (*destroy_fn)(void *) = NULL;
+
+ if (key < 0 || key > tlsdata_cnt)
+ return -1;
+
+ value = tlsdata_values[key].value;
+ destroy_fn = tlsdata_values[key].destroy_fn;
+
+ tlsdata_values[key].value = NULL;
+ tlsdata_values[key].destroy_fn = NULL;
+
+ if (value && destroy_fn)
+ destroy_fn(value);
+
+ return 0;
+}
+
+#elif defined(GIT_WIN32)
+
+int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
+{
+ DWORD fls_index = FlsAlloc(destroy_fn);
+
+ if (fls_index == FLS_OUT_OF_INDEXES)
+ return -1;
+
+ *key = fls_index;
+ return 0;
+}
+
+int git_tlsdata_set(git_tlsdata_key key, void *value)
+{
+ if (!FlsSetValue(key, value))
+ return -1;
+
+ return 0;
+}
+
+void *git_tlsdata_get(git_tlsdata_key key)
+{
+ return FlsGetValue(key);
+}
+
+int git_tlsdata_dispose(git_tlsdata_key key)
+{
+ if (!FlsFree(key))
+ return -1;
+
+ return 0;
+}
+
+#elif defined(_POSIX_THREADS)
+
+int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
+{
+ if (pthread_key_create(key, destroy_fn) != 0)
+ return -1;
+
+ return 0;
+}
+
+int git_tlsdata_set(git_tlsdata_key key, void *value)
+{
+ if (pthread_setspecific(key, value) != 0)
+ return -1;
+
+ return 0;
+}
+
+void *git_tlsdata_get(git_tlsdata_key key)
+{
+ return pthread_getspecific(key);
+}
+
+int git_tlsdata_dispose(git_tlsdata_key key)
+{
+ if (pthread_key_delete(key) != 0)
+ return -1;
+
+ return 0;
+}
+
+#else
+# error unknown threading model
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_thread_h__
+#define INCLUDE_thread_h__
+
+#if defined(GIT_THREADS)
+
+#if defined(__clang__)
+
+# if (__clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 1))
+# error Atomic primitives do not exist on this version of clang; configure libgit2 with -DTHREADSAFE=OFF
+# else
+# define GIT_BUILTIN_ATOMIC
+# endif
+
+#elif defined(__GNUC__)
+
+# if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1))
+# error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DTHREADSAFE=OFF
+# elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
+# define GIT_BUILTIN_ATOMIC
+# else
+# define GIT_BUILTIN_SYNC
+# endif
+
+#endif
+
+#endif /* GIT_THREADS */
+
+/* Common operations even if threading has been disabled */
+typedef struct {
+#if defined(GIT_WIN32)
+ volatile long val;
+#else
+ volatile int val;
+#endif
+} git_atomic32;
+
+#ifdef GIT_ARCH_64
+
+typedef struct {
+#if defined(GIT_WIN32)
+ volatile __int64 val;
+#else
+ volatile int64_t val;
+#endif
+} git_atomic64;
+
+typedef git_atomic64 git_atomic_ssize;
+
+#define git_atomic_ssize_set git_atomic64_set
+#define git_atomic_ssize_add git_atomic64_add
+#define git_atomic_ssize_get git_atomic64_get
+
+#else
+
+typedef git_atomic32 git_atomic_ssize;
+
+#define git_atomic_ssize_set git_atomic32_set
+#define git_atomic_ssize_add git_atomic32_add
+#define git_atomic_ssize_get git_atomic32_get
+
+#endif
+
+#ifdef GIT_THREADS
+
+#ifdef GIT_WIN32
+# include "win32/thread.h"
+#else
+# include "unix/pthread.h"
+#endif
+
+/*
+ * Atomically sets the contents of *a to be val.
+ */
+GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
+{
+#if defined(GIT_WIN32)
+ InterlockedExchange(&a->val, (LONG)val);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ __atomic_store_n(&a->val, val, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ __sync_lock_test_and_set(&a->val, val);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically increments the contents of *a by 1, and stores the result back into *a.
+ * @return the result of the operation.
+ */
+GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
+{
+#if defined(GIT_WIN32)
+ return InterlockedIncrement(&a->val);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_add_fetch(&a->val, 1, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_add_and_fetch(&a->val, 1);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically adds the contents of *a and addend, and stores the result back into *a.
+ * @return the result of the operation.
+ */
+GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
+{
+#if defined(GIT_WIN32)
+ return InterlockedAdd(&a->val, addend);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_add_and_fetch(&a->val, addend);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically decrements the contents of *a by 1, and stores the result back into *a.
+ * @return the result of the operation.
+ */
+GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
+{
+#if defined(GIT_WIN32)
+ return InterlockedDecrement(&a->val);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_sub_fetch(&a->val, 1, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_sub_and_fetch(&a->val, 1);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically gets the contents of *a.
+ * @return the contents of *a.
+ */
+GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
+{
+#if defined(GIT_WIN32)
+ return (int)InterlockedCompareExchange(&a->val, 0, 0);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_load_n(&a->val, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_val_compare_and_swap(&a->val, 0, 0);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+GIT_INLINE(void *) git_atomic__compare_and_swap(
+ void * volatile *ptr, void *oldval, void *newval)
+{
+#if defined(GIT_WIN32)
+ return InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ void *foundval = oldval;
+ __atomic_compare_exchange(ptr, &foundval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+ return foundval;
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_val_compare_and_swap(ptr, oldval, newval);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+GIT_INLINE(volatile void *) git_atomic__swap(
+ void * volatile *ptr, void *newval)
+{
+#if defined(GIT_WIN32)
+ return InterlockedExchangePointer(ptr, newval);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ void * volatile foundval = NULL;
+ __atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST);
+ return foundval;
+#elif defined(GIT_BUILTIN_SYNC)
+ return (volatile void *)__sync_lock_test_and_set(ptr, newval);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
+{
+#if defined(GIT_WIN32)
+ void *newval = NULL, *oldval = NULL;
+ return (volatile void *)InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return (volatile void *)__sync_val_compare_and_swap(ptr, 0, 0);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+#ifdef GIT_ARCH_64
+
+/*
+ * Atomically adds the contents of *a and addend, and stores the result back into *a.
+ * @return the result of the operation.
+ */
+GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
+{
+#if defined(GIT_WIN32)
+ return InterlockedAdd64(&a->val, addend);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_add_and_fetch(&a->val, addend);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically sets the contents of *a to be val.
+ */
+GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
+{
+#if defined(GIT_WIN32)
+ InterlockedExchange64(&a->val, val);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ __atomic_store_n(&a->val, val, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ __sync_lock_test_and_set(&a->val, val);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically gets the contents of *a.
+ * @return the contents of *a.
+ */
+GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
+{
+#if defined(GIT_WIN32)
+ return (int64_t)InterlockedCompareExchange64(&a->val, 0, 0);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_load_n(&a->val, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_val_compare_and_swap(&a->val, 0, 0);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+#endif
+
+#else
+
+#define git_threads_global_init git__noop
+
+#define git_thread unsigned int
+#define git_thread_create(thread, start_routine, arg) git__noop()
+#define git_thread_join(id, status) git__noop()
+
+/* Pthreads Mutex */
+#define git_mutex unsigned int
+#define git_mutex_init(a) git__noop()
+#define git_mutex_init(a) git__noop()
+#define git_mutex_lock(a) git__noop()
+#define git_mutex_unlock(a) git__noop()
+#define git_mutex_free(a) git__noop()
+
+/* Pthreads condition vars */
+#define git_cond unsigned int
+#define git_cond_init(c) git__noop()
+#define git_cond_free(c) git__noop()
+#define git_cond_wait(c, l) git__noop()
+#define git_cond_signal(c) git__noop()
+#define git_cond_broadcast(c) git__noop()
+
+/* Pthreads rwlock */
+#define git_rwlock unsigned int
+#define git_rwlock_init(a) git__noop()
+#define git_rwlock_rdlock(a) git__noop()
+#define git_rwlock_rdunlock(a) git__noop()
+#define git_rwlock_wrlock(a) git__noop()
+#define git_rwlock_wrunlock(a) git__noop()
+#define git_rwlock_free(a) git__noop()
+#define GIT_RWLOCK_STATIC_INIT 0
+
+
+GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
+{
+ a->val = val;
+}
+
+GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
+{
+ return ++a->val;
+}
+
+GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
+{
+ a->val += addend;
+ return a->val;
+}
+
+GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
+{
+ return --a->val;
+}
+
+GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
+{
+ return (int)a->val;
+}
+
+GIT_INLINE(void *) git_atomic__compare_and_swap(
+ void * volatile *ptr, void *oldval, void *newval)
+{
+ void *foundval = *ptr;
+ if (foundval == oldval)
+ *ptr = newval;
+ return foundval;
+}
+
+GIT_INLINE(volatile void *) git_atomic__swap(
+ void * volatile *ptr, void *newval)
+{
+ volatile void *old = *ptr;
+ *ptr = newval;
+ return old;
+}
+
+GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
+{
+ return *ptr;
+}
+
+#ifdef GIT_ARCH_64
+
+GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
+{
+ a->val += addend;
+ return a->val;
+}
+
+GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
+{
+ a->val = val;
+}
+
+GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
+{
+ return (int64_t)a->val;
+}
+
+#endif
+
+#endif
+
+/*
+ * Atomically replace the contents of *ptr (if they are equal to oldval) with
+ * newval. ptr must point to a pointer or a value that is the same size as a
+ * pointer. This is semantically compatible with:
+ *
+ * #define git_atomic_compare_and_swap(ptr, oldval, newval) \
+ * ({ \
+ * void *foundval = *ptr; \
+ * if (foundval == oldval) \
+ * *ptr = newval; \
+ * foundval; \
+ * })
+ *
+ * @return the original contents of *ptr.
+ */
+#define git_atomic_compare_and_swap(ptr, oldval, newval) \
+ git_atomic__compare_and_swap((void * volatile *)ptr, oldval, newval)
+
+/*
+ * Atomically replace the contents of v with newval. v must be the same size as
+ * a pointer. This is semantically compatible with:
+ *
+ * #define git_atomic_swap(v, newval) \
+ * ({ \
+ * volatile void *old = v; \
+ * v = newval; \
+ * old; \
+ * })
+ *
+ * @return the original contents of v.
+ */
+#define git_atomic_swap(v, newval) \
+ (void *)git_atomic__swap((void * volatile *)&(v), newval)
+
+/*
+ * Atomically reads the contents of v. v must be the same size as a pointer.
+ * This is semantically compatible with:
+ *
+ * #define git_atomic_load(v) v
+ *
+ * @return the contents of v.
+ */
+#define git_atomic_load(v) \
+ (void *)git_atomic__load((void * volatile *)&(v))
+
+#if defined(GIT_THREADS)
+
+# if defined(GIT_WIN32)
+# define GIT_MEMORY_BARRIER MemoryBarrier()
+# elif defined(GIT_BUILTIN_ATOMIC)
+# define GIT_MEMORY_BARRIER __atomic_thread_fence(__ATOMIC_SEQ_CST)
+# elif defined(GIT_BUILTIN_SYNC)
+# define GIT_MEMORY_BARRIER __sync_synchronize()
+# endif
+
+#else
+
+# define GIT_MEMORY_BARRIER /* noop */
+
+#endif
+
+/* Thread-local data */
+
+#if !defined(GIT_THREADS)
+# define git_tlsdata_key int
+#elif defined(GIT_WIN32)
+# define git_tlsdata_key DWORD
+#elif defined(_POSIX_THREADS)
+# define git_tlsdata_key pthread_key_t
+#else
+# error unknown threading model
+#endif
+
+/**
+ * Create a thread-local data key. The destroy function will be
+ * called upon thread exit. On some platforms, it may be called
+ * when all threads have deleted their keys.
+ *
+ * Note that the tlsdata functions do not set an error message on
+ * failure; this is because the error handling in libgit2 is itself
+ * handled by thread-local data storage.
+ *
+ * @param key the tlsdata key
+ * @param destroy_fn function pointer called upon thread exit
+ * @return 0 on success, non-zero on failure
+ */
+int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *));
+
+/**
+ * Set a the thread-local value for the given key.
+ *
+ * @param key the tlsdata key to store data on
+ * @param value the pointer to store
+ * @return 0 on success, non-zero on failure
+ */
+int git_tlsdata_set(git_tlsdata_key key, void *value);
+
+/**
+ * Get the thread-local value for the given key.
+ *
+ * @param key the tlsdata key to retrieve the value of
+ * @return the pointer stored with git_tlsdata_set
+ */
+void *git_tlsdata_get(git_tlsdata_key key);
+
+/**
+ * Delete the given thread-local key.
+ *
+ * @param key the tlsdata key to dispose
+ * @return 0 on success, non-zero on failure
+ */
+int git_tlsdata_dispose(git_tlsdata_key key);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "threadstate.h"
+#include "runtime.h"
+
+/**
+ * Handle the thread-local state
+ *
+ * `git_threadstate_global_init` will be called as part
+ * of `git_libgit2_init` (which itself must be called
+ * before calling any other function in the library).
+ *
+ * This function allocates a TLS index to store the per-
+ * thread state.
+ *
+ * Any internal method that requires thread-local state
+ * will then call `git_threadstate_get()` which returns a
+ * pointer to the thread-local state structure; this
+ * structure is lazily allocated on each thread.
+ *
+ * This mechanism will register a shutdown handler
+ * (`git_threadstate_global_shutdown`) which will free the
+ * TLS index. This shutdown handler will be called by
+ * `git_libgit2_shutdown`.
+ */
+
+static git_tlsdata_key tls_key;
+
+static void threadstate_dispose(git_threadstate *threadstate)
+{
+ if (!threadstate)
+ return;
+
+ if (threadstate->error_t.message != git_buf__initbuf)
+ git__free(threadstate->error_t.message);
+ threadstate->error_t.message = NULL;
+}
+
+static void GIT_SYSTEM_CALL threadstate_free(void *threadstate)
+{
+ threadstate_dispose(threadstate);
+ git__free(threadstate);
+}
+
+static void git_threadstate_global_shutdown(void)
+{
+ git_threadstate *threadstate;
+
+ threadstate = git_tlsdata_get(tls_key);
+ git_tlsdata_set(tls_key, NULL);
+
+ threadstate_dispose(threadstate);
+ git__free(threadstate);
+
+ git_tlsdata_dispose(tls_key);
+}
+
+int git_threadstate_global_init(void)
+{
+ if (git_tlsdata_init(&tls_key, &threadstate_free) != 0)
+ return -1;
+
+ return git_runtime_shutdown_register(git_threadstate_global_shutdown);
+}
+
+git_threadstate *git_threadstate_get(void)
+{
+ git_threadstate *threadstate;
+
+ if ((threadstate = git_tlsdata_get(tls_key)) != NULL)
+ return threadstate;
+
+ if ((threadstate = git__calloc(1, sizeof(git_threadstate))) == NULL ||
+ git_buf_init(&threadstate->error_buf, 0) < 0)
+ return NULL;
+
+ git_tlsdata_set(tls_key, threadstate);
+ return threadstate;
+}
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_threadstate_h__
+#define INCLUDE_threadstate_h__
+
+#include "common.h"
+
+typedef struct {
+ git_error *last_error;
+ git_error error_t;
+ git_buf error_buf;
+ char oid_fmt[GIT_OID_HEXSZ+1];
+} git_threadstate;
+
+extern int git_threadstate_global_init(void);
+extern git_threadstate *git_threadstate_get(void);
+
+#define GIT_THREADSTATE (git_threadstate_get())
+
+#endif
#include "trace.h"
#include "buffer.h"
-#include "global.h"
+#include "runtime.h"
#include "git2/trace.h"
#ifdef GIT_TRACE
int git_trace_set(git_trace_level_t level, git_trace_cb callback)
{
#ifdef GIT_TRACE
- assert(level == 0 || callback != NULL);
+ GIT_ASSERT_ARG(level == 0 || callback != NULL);
git_trace__data.level = level;
git_trace__data.callback = callback;
GIT_INLINE(void) git_trace__write_fmt(
git_trace_level_t level,
- const char *fmt, ...)
+ const char *fmt,
+ va_list ap)
{
git_trace_cb callback = git_trace__data.callback;
git_buf message = GIT_BUF_INIT;
- va_list ap;
- va_start(ap, fmt);
git_buf_vprintf(&message, fmt, ap);
- va_end(ap);
callback(level, git_buf_cstr(&message));
git_buf_dispose(&message);
}
-#define git_trace_level() (git_trace__data.level)
-#define git_trace(l, ...) { \
- if (git_trace__data.level >= l && \
- git_trace__data.callback != NULL) { \
- git_trace__write_fmt(l, __VA_ARGS__); \
- } \
- }
+#define git_trace_level() (git_trace__data.level)
+
+GIT_INLINE(void) git_trace(git_trace_level_t level, const char *fmt, ...)
+{
+ if (git_trace__data.level >= level &&
+ git_trace__data.callback != NULL) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ git_trace__write_fmt(level, fmt, ap);
+ va_end(ap);
+ }
+}
#else
GIT_UNUSED(fmt);
}
-#define git_trace_level() ((git_trace_level_t)0)
-#define git_trace git_trace__null
+#define git_trace_level() ((git_trace_level_t)0)
+#define git_trace git_trace__null
#endif
return len - ignore_non_trailer(buf, len);
}
-static char *extract_trailer_block(const char *message, size_t* len)
+static char *extract_trailer_block(const char *message, size_t *len)
{
size_t patch_start = find_patch_start(message);
size_t trailer_end = find_trailer_end(message, patch_start);
int git_transaction_config_new(git_transaction **out, git_config *cfg)
{
git_transaction *tx;
- assert(out && cfg);
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(cfg);
tx = git__calloc(1, sizeof(git_transaction));
GIT_ERROR_CHECK_ALLOC(tx);
git_pool pool;
git_transaction *tx = NULL;
- assert(out && repo);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
if ((error = git_pool_init(&pool, 1)) < 0)
goto on_error;
int error;
transaction_node *node;
- assert(tx && refname);
+ GIT_ASSERT_ARG(tx);
+ GIT_ASSERT_ARG(refname);
node = git_pool_mallocz(&tx->pool, sizeof(transaction_node));
GIT_ERROR_CHECK_ALLOC(node);
int error;
transaction_node *node;
- assert(tx && refname && target);
+ GIT_ASSERT_ARG(tx);
+ GIT_ASSERT_ARG(refname);
+ GIT_ASSERT_ARG(target);
if ((error = find_locked(&node, tx, refname)) < 0)
return error;
int error;
transaction_node *node;
- assert(tx && refname && target);
+ GIT_ASSERT_ARG(tx);
+ GIT_ASSERT_ARG(refname);
+ GIT_ASSERT_ARG(target);
if ((error = find_locked(&node, tx, refname)) < 0)
return error;
int error;
transaction_node *node;
- assert(tx && refname && reflog);
+ GIT_ASSERT_ARG(tx);
+ GIT_ASSERT_ARG(refname);
+ GIT_ASSERT_ARG(reflog);
if ((error = find_locked(&node, tx, refname)) < 0)
return error;
transaction_node *node;
int error = 0;
- assert(tx);
+ GIT_ASSERT_ARG(tx);
if (tx->type == TRANSACTION_CONFIG) {
error = git_config_unlock(tx->cfg, true);
transaction_node *node;
git_pool pool;
- assert(tx);
+ if (!tx)
+ return;
if (tx->type == TRANSACTION_CONFIG) {
if (tx->cfg) {
size_t i;
int error = 0;
- assert(scheme);
- assert(cb);
+ GIT_ASSERT_ARG(scheme);
+ GIT_ASSERT_ARG(cb);
if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
goto on_error;
size_t i;
int error = 0;
- assert(scheme);
+ GIT_ASSERT_ARG(scheme);
if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
goto done;
{
git_credential_userpass_plaintext *cred;
git_buf raw = GIT_BUF_INIT;
- int error = -1;
+ int error = GIT_EAUTH;
GIT_UNUSED(ctx);
{
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
- assert(ctx && ctx->configured && challenge);
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(challenge);
+ GIT_ASSERT(ctx->configured);
git__free(ctx->challenge);
size_t challenge_len;
int error = 0;
- assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDENTIAL_DEFAULT);
+ GIT_ASSERT_ARG(buf);
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(cred);
+
+ GIT_ASSERT(ctx->configured);
+ GIT_ASSERT(cred->credtype == GIT_CREDENTIAL_DEFAULT);
if (ctx->complete)
return 0;
{
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
return (ctx->complete == 1);
}
if (!ctx->oid) {
git_error_set(GIT_ERROR_NET, "negotiate authentication is not supported");
- return -1;
+ return GIT_EAUTH;
}
git_buf_puts(&ctx->target, "HTTP@");
#ifdef GIT_NTLM
-#include "ntlm.h"
+#include "ntlmclient.h"
typedef struct {
git_http_auth_context parent;
{
http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
- assert(ctx && challenge);
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(challenge);
git__free(ctx->challenge);
char *domain = NULL, *domainuser = NULL;
int error = 0;
- assert(_cred->credtype == GIT_CREDENTIAL_USERPASS_PLAINTEXT);
+ GIT_ASSERT(_cred->credtype == GIT_CREDENTIAL_USERPASS_PLAINTEXT);
cred = (git_credential_userpass_plaintext *)_cred;
if ((sep = strchr(cred->username, '\\')) != NULL) {
git_buf input_buf = GIT_BUF_INIT;
const unsigned char *msg;
size_t challenge_len, msg_len;
- int error = -1;
+ int error = GIT_EAUTH;
- assert(buf && ctx && ctx->ntlm);
+ GIT_ASSERT_ARG(buf);
+ GIT_ASSERT_ARG(ctx);
+
+ GIT_ASSERT(ctx->ntlm);
challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
{
http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
- assert(ctx);
+ GIT_ASSERT_ARG(ctx);
return (ctx->complete == true);
}
{
git_credential_userpass_plaintext *c;
- assert(cred && username && password);
+ GIT_ASSERT_ARG(cred);
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(password);
c = git__malloc(sizeof(git_credential_userpass_plaintext));
GIT_ERROR_CHECK_ALLOC(c);
{
git_credential_ssh_key *c;
- assert(username && cred && privatekey);
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(cred);
+ GIT_ASSERT_ARG(privatekey);
c = git__calloc(1, sizeof(git_credential_ssh_key));
GIT_ERROR_CHECK_ALLOC(c);
{
git_credential_ssh_interactive *c;
- assert(out && username && prompt_callback);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(prompt_callback);
c = git__calloc(1, sizeof(git_credential_ssh_interactive));
GIT_ERROR_CHECK_ALLOC(c);
int git_credential_ssh_key_from_agent(git_credential **cred, const char *username) {
git_credential_ssh_key *c;
- assert(username && cred);
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(cred);
c = git__calloc(1, sizeof(git_credential_ssh_key));
GIT_ERROR_CHECK_ALLOC(c);
{
git_credential_ssh_custom *c;
- assert(username && cred);
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(cred);
c = git__calloc(1, sizeof(git_credential_ssh_custom));
GIT_ERROR_CHECK_ALLOC(c);
{
git_credential_default *c;
- assert(cred);
+ GIT_ASSERT_ARG(cred);
c = git__calloc(1, sizeof(git_credential_default));
GIT_ERROR_CHECK_ALLOC(c);
git_credential_username *c;
size_t len, allocsize;
- assert(cred);
+ GIT_ASSERT_ARG(cred);
len = strlen(username);
{
git_subtransport *t = (git_subtransport *) subtransport;
- assert(!t->current_stream);
+ GIT_ASSERT(!t->current_stream);
GIT_UNUSED(t);
{
git_subtransport *t = (git_subtransport *) subtransport;
- assert(!t->current_stream);
-
git__free(t);
}
#include "buffer.h"
#include "net.h"
#include "netops.h"
-#include "global.h"
#include "remote.h"
#include "git2/sys/credential.h"
#include "smart.h"
const char *username,
const char *password)
{
+ GIT_ASSERT_ARG(username);
+
+ if (!password)
+ password = "";
+
if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)
return git_credential_userpass_plaintext_new(cred, username, password);
/* Start with URL-specified credentials, if there were any. */
if ((allowed_credtypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT) &&
!server->url_cred_presented &&
- server->url.username &&
- server->url.password) {
+ server->url.username) {
error = apply_url_credentials(&server->cred, allowed_credtypes, server->url.username, server->url.password);
server->url_cred_presented = 1;
if (error > 0) {
git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type);
- error = -1;
+ error = GIT_EAUTH;
}
if (!error)
if (response->server_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support");
- return -1;
+ return GIT_EAUTH;
}
/* Otherwise, prompt for credentials. */
if (response->proxy_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
- return -1;
+ return GIT_EAUTH;
}
/* Otherwise, prompt for credentials. */
} else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED ||
response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure");
- return -1;
+ return GIT_EAUTH;
}
if (response->status != GIT_HTTP_STATUS_OK) {
{
const char *proxy;
git_remote *remote;
- bool use_ssl;
char *config = NULL;
int error = 0;
case GIT_PROXY_AUTO:
remote = transport->owner->owner;
- use_ssl = !strcmp(transport->server.url.scheme, "https");
- error = git_remote__get_http_proxy(remote, use_ssl, &config);
+ error = git_remote__http_proxy(&config, remote, &transport->server.url);
if (error || !config)
goto done;
if (stream->state == HTTP_STATE_SENDING_REQUEST) {
git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
- error = -1;
+ error = GIT_ERROR; /* not GIT_EAUTH, because the exact cause is unclear */
goto done;
}
- assert (stream->state == HTTP_STATE_RECEIVING_RESPONSE);
+ GIT_ASSERT(stream->state == HTTP_STATE_RECEIVING_RESPONSE);
error = git_http_client_read_body(transport->http_client, buffer, buffer_size);
if (stream->state == HTTP_STATE_NONE) {
git_error_set(GIT_ERROR_HTTP,
"too many redirects or authentication replays");
- error = -1;
+ error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */
goto done;
}
- assert(stream->state == HTTP_STATE_SENDING_REQUEST);
+ GIT_ASSERT(stream->state == HTTP_STATE_SENDING_REQUEST);
error = git_http_client_send_body(transport->http_client, buffer, len);
(error = handle_response(&complete, stream, &response, false)) < 0)
goto done;
- assert(complete);
+ GIT_ASSERT(complete);
stream->state = HTTP_STATE_RECEIVING_RESPONSE;
}
const http_service *service;
int error;
- assert(out && t);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(t);
*out = NULL;
GIT_UNUSED(param);
- assert(out);
+ GIT_ASSERT_ARG(out);
transport = git__calloc(sizeof(http_subtransport), 1);
GIT_ERROR_CHECK_ALLOC(transport);
#define INCLUDE_transports_http_h__
#include "buffer.h"
+#include "settings.h"
#include "httpclient.h"
#define GIT_HTTP_REPLAY_MAX 15
#include "http_parser.h"
#include "vector.h"
#include "trace.h"
-#include "global.h"
#include "httpclient.h"
#include "http.h"
#include "auth.h"
void git_http_response_dispose(git_http_response *response)
{
- assert(response);
+ if (!response)
+ return;
git__free(response->content_type);
git__free(response->location);
return 0;
}
- assert(ctx->output_size >= ctx->output_written);
+ GIT_ASSERT(ctx->output_size >= ctx->output_written);
max_len = min(ctx->output_size - ctx->output_written, len);
max_len = min(max_len, INT_MAX);
free_auth_context(server);
} else if (!token.size) {
git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challenge");
- error = -1;
+ error = GIT_EAUTH;
goto done;
}
request->proxy_credentials);
}
+static int puts_host_and_port(git_buf *buf, git_net_url *url, bool force_port)
+{
+ bool ipv6 = git_net_url_is_ipv6(url);
+
+ if (ipv6)
+ git_buf_putc(buf, '[');
+
+ git_buf_puts(buf, url->host);
+
+ if (ipv6)
+ git_buf_putc(buf, ']');
+
+ if (force_port || !git_net_url_is_default_port(url)) {
+ git_buf_putc(buf, ':');
+ git_buf_puts(buf, url->port);
+ }
+
+ return git_buf_oom(buf) ? -1 : 0;
+}
+
static int generate_connect_request(
git_http_client *client,
git_http_request *request)
git_buf_clear(&client->request_msg);
buf = &client->request_msg;
- git_buf_printf(buf, "CONNECT %s:%s HTTP/1.1\r\n",
- client->server.url.host, client->server.url.port);
+ git_buf_puts(buf, "CONNECT ");
+ puts_host_and_port(buf, &client->server.url, true);
+ git_buf_puts(buf, " HTTP/1.1\r\n");
git_buf_puts(buf, "User-Agent: ");
git_http__user_agent(buf);
git_buf_puts(buf, "\r\n");
- git_buf_printf(buf, "Host: %s\r\n", client->proxy.url.host);
+ git_buf_puts(buf, "Host: ");
+ puts_host_and_port(buf, &client->server.url, true);
+ git_buf_puts(buf, "\r\n");
if ((error = apply_proxy_credentials(buf, client, request) < 0))
return -1;
return git_buf_oom(buf) ? -1 : 0;
}
+static bool use_connect_proxy(git_http_client *client)
+{
+ return client->proxy.url.host && !strcmp(client->server.url.scheme, "https");
+}
+
static int generate_request(
git_http_client *client,
git_http_request *request)
size_t i;
int error;
- assert(client && request);
+ GIT_ASSERT_ARG(client);
+ GIT_ASSERT_ARG(request);
git_buf_clear(&client->request_msg);
buf = &client->request_msg;
git_http__user_agent(buf);
git_buf_puts(buf, "\r\n");
- git_buf_printf(buf, "Host: %s", request->url->host);
-
- if (!git_net_url_is_default_port(request->url))
- git_buf_printf(buf, ":%s", request->url->port);
-
+ git_buf_puts(buf, "Host: ");
+ puts_host_and_port(buf, request->url, false);
git_buf_puts(buf, "\r\n");
if (request->accept)
git_buf_printf(buf, "Expect: 100-continue\r\n");
if ((error = apply_server_credentials(buf, client, request)) < 0 ||
- (error = apply_proxy_credentials(buf, client, request)) < 0)
+ (!use_connect_proxy(client) &&
+ (error = apply_proxy_credentials(buf, client, request)) < 0))
return error;
if (request->custom_headers) {
{
int ret, diff = 0;
- assert(client && request && request->url);
+ GIT_ASSERT_ARG(client);
+ GIT_ASSERT_ARG(request);
+
+ GIT_ASSERT(request->url);
if ((ret = server_setup_from_url(&client->server, request->url)) < 0)
return ret;
int error;
if (!client->proxy_connected || !client->keepalive) {
- git_trace(GIT_TRACE_DEBUG, "Connecting to proxy %s:%s",
+ git_trace(GIT_TRACE_DEBUG, "Connecting to proxy %s port %s",
client->proxy.url.host, client->proxy.url.port);
if ((error = server_create_stream(&client->proxy)) < 0 ||
(error = git_http_client_skip_body(client)) < 0)
goto done;
- assert(client->state == DONE);
+ GIT_ASSERT(client->state == DONE);
if (response.status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
save_early_response(client, &response);
reset_parser(client);
/* Reconnect to the proxy if necessary. */
- use_proxy = client->proxy.url.host &&
- !strcmp(client->server.url.scheme, "https");
+ use_proxy = use_connect_proxy(client);
if (use_proxy) {
if (!client->proxy_connected || !client->keepalive ||
goto on_error;
}
- git_trace(GIT_TRACE_DEBUG, "Connecting to remote %s:%s",
+ git_trace(GIT_TRACE_DEBUG, "Connecting to remote %s port %s",
client->server.url.host, client->server.url.port);
if ((error = server_connect(client)) < 0)
* final byte when paused in a callback. Consume that byte.
* https://github.com/nodejs/http-parser/issues/97
*/
- assert(client->read_buf.size > parsed_len);
+ GIT_ASSERT(client->read_buf.size > parsed_len);
http_parser_pause(parser, 0);
git_http_response response = {0};
int error = -1;
- assert(client && request);
+ GIT_ASSERT_ARG(client);
+ GIT_ASSERT_ARG(request);
/* If the client did not finish reading, clean up the stream. */
if (client->state == READING_BODY)
git_buf hdr = GIT_BUF_INIT;
int error;
- assert(client);
+ GIT_ASSERT_ARG(client);
/* If we're waiting for proxy auth, don't sending more requests. */
if (client->state == HAS_EARLY_RESPONSE)
server = &client->server;
if (client->request_body_len) {
- assert(buffer_len <= client->request_body_remain);
+ GIT_ASSERT(buffer_len <= client->request_body_remain);
if ((error = stream_write(server, buffer, buffer_len)) < 0)
goto done;
{
int error = 0;
- assert(client && client->state == SENDING_BODY);
+ GIT_ASSERT_ARG(client);
+ GIT_ASSERT(client->state == SENDING_BODY);
if (client->request_body_len && client->request_body_remain) {
git_error_set(GIT_ERROR_HTTP, "truncated write");
http_parser_context parser_context = {0};
int error;
- assert(response && client);
+ GIT_ASSERT_ARG(response);
+ GIT_ASSERT_ARG(client);
if (client->state == SENDING_BODY) {
if ((error = complete_request(client)) < 0)
goto done;
}
- assert(client->state == READING_BODY || client->state == DONE);
+ GIT_ASSERT(client->state == READING_BODY || client->state == DONE);
done:
git_buf_dispose(&parser_context.parse_header_name);
break;
}
- assert(parser_context.output_written <= INT_MAX);
+ GIT_ASSERT(parser_context.output_written <= INT_MAX);
error = (int)parser_context.output_written;
done:
"unexpected data handled in callback");
error = -1;
}
- } while (!error);
+ } while (error >= 0 && client->state != DONE);
if (error < 0)
client->connected = 0;
{
git_http_client *client;
- assert(out);
+ GIT_ASSERT_ARG(out);
client = git__calloc(1, sizeof(git_http_client));
GIT_ERROR_CHECK_ALLOC(client);
/*
* Sends a request to the host specified by the request URL. If the
- * method is POST, either the the content_length or the chunked flag must
+ * method is POST, either the content_length or the chunked flag must
* be specified. The body should be provided in subsequent calls to
* git_http_client_send_body.
*
char *url;
int direction;
int flags;
- git_atomic cancelled;
+ git_atomic32 cancelled;
git_repository *repo;
git_transport_message_cb progress_cb;
git_transport_message_cb error_cb;
git_remote_head *head;
git_strarray ref_names = {0};
- assert(t);
+ GIT_ASSERT_ARG(t);
if (git_reference_list(&ref_names, t->repo) < 0)
goto on_error;
{
transport_local *t = (transport_local *)transport;
- git_atomic_set(&t->cancelled, 1);
+ git_atomic32_set(&t->cancelled, 1);
}
static int local_close(git_transport *transport)
size_t old_len, bytes_read;
int error;
- assert(t->current_stream);
+ GIT_ASSERT(t->current_stream);
old_len = buf->offset;
if (t->packetsize_cb && !t->cancelled.val) {
error = t->packetsize_cb(bytes_read, t->packetsize_payload);
if (error) {
- git_atomic_set(&t->cancelled, 1);
+ git_atomic32_set(&t->cancelled, 1);
return GIT_EUSER;
}
}
t->url = git__strdup(url);
GIT_ERROR_CHECK_ALLOC(t->url);
+ git_proxy_options_clear(&t->proxy);
+
if (git_proxy_options_dup(&t->proxy, proxy) < 0)
return -1;
return error;
/* If this is a stateful implementation, the stream we get back should be the same */
- assert(t->rpc || t->current_stream == stream);
+ GIT_ASSERT(t->rpc || t->current_stream == stream);
/* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = stream;
return error;
/* If this is a stateful implementation, the stream we get back should be the same */
- assert(t->rpc || t->current_stream == *stream);
+ GIT_ASSERT(t->rpc || t->current_stream == *stream);
/* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = *stream;
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
- git_atomic_set(&t->cancelled, 1);
+ git_atomic32_set(&t->cancelled, 1);
}
static int git_smart__is_connected(git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
- assert(transport && cert && hostname);
+ GIT_ASSERT_ARG(transport);
+ GIT_ASSERT_ARG(cert);
+ GIT_ASSERT_ARG(hostname);
if (!t->certificate_check_cb)
return GIT_PASSTHROUGH;
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
- assert(out && transport);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(transport);
if (!t->cred_acquire_cb)
return GIT_PASSTHROUGH;
git_vector refs;
git_vector heads;
git_vector common;
- git_atomic cancelled;
+ git_atomic32 cancelled;
packetsize_cb packetsize_cb;
void *packetsize_payload;
unsigned rpc : 1,
continue;
}
- gitno_consume(buf, line_end);
+ if (gitno_consume(buf, line_end) < 0)
+ return -1;
+
if (pkt->type == GIT_PKT_ERR) {
git_error_set(GIT_ERROR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error);
git__free(pkt);
}
} while (error);
- gitno_consume(buf, line_end);
+ if (gitno_consume(buf, line_end) < 0)
+ return -1;
+
if (out_type != NULL)
*out_type = pkt->type;
if (out_pkt != NULL)
/* We might have something in the buffer already from negotiate_fetch */
if (t->buffer.offset > 0 && !t->cancelled.val)
if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
- git_atomic_set(&t->cancelled, 1);
+ git_atomic32_set(&t->cancelled, 1);
}
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
continue;
}
- gitno_consume(buf, line_end);
+ if (gitno_consume(buf, line_end) < 0)
+ return -1;
error = 0;
if (payload->cb) {
double current_time = git__timer();
+ double elapsed = current_time - payload->last_progress_report_time;
payload->last_bytes += size;
- if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ if (elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
payload->last_progress_report_time = current_time;
error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
}
#include <libssh2.h>
#endif
-#include "global.h"
+#include "runtime.h"
#include "git2.h"
#include "buffer.h"
#include "net.h"
{
ssh_stream *s;
- assert(stream);
+ GIT_ASSERT_ARG(stream);
s = git__calloc(sizeof(ssh_stream), 1);
GIT_ERROR_CHECK_ALLOC(s);
case GIT_CREDENTIAL_SSH_MEMORY: {
git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
- assert(c->username);
- assert(c->privatekey);
+ GIT_ASSERT(c->username);
+ GIT_ASSERT(c->privatekey);
rc = libssh2_userauth_publickey_frommemory(
session,
if (no_callback) {
git_error_set(GIT_ERROR_SSH, "authentication required but no callback set");
- return -1;
+ return GIT_EAUTH;
}
if (!(cred->credtype & auth_methods)) {
cred->free(cred);
- git_error_set(GIT_ERROR_SSH, "callback returned unsupported credentials type");
- return -1;
+ git_error_set(GIT_ERROR_SSH, "authentication callback returned unsupported credentials type");
+ return GIT_EAUTH;
}
*out = cred;
}
static int _git_ssh_session_create(
- LIBSSH2_SESSION** session,
+ LIBSSH2_SESSION **session,
git_stream *io)
{
int rc = 0;
- LIBSSH2_SESSION* s;
+ LIBSSH2_SESSION *s;
git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent);
- assert(session);
+ GIT_ASSERT_ARG(session);
s = libssh2_session_init();
if (!s) {
size_t i;
ssh_stream *s;
git_credential *cred = NULL;
- LIBSSH2_SESSION* session=NULL;
- LIBSSH2_CHANNEL* channel=NULL;
+ LIBSSH2_SESSION *session=NULL;
+ LIBSSH2_CHANNEL *channel=NULL;
t->current_stream = NULL;
if (t->owner->certificate_check_cb != NULL) {
git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
const char *key;
+ size_t cert_len;
+ int cert_type;
cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
+ key = libssh2_session_hostkey(session, &cert_len, &cert_type);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_RAW;
+ cert.hostkey = key;
+ cert.hostkey_len = cert_len;
+ switch (cert_type) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_RSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_DSS;
+ break;
+
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521;
+ break;
+#endif
+
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ED25519;
+ break;
+#endif
+ default:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_UNKNOWN;
+ }
+ }
+
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
if (key != NULL) {
{
ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
- assert(!t->current_stream);
+ GIT_ASSERT(!t->current_stream);
GIT_UNUSED(t);
{
ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
- assert(!t->current_stream);
-
git__free(t->cmd_uploadpack);
git__free(t->cmd_receivepack);
git__free(t);
/* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
if (list == NULL && !libssh2_userauth_authenticated(session)) {
ssh_error(session, "Failed to retrieve list of SSH authentication methods");
- return -1;
+ return GIT_EAUTH;
}
ptr = list;
#ifdef GIT_SSH
ssh_subtransport *t;
- assert(out);
+ GIT_ASSERT_ARG(out);
GIT_UNUSED(param);
GIT_UNUSED(owner);
GIT_UNUSED(param);
- assert(out);
+ GIT_ASSERT_ARG(out);
*out = NULL;
git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
GIT_UNUSED(owner);
GIT_UNUSED(payload);
- assert(out);
+ GIT_ASSERT_ARG(out);
*out = NULL;
git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
return -1;
}
- git__on_shutdown(shutdown_ssh);
- return 0;
+ return git_runtime_shutdown_register(shutdown_ssh);
#else
#include "smart.h"
#include "remote.h"
#include "repository.h"
-#include "global.h"
#include "http.h"
#include "git2/sys/credential.h"
# define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 0x00002000
#endif
+#ifndef WINHTTP_NO_CLIENT_CERT_CONTEXT
+# define WINHTTP_NO_CLIENT_CERT_CONTEXT NULL
+#endif
+
#ifndef HTTP_STATUS_PERMANENT_REDIRECT
# define HTTP_STATUS_PERMANENT_REDIRECT 308
#endif
DWORD post_body_len;
unsigned sent_request : 1,
received_response : 1,
- chunked : 1;
+ chunked : 1,
+ status_sending_request_reached: 1;
} winhttp_stream;
typedef struct {
native_scheme = WINHTTP_AUTH_SCHEME_BASIC;
} else {
git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
- error = -1;
+ error = GIT_EAUTH;
goto done;
}
native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
} else {
git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
- return -1;
+ return GIT_EAUTH;
}
/*
hCoInitResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (SUCCEEDED(hCoInitResult) || hCoInitResult == RPC_E_CHANGED_MODE) {
- IInternetSecurityManager* pISM;
+ IInternetSecurityManager *pISM;
/* And if the target URI is in the My Computer, Intranet, or Trusted zones */
if (SUCCEEDED(CoCreateInstance(&CLSID_InternetSecurityManager, NULL,
pISM->lpVtbl->Release(pISM);
}
- /* Only unitialize if the call to CoInitializeEx was successful. */
+ /* Only uninitialize if the call to CoInitializeEx was successful. */
if (SUCCEEDED(hCoInitResult))
CoUninitialize();
}
proxy_opts = &t->owner->proxy;
if (proxy_opts->type == GIT_PROXY_AUTO) {
/* Set proxy if necessary */
- if (git_remote__get_http_proxy(t->owner->owner, (strcmp(t->server.url.scheme, "https") == 0), &proxy_url) < 0)
+ if (git_remote__http_proxy(&proxy_url, t->owner->owner, &t->server.url) < 0)
goto on_error;
}
else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
git_buf_puts(&processed_url, t->proxy.url.scheme);
git_buf_PUTS(&processed_url, "://");
+ if (git_net_url_is_ipv6(&t->proxy.url))
+ git_buf_putc(&processed_url, '[');
+
git_buf_puts(&processed_url, t->proxy.url.host);
+ if (git_net_url_is_ipv6(&t->proxy.url))
+ git_buf_putc(&processed_url, ']');
+
if (!git_net_url_is_default_port(&t->proxy.url))
git_buf_printf(&processed_url, ":%s", t->proxy.url.port);
*/
if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) {
git_error_set(GIT_ERROR_OS, "failed to parse supported auth schemes");
- return -1;
+ return GIT_EAUTH;
}
if (WINHTTP_AUTH_SCHEME_NTLM & supported) {
DWORD status;
GIT_UNUSED(connection);
- GIT_UNUSED(ctx);
GIT_UNUSED(info_len);
- if (code != WINHTTP_CALLBACK_STATUS_SECURE_FAILURE)
- return;
-
- status = *((DWORD *)info);
-
- if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
- git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name");
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
- git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired");
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
- git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA");
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
- git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid");
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
- git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed");
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
- git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked");
- else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
- git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded");
- else
- git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
+ switch (code) {
+ case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
+ status = *((DWORD *)info);
+
+ if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
+ git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
+ git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded");
+ else
+ git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
+
+ break;
+
+ case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
+ ((winhttp_stream *) ctx)->status_sending_request_reached = 1;
+
+ break;
+ }
}
static int winhttp_connect(
winhttp_subtransport *t)
{
- wchar_t *wide_host;
+ wchar_t *wide_host = NULL;
int32_t port;
- wchar_t *wide_ua;
- git_buf ua = GIT_BUF_INIT;
+ wchar_t *wide_ua = NULL;
+ git_buf ipv6 = GIT_BUF_INIT, ua = GIT_BUF_INIT;
+ const char *host;
int error = -1;
int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
/* Prepare port */
if (git__strntol32(&port, t->server.url.port,
strlen(t->server.url.port), NULL, 10) < 0)
- return -1;
+ goto on_error;
+
+ /* IPv6? Add braces around the host. */
+ if (git_net_url_is_ipv6(&t->server.url)) {
+ if (git_buf_printf(&ipv6, "[%s]", t->server.url.host) < 0)
+ goto on_error;
+
+ host = ipv6.ptr;
+ } else {
+ host = t->server.url.host;
+ }
/* Prepare host */
- if (git__utf8_to_16_alloc(&wide_host, t->server.url.host) < 0) {
+ if (git__utf8_to_16_alloc(&wide_host, host) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
- return -1;
+ goto on_error;
}
- if ((error = git_http__user_agent(&ua)) < 0) {
- git__free(wide_host);
- return error;
- }
+ if (git_http__user_agent(&ua) < 0)
+ goto on_error;
if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
- git__free(wide_host);
- git_buf_dispose(&ua);
- return -1;
+ goto on_error;
}
- git_buf_dispose(&ua);
-
/* Establish session */
t->session = WinHttpOpen(
wide_ua,
goto on_error;
}
- if (WinHttpSetStatusCallback(t->connection, winhttp_status, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE, 0) == WINHTTP_INVALID_STATUS_CALLBACK) {
+ if (WinHttpSetStatusCallback(
+ t->connection,
+ winhttp_status,
+ WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST,
+ 0
+ ) == WINHTTP_INVALID_STATUS_CALLBACK) {
git_error_set(GIT_ERROR_OS, "failed to set status callback");
goto on_error;
}
if (error < 0)
winhttp_close_connection(t);
+ git_buf_dispose(&ua);
+ git_buf_dispose(&ipv6);
git__free(wide_host);
git__free(wide_ua);
success = WinHttpSendRequest(s->request,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0,
- WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0);
+ WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, (DWORD_PTR)s);
} else {
success = WinHttpSendRequest(s->request,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0,
- (DWORD)len, 0);
+ (DWORD)len, (DWORD_PTR)s);
}
if (success || GetLastError() != (DWORD)SEC_E_BUFFER_TOO_SMALL)
static int send_request(winhttp_stream *s, size_t len, bool chunked)
{
- int request_failed = 0, cert_valid = 1, error = 0;
- DWORD ignore_flags;
+ int request_failed = 1, error, attempts = 0;
+ DWORD ignore_flags, send_request_error;
git_error_clear();
- if ((error = do_send_request(s, len, chunked)) < 0) {
- if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) {
- git_error_set(GIT_ERROR_OS, "failed to send request");
- return -1;
- }
- request_failed = 1;
- cert_valid = 0;
- }
+ while (request_failed && attempts++ < 3) {
+ int cert_valid = 1;
+ int client_cert_requested = 0;
+ request_failed = 0;
+ if ((error = do_send_request(s, len, chunked)) < 0) {
+ send_request_error = GetLastError();
+ request_failed = 1;
+ switch (send_request_error) {
+ case ERROR_WINHTTP_SECURE_FAILURE:
+ cert_valid = 0;
+ break;
+ case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
+ client_cert_requested = 1;
+ break;
+ default:
+ git_error_set(GIT_ERROR_OS, "failed to send request");
+ return -1;
+ }
+ }
- git_error_clear();
- if ((error = certificate_check(s, cert_valid)) < 0) {
- if (!git_error_last())
- git_error_set(GIT_ERROR_OS, "user cancelled certificate check");
+ /*
+ * Only check the certificate if we were able to reach the sending request phase, or
+ * received a secure failure error. Otherwise, the server certificate won't be available
+ * since the request wasn't able to complete (e.g. proxy auth required)
+ */
+ if (!cert_valid ||
+ (!request_failed && s->status_sending_request_reached)) {
+ git_error_clear();
+ if ((error = certificate_check(s, cert_valid)) < 0) {
+ if (!git_error_last())
+ git_error_set(GIT_ERROR_OS, "user cancelled certificate check");
- return error;
- }
+ return error;
+ }
+ }
- /* if neither the request nor the certificate check returned errors, we're done */
- if (!request_failed)
- return 0;
+ /* if neither the request nor the certificate check returned errors, we're done */
+ if (!request_failed)
+ return 0;
- ignore_flags = no_check_cert_flags;
+ if (!cert_valid) {
+ ignore_flags = no_check_cert_flags;
+ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) {
+ git_error_set(GIT_ERROR_OS, "failed to set security options");
+ return -1;
+ }
+ }
- if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) {
- git_error_set(GIT_ERROR_OS, "failed to set security options");
- return -1;
+ if (client_cert_requested) {
+ /*
+ * Client certificates are not supported, explicitly tell the server that
+ * (it's possible a client certificate was requested but is not required)
+ */
+ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, WINHTTP_NO_CLIENT_CERT_CONTEXT, 0)) {
+ git_error_set(GIT_ERROR_OS, "failed to set client cert context");
+ return -1;
+ }
+ }
}
- if ((error = do_send_request(s, len, chunked)) < 0)
- git_error_set(GIT_ERROR_OS, "failed to send request with unchecked certificate");
-
return error;
}
/* Enforce a reasonable cap on the number of replays */
if (replay_count++ >= GIT_HTTP_REPLAY_MAX) {
git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
- return -1;
+ return GIT_ERROR; /* not GIT_EAUTH because the exact cause is not clear */
}
/* Connect if necessary */
}
if (s->chunked) {
- assert(s->verb == post_verb);
+ GIT_ASSERT(s->verb == post_verb);
/* Flush, if necessary */
if (s->chunk_buffer_len > 0 &&
}
len -= bytes_read;
- assert(bytes_read == bytes_written);
+ GIT_ASSERT(bytes_read == bytes_written);
}
git__free(buffer);
if (error < 0) {
return error;
} else if (!error) {
- assert(t->server.cred);
+ GIT_ASSERT(t->server.cred);
winhttp_stream_close(s);
goto replay;
}
if (error < 0) {
return error;
} else if (!error) {
- assert(t->proxy.cred);
+ GIT_ASSERT(t->proxy.cred);
winhttp_stream_close(s);
goto replay;
}
return -1;
}
- assert((DWORD)len == bytes_written);
+ GIT_ASSERT((DWORD)len == bytes_written);
return 0;
}
return -1;
}
- assert((DWORD)len == bytes_written);
+ GIT_ASSERT((DWORD)len == bytes_written);
s->post_body_len += bytes_written;
break;
default:
- assert(0);
+ GIT_ASSERT(0);
}
if (!ret)
static int valid_entry_name(git_repository *repo, const char *filename)
{
return *filename != '\0' &&
- git_path_isvalid(repo, filename, 0,
+ git_path_validate(repo, filename, 0,
GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_PATH_REJECT_SLASH);
}
{
git_tree_entry *cpy;
- assert(source);
+ GIT_ASSERT_ARG(source);
cpy = alloc_entry(source->filename, source->filename_len, source->oid);
if (cpy == NULL)
const char *git_tree_entry_name(const git_tree_entry *entry)
{
- assert(entry);
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
return entry->filename;
}
const git_oid *git_tree_entry_id(const git_tree_entry *entry)
{
- assert(entry);
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
return entry->oid;
}
git_object_t git_tree_entry_type(const git_tree_entry *entry)
{
- assert(entry);
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, GIT_OBJECT_INVALID);
if (S_ISGITLINK(entry->attr))
return GIT_OBJECT_COMMIT;
git_repository *repo,
const git_tree_entry *entry)
{
- assert(entry && object_out);
+ GIT_ASSERT_ARG(entry);
+ GIT_ASSERT_ARG(object_out);
+
return git_object_lookup(object_out, repo, entry->oid, GIT_OBJECT_ANY);
}
const git_tree_entry *git_tree_entry_byname(
const git_tree *tree, const char *filename)
{
- assert(tree && filename);
+ GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(filename, NULL);
return entry_fromname(tree, filename, strlen(filename));
}
const git_tree_entry *git_tree_entry_byindex(
const git_tree *tree, size_t idx)
{
- assert(tree);
+ GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL);
return git_array_get(tree->entries, idx);
}
size_t i;
const git_tree_entry *e;
- assert(tree);
+ GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL);
git_array_foreach(tree->entries, i, e) {
if (memcmp(&e->oid->id, &id->id, sizeof(id->id)) == 0)
size_t git_tree_entrycount(const git_tree *tree)
{
- assert(tree);
+ GIT_ASSERT_ARG_WITH_RETVAL(tree, 0);
return tree->entries.size;
}
size_t git_treebuilder_entrycount(git_treebuilder *bld)
{
- assert(bld);
+ GIT_ASSERT_ARG_WITH_RETVAL(bld, 0);
return git_strmap_size(bld->map);
}
return 0;
}
+static int git_treebuilder__write_with_buffer(
+ git_oid *oid,
+ git_treebuilder *bld,
+ git_buf *buf)
+{
+ int error = 0;
+ size_t i, entrycount;
+ git_odb *odb;
+ git_tree_entry *entry;
+ git_vector entries = GIT_VECTOR_INIT;
+
+ git_buf_clear(buf);
+
+ entrycount = git_strmap_size(bld->map);
+ if ((error = git_vector_init(&entries, entrycount, entry_sort_cmp)) < 0)
+ goto out;
+
+ if (buf->asize == 0 &&
+ (error = git_buf_grow(buf, entrycount * 72)) < 0)
+ goto out;
+
+ git_strmap_foreach_value(bld->map, entry, {
+ if ((error = git_vector_insert(&entries, entry)) < 0)
+ goto out;
+ });
+
+ git_vector_sort(&entries);
+
+ for (i = 0; i < entries.length && !error; ++i) {
+ entry = git_vector_get(&entries, i);
+
+ git_buf_printf(buf, "%o ", entry->attr);
+ git_buf_put(buf, entry->filename, entry->filename_len + 1);
+ git_buf_put(buf, (char *)entry->oid->id, GIT_OID_RAWSZ);
+
+ if (git_buf_oom(buf)) {
+ error = -1;
+ goto out;
+ }
+ }
+
+ if ((error = git_repository_odb__weakptr(&odb, bld->repo)) == 0)
+ error = git_odb_write(oid, odb, buf->ptr, buf->size, GIT_OBJECT_TREE);
+
+out:
+ git_vector_free(&entries);
+
+ return error;
+}
+
static int append_entry(
git_treebuilder *bld,
const char *filename,
}
}
- if (git_treebuilder_write_with_buffer(oid, bld, shared_buf) < 0)
+ if (git_treebuilder__write_with_buffer(oid, bld, shared_buf) < 0)
goto on_error;
git_treebuilder_free(bld);
git_buf shared_buf = GIT_BUF_INIT;
bool old_ignore_case = false;
- assert(oid && index && repo);
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(repo);
if (git_index_has_conflicts(index)) {
git_error_set(GIT_ERROR_INDEX,
git_treebuilder *bld;
size_t i;
- assert(builder_p && repo);
+ GIT_ASSERT_ARG(builder_p);
+ GIT_ASSERT_ARG(repo);
bld = git__calloc(1, sizeof(git_treebuilder));
GIT_ERROR_CHECK_ALLOC(bld);
git_tree_entry *entry;
int error;
- assert(bld && id && filename);
+ GIT_ASSERT_ARG(bld);
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(filename);
if ((error = check_entry(bld->repo, filename, id, filemode)) < 0)
return error;
static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
{
- assert(bld && filename);
+ GIT_ASSERT_ARG_WITH_RETVAL(bld, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(filename, NULL);
+
return git_strmap_get(bld->map, filename);
}
int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
{
- int error;
- git_buf buffer = GIT_BUF_INIT;
-
- error = git_treebuilder_write_with_buffer(oid, bld, &buffer);
-
- git_buf_dispose(&buffer);
- return error;
-}
-
-int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *tree)
-{
- int error = 0;
- size_t i, entrycount;
- git_odb *odb;
- git_tree_entry *entry;
- git_vector entries = GIT_VECTOR_INIT;
-
- assert(bld);
- assert(tree);
-
- git_buf_clear(tree);
-
- entrycount = git_strmap_size(bld->map);
- if ((error = git_vector_init(&entries, entrycount, entry_sort_cmp)) < 0)
- goto out;
-
- if (tree->asize == 0 &&
- (error = git_buf_grow(tree, entrycount * 72)) < 0)
- goto out;
-
- git_strmap_foreach_value(bld->map, entry, {
- if ((error = git_vector_insert(&entries, entry)) < 0)
- goto out;
- });
-
- git_vector_sort(&entries);
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(bld);
- for (i = 0; i < entries.length && !error; ++i) {
- entry = git_vector_get(&entries, i);
-
- git_buf_printf(tree, "%o ", entry->attr);
- git_buf_put(tree, entry->filename, entry->filename_len + 1);
- git_buf_put(tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
-
- if (git_buf_oom(tree)) {
- error = -1;
- goto out;
- }
- }
-
- if ((error = git_repository_odb__weakptr(&odb, bld->repo)) == 0)
- error = git_odb_write(oid, odb, tree->ptr, tree->size, GIT_OBJECT_TREE);
-
-out:
- git_vector_free(&entries);
-
- return error;
+ return git_treebuilder__write_with_buffer(oid, bld, &bld->write_cache);
}
int git_treebuilder_filter(
const char *filename;
git_tree_entry *entry;
- assert(bld && filter);
+ GIT_ASSERT_ARG(bld);
+ GIT_ASSERT_ARG(filter);
git_strmap_foreach(bld->map, filename, entry, {
if (filter(entry, payload)) {
{
git_tree_entry *e;
- assert(bld);
+ GIT_ASSERT_ARG(bld);
git_strmap_foreach_value(bld->map, e, git_tree_entry_free(e));
git_strmap_clear(bld->map);
if (bld == NULL)
return;
+ git_buf_dispose(&bld->write_cache);
git_treebuilder_clear(bld);
git_strmap_free(bld->map);
git__free(bld);
for (j = 0; j < steps_up; j++) {
tree_stack_entry *current, *popped = git_array_pop(stack);
- assert(popped);
+ GIT_ASSERT(popped);
current = git_array_last(stack);
- assert(current);
+ GIT_ASSERT(current);
if ((error = create_popped_tree(current, popped, &component)) < 0)
goto cleanup;
}
case GIT_TREE_UPDATE_REMOVE:
{
+ tree_stack_entry *last = git_array_last(stack);
char *basename = git_path_basename(update->path);
- error = git_treebuilder_remove(git_array_last(stack)->bld, basename);
+ error = git_treebuilder_remove(last->bld, basename);
git__free(basename);
break;
}
git_vector_free(&entries);
return error;
}
+
+/* Deprecated Functions */
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *buf)
+{
+ GIT_UNUSED(buf);
+
+ return git_treebuilder_write(oid, bld);
+}
+
+#endif
struct git_treebuilder {
git_repository *repo;
git_strmap *map;
+ git_buf write_cache;
};
GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
int l, c, r;
void *lx, *cx;
- assert(size > 0);
-
l = 0;
r = (int)size - 1;
c = r >> 1;
int p_munmap(git_map *map)
{
- assert(map != NULL);
+ GIT_ASSERT_ARG(map);
munmap(map->data, map->len);
+ map->data = NULL;
+ map->len = 0;
return 0;
}
# define p_futimes futimes
#endif
+#define p_pread(f, d, s, o) pread(f, d, s, o)
+#define p_pwrite(f, d, s, o) pwrite(f, d, s, o)
+
#endif
pthread_t thread;
} git_thread;
-#define git_threads_init() (void)0
+GIT_INLINE(int) git_threads_global_init(void) { return 0; }
+
#define git_thread_create(git_thread_ptr, start_routine, arg) \
pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg)
#define git_thread_join(git_thread_ptr, status) \
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "utf8.h"
+
+#include "common.h"
+
+/*
+ * git_utf8_iterate is taken from the utf8proc project,
+ * http://www.public-software-group.org/utf8proc
+ *
+ * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany
+ *
+ * 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.
+ */
+
+static const uint8_t utf8proc_utf8class[256] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int utf8_charlen(const uint8_t *str, size_t str_len)
+{
+ uint8_t length;
+ size_t i;
+
+ length = utf8proc_utf8class[str[0]];
+ if (!length)
+ return -1;
+
+ if (str_len > 0 && length > str_len)
+ return -1;
+
+ for (i = 1; i < length; i++) {
+ if ((str[i] & 0xC0) != 0x80)
+ return -1;
+ }
+
+ return (int)length;
+}
+
+int git_utf8_iterate(uint32_t *out, const char *_str, size_t str_len)
+{
+ const uint8_t *str = (const uint8_t *)_str;
+ uint32_t uc = 0;
+ int length;
+
+ *out = 0;
+
+ if ((length = utf8_charlen(str, str_len)) < 0)
+ return -1;
+
+ switch (length) {
+ case 1:
+ uc = str[0];
+ break;
+ case 2:
+ uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
+ if (uc < 0x80) uc = -1;
+ break;
+ case 3:
+ uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
+ + (str[2] & 0x3F);
+ if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
+ (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
+ break;
+ case 4:
+ uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
+ + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
+ if (uc < 0x10000 || uc >= 0x110000) uc = -1;
+ break;
+ default:
+ return -1;
+ }
+
+ if ((uc & 0xFFFF) >= 0xFFFE)
+ return -1;
+
+ *out = uc;
+ return length;
+}
+
+size_t git_utf8_char_length(const char *_str, size_t str_len)
+{
+ const uint8_t *str = (const uint8_t *)_str;
+ size_t offset = 0, count = 0;
+
+ while (offset < str_len) {
+ int length = utf8_charlen(str + offset, str_len - offset);
+
+ if (length < 0)
+ length = 1;
+
+ offset += length;
+ count++;
+ }
+
+ return count;
+}
+
+size_t git_utf8_valid_buf_length(const char *_str, size_t str_len)
+{
+ const uint8_t *str = (const uint8_t *)_str;
+ size_t offset = 0;
+
+ while (offset < str_len) {
+ int length = utf8_charlen(str + offset, str_len - offset);
+
+ if (length < 0)
+ break;
+
+ offset += length;
+ }
+
+ return offset;
+}
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_utf8_h__
+#define INCLUDE_utf8_h__
+
+#include "common.h"
+
+/*
+ * Iterate through an UTF-8 string, yielding one codepoint at a time.
+ *
+ * @param out pointer where to store the current codepoint
+ * @param str current position in the string
+ * @param str_len size left in the string
+ * @return length in bytes of the read codepoint; -1 if the codepoint was invalid
+ */
+extern int git_utf8_iterate(uint32_t *out, const char *str, size_t str_len);
+
+/**
+ * Returns the number of characters in the given string.
+ *
+ * This function will count invalid codepoints; if any given byte is
+ * not part of a valid UTF-8 codepoint, then it will be counted toward
+ * the length in characters.
+ *
+ * In other words:
+ * 0x24 (U+0024 "$") has length 1
+ * 0xc2 0xa2 (U+00A2 "¢") has length 1
+ * 0x24 0xc2 0xa2 (U+0024 U+00A2 "$¢") has length 2
+ * 0xf0 0x90 0x8d 0x88 (U+10348 "𐍈") has length 1
+ * 0x24 0xc0 0xc1 0x34 (U+0024 <invalid> <invalid> "4) has length 4
+ *
+ * @param str string to scan
+ * @param str_len size of the string
+ * @return length in characters of the string
+ */
+extern size_t git_utf8_char_length(const char *str, size_t str_len);
+
+/**
+ * Iterate through an UTF-8 string and stops after finding any invalid UTF-8
+ * codepoints.
+ *
+ * @param str string to scan
+ * @param str_len size of the string
+ * @return length in bytes of the string that contains valid data
+ */
+extern size_t git_utf8_valid_buf_length(const char *str, size_t str_len);
+
+#endif
# include "win32/utf-conv.h"
# include "win32/w32_buffer.h"
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h>
+
# ifdef HAVE_QSORT_S
# include <search.h>
# endif
# include <Shlwapi.h>
#endif
+#if defined(hpux) || defined(__hpux) || defined(_hpux)
+# include <sys/pstat.h>
+#endif
+
int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
{
const char *p;
- int64_t n, nn;
- int c, ovfl, v, neg, ndig;
+ int64_t n, nn, v;
+ int c, ovfl, neg, ndig;
p = nptr;
neg = 0;
if (v >= base)
break;
v = neg ? -v : v;
- if (n > INT64_MAX / base || n < INT64_MIN / base) {
- ovfl = 1;
- /* Keep on iterating until the end of this number */
- continue;
- }
- nn = n * base;
- if ((v > 0 && nn > INT64_MAX - v) ||
- (v < 0 && nn < INT64_MIN - v)) {
+ if (git__multiply_int64_overflow(&nn, n, base) || git__add_int64_overflow(&n, nn, v)) {
ovfl = 1;
/* Keep on iterating until the end of this number */
continue;
}
- n = nn + v;
}
Return:
void *payload;
} git__qsort_r_glue;
-static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
+static int GIT_LIBGIT2_CALL git__qsort_r_glue_cmp(
void *payload, const void *a, const void *b)
{
git__qsort_r_glue *glue = payload;
#endif
}
-/*
- * git__utf8_iterate is taken from the utf8proc project,
- * http://www.public-software-group.org/utf8proc
- *
- * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany
- *
- * 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.
- */
-
-static const int8_t utf8proc_utf8class[256] = {
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-static int util_utf8_charlen(const uint8_t *str, size_t str_len)
-{
- size_t length, i;
-
- length = utf8proc_utf8class[str[0]];
- if (!length)
- return -1;
-
- if (str_len > 0 && length > str_len)
- return -1;
-
- for (i = 1; i < length; i++) {
- if ((str[i] & 0xC0) != 0x80)
- return -1;
- }
-
- return (int)length;
-}
-
-int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst)
-{
- int length;
- int32_t uc = -1;
-
- *dst = -1;
- length = util_utf8_charlen(str, str_len);
- if (length < 0)
- return -1;
-
- switch (length) {
- case 1:
- uc = str[0];
- break;
- case 2:
- uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
- if (uc < 0x80) uc = -1;
- break;
- case 3:
- uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
- + (str[2] & 0x3F);
- if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
- (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
- break;
- case 4:
- uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
- + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
- if (uc < 0x10000 || uc >= 0x110000) uc = -1;
- break;
- }
-
- if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
- return -1;
-
- *dst = uc;
- return length;
-}
-
-size_t git__utf8_valid_buf_length(const uint8_t *str, size_t str_len)
-{
- size_t offset = 0;
-
- while (offset < str_len) {
- int length = util_utf8_charlen(str + offset, str_len - offset);
-
- if (length < 0)
- break;
-
- offset += length;
- }
-
- return offset;
-}
-
#ifdef GIT_WIN32
int git__getenv(git_buf *out, const char *name)
{
if (value_len)
error = git_buf_put_w(out, wide_value, value_len);
- else if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
+ else if (GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_ENVVAR_NOT_FOUND)
error = GIT_ENOTFOUND;
else
git_error_set(GIT_ERROR_OS, "could not read environment variable '%s'", name);
return git_buf_puts(out, val);
}
#endif
+
+/*
+ * By doing this in two steps we can at least get
+ * the function to be somewhat coherent, even
+ * with this disgusting nest of #ifdefs.
+ */
+#ifndef _SC_NPROCESSORS_ONLN
+# ifdef _SC_NPROC_ONLN
+# define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
+# elif defined _SC_CRAY_NCPU
+# define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
+# endif
+#endif
+
+int git__online_cpus(void)
+{
+#ifdef _SC_NPROCESSORS_ONLN
+ long ncpus;
+#endif
+
+#ifdef _WIN32
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+
+ if ((int)info.dwNumberOfProcessors > 0)
+ return (int)info.dwNumberOfProcessors;
+#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+ struct pst_dynamic psd;
+
+ if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))
+ return (int)psd.psd_proc_cnt;
+#endif
+
+#ifdef _SC_NPROCESSORS_ONLN
+ if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)
+ return (int)ncpus;
+#endif
+
+ return 1;
+}
#include "buffer.h"
#include "common.h"
#include "strnlen.h"
-#include "thread-utils.h"
+#include "thread.h"
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#define bitsizeof(x) (CHAR_BIT * sizeof(x))
-#define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits))))
+#define MSB(x, bits) ((x) & (~UINT64_C(0) << (bitsizeof(x) - (bits))))
#ifndef min
# define min(a,b) ((a) < (b) ? (a) : (b))
#endif
# define GIT_CONTAINER_OF(ptr, type, member) \
__builtin_choose_expr( \
__builtin_offsetof(type, member) == 0 && \
- __builtin_types_compatible_p(typeof(&((type *) 0)->member), typeof(ptr)), \
+ __builtin_types_compatible_p(__typeof__(&((type *) 0)->member), __typeof__(ptr)), \
((type *) (ptr)), \
(void)0)
#else
extern int git__strcasesort_cmp(const char *a, const char *b);
+/*
+ * Compare some NUL-terminated `a` to a possibly non-NUL terminated
+ * `b` of length `b_len`; like `strncmp` but ensuring that
+ * `strlen(a) == b_len` as well.
+ */
+GIT_INLINE(int) git__strlcmp(const char *a, const char *b, size_t b_len)
+{
+ int cmp = strncmp(a, b, b_len);
+ return cmp ? cmp : (int)a[b_len];
+}
+
typedef struct {
- git_atomic refcount;
+ git_atomic32 refcount;
void *owner;
} git_refcount;
typedef void (*git_refcount_freeptr)(void *r);
#define GIT_REFCOUNT_INC(r) { \
- git_atomic_inc(&(r)->rc.refcount); \
+ git_atomic32_inc(&(r)->rc.refcount); \
}
#define GIT_REFCOUNT_DEC(_r, do_free) { \
git_refcount *r = &(_r)->rc; \
- int val = git_atomic_dec(&r->refcount); \
+ int val = git_atomic32_dec(&r->refcount); \
if (val <= 0 && r->owner == NULL) { do_free(_r); } \
}
#define GIT_REFCOUNT_OWN(r, o) { \
- (void)git__swap((r)->rc.owner, o); \
+ (void)git_atomic_swap((r)->rc.owner, o); \
}
-#define GIT_REFCOUNT_OWNER(r) git__load((r)->rc.owner)
+#define GIT_REFCOUNT_OWNER(r) git_atomic_load((r)->rc.owner)
-#define GIT_REFCOUNT_VAL(r) git_atomic_get((r)->rc.refcount)
+#define GIT_REFCOUNT_VAL(r) git_atomic32_get((r)->rc.refcount)
static signed char from_hex[] = {
*/
extern size_t git__unescape(char *str);
-/*
- * Iterate through an UTF-8 string, yielding one
- * codepoint at a time.
- *
- * @param str current position in the string
- * @param str_len size left in the string; -1 if the string is NULL-terminated
- * @param dst pointer where to store the current codepoint
- * @return length in bytes of the read codepoint; -1 if the codepoint was invalid
- */
-extern int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst);
-
-/*
- * Iterate through an UTF-8 string and stops after finding any invalid UTF-8
- * codepoints.
- *
- * @param str string to scan
- * @param str_len size of the string
- * @return length in bytes of the string that contains valid data
- */
-extern size_t git__utf8_valid_buf_length(const uint8_t *str, size_t str_len);
-
/*
* Safely zero-out memory, making sure that the compiler
* doesn't optimize away the operation.
return (double)time * scaling_factor / 1.0E9;
}
-#elif defined(AMIGA)
+#elif defined(__amigaos4__)
#include <proto/timer.h>
GIT_INLINE(double) git__timer(void)
{
- struct timespec tp;
+ struct timeval tv;
- if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+#ifdef CLOCK_MONOTONIC
+ struct timespec tp;
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
return (double) tp.tv_sec + (double) tp.tv_nsec / 1.0E9;
- } else {
- /* Fall back to using gettimeofday */
- struct timeval tv;
- struct timezone tz;
- gettimeofday(&tv, &tz);
- return (double)tv.tv_sec + (double)tv.tv_usec / 1.0E6;
- }
+#endif
+
+ /* Fall back to using gettimeofday */
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1.0E6;
}
#endif
extern int git__getenv(git_buf *out, const char *name);
+extern int git__online_cpus(void);
+
+GIT_INLINE(int) git__noop(void) { return 0; }
+
#include "alloc.h"
#endif
int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
{
- assert(v && src);
+ GIT_ASSERT_ARG(v);
+ GIT_ASSERT_ARG(src);
v->_alloc_size = 0;
v->contents = NULL;
void git_vector_free(git_vector *v)
{
- assert(v);
+ if (!v)
+ return;
git__free(v->contents);
v->contents = NULL;
{
size_t i;
- assert(v);
+ if (!v)
+ return;
for (i = 0; i < v->length; ++i) {
git__free(v->contents[i]);
int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp)
{
- assert(v);
+ GIT_ASSERT_ARG(v);
v->_alloc_size = 0;
v->_cmp = cmp;
int git_vector_insert(git_vector *v, void *element)
{
- assert(v);
+ GIT_ASSERT_ARG(v);
if (v->length >= v->_alloc_size &&
resize_vector(v, compute_new_size(v)) < 0)
int result;
size_t pos;
- assert(v && v->_cmp);
+ GIT_ASSERT_ARG(v);
+ GIT_ASSERT(v->_cmp);
if (!git_vector_is_sorted(v))
git_vector_sort(v);
void git_vector_sort(git_vector *v)
{
- assert(v);
-
if (git_vector_is_sorted(v) || !v->_cmp)
return;
git_vector_cmp key_lookup,
const void *key)
{
- assert(v && key && key_lookup);
+ GIT_ASSERT_ARG(v);
+ GIT_ASSERT_ARG(key);
+ GIT_ASSERT(key_lookup);
/* need comparison function to sort the vector */
if (!v->_cmp)
{
size_t i;
- assert(v && key && key_lookup);
+ GIT_ASSERT_ARG(v);
+ GIT_ASSERT_ARG(key);
+ GIT_ASSERT(key_lookup);
for (i = 0; i < v->length; ++i) {
if (key_lookup(key, v->contents[i]) == 0) {
{
size_t shift_count;
- assert(v);
+ GIT_ASSERT_ARG(v);
if (idx >= v->length)
return GIT_ENOTFOUND;
void git_vector_clear(git_vector *v)
{
- assert(v);
v->length = 0;
git_vector_set_sorted(v, 1);
}
{
git_vector t;
- assert(a && b);
-
if (a != b) {
memcpy(&t, a, sizeof(t));
memcpy(a, b, sizeof(t));
{
size_t new_length;
- assert(insert_len > 0 && idx <= v->length);
+ GIT_ASSERT_ARG(insert_len > 0);
+ GIT_ASSERT_ARG(idx <= v->length);
GIT_ERROR_CHECK_ALLOC_ADD(&new_length, v->length, insert_len);
{
size_t new_length = v->length - remove_len;
size_t end_idx = 0;
-
- assert(remove_len > 0);
+
+ GIT_ASSERT_ARG(remove_len > 0);
if (git__add_sizet_overflow(&end_idx, idx, remove_len))
- assert(0);
+ GIT_ASSERT(0);
- assert(end_idx <= v->length);
+ GIT_ASSERT(end_idx <= v->length);
if (end_idx < v->length)
memmove(&v->contents[idx], &v->contents[end_idx],
#define GIT_VECTOR_INIT {0}
-int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp);
+GIT_WARN_UNUSED_RESULT int git_vector_init(
+ git_vector *v, size_t initial_size, git_vector_cmp cmp);
void git_vector_free(git_vector *v);
void git_vector_free_deep(git_vector *v); /* free each entry and self */
void git_vector_clear(git_vector *v);
-int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp);
+GIT_WARN_UNUSED_RESULT int git_vector_dup(
+ git_vector *v, const git_vector *src, git_vector_cmp cmp);
void git_vector_swap(git_vector *a, git_vector *b);
int git_vector_size_hint(git_vector *v, size_t size_hint);
return git_buf_sets(dest, utf8_path);
}
-static wchar_t* win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
+static wchar_t *win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
{
wchar_t term, *base = path;
- assert(path && buf && buflen);
+ GIT_ASSERT_ARG_WITH_RETVAL(path, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(buf, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(buflen, NULL);
term = (*path == L'"') ? *path++ : L';';
HKEY hKey;
int error = GIT_ENOTFOUND;
- assert(buf);
+ GIT_ASSERT_ARG(buf);
if (!RegOpenKeyExW(hive, key, 0, KEY_READ, &hKey)) {
DWORD dwType, cbData;
{
int error = 0;
- assert(map != NULL);
+ GIT_ASSERT_ARG(map);
if (map->data) {
if (!UnmapViewOfFile(map->data)) {
#endif
-#define GIT_STDLIB_CALL __cdecl
+/*
+ * Offer GIT_LIBGIT2_CALL for our calling conventions (__cdecl, always).
+ * This is useful for providing callbacks to userspace code.
+ *
+ * Offer GIT_SYSTEM_CALL for the system calling conventions (__stdcall on
+ * Win32). Useful for providing callbacks to system libraries.
+ */
+#define GIT_LIBGIT2_CALL __cdecl
+#define GIT_SYSTEM_CALL NTAPI
#endif
* '\'s, but we we add a 'UNC' specifier to the path, plus
* a trailing directory separator, plus a NUL.
*/
- if (cwd_len > MAX_PATH - 4) {
+ if (cwd_len > GIT_WIN_PATH_MAX - 4) {
errno = ENAMETOOLONG;
return -1;
}
* working directory. (One character for the directory separator,
* one for the null.
*/
- else if (cwd_len > MAX_PATH - 2) {
+ else if (cwd_len > GIT_WIN_PATH_MAX - 2) {
errno = ENAMETOOLONG;
return -1;
}
/* See if this is an absolute path (beginning with a drive letter) */
if (git_path_is_absolute(src)) {
- if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
+ if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src) < 0)
goto on_error;
}
/* File-prefixed NT-style paths beginning with \\?\ */
else if (path__is_nt_namespace(src)) {
/* Skip the NT prefix, the destination already contains it */
- if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0)
+ if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src + PATH__NT_NAMESPACE_LEN) < 0)
goto on_error;
}
/* UNC paths */
dest += 4;
/* Skip the leading "\\" */
- if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0)
+ if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX - 2, src + 2) < 0)
goto on_error;
}
/* Absolute paths omitting the drive letter */
else if (path__startswith_slash(src)) {
- if (path__cwd(dest, MAX_PATH) < 0)
+ if (path__cwd(dest, GIT_WIN_PATH_MAX) < 0)
goto on_error;
if (!git_path_is_absolute(dest)) {
}
/* Skip the drive letter specification ("C:") */
- if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0)
+ if (git__utf8_to_16(dest + 2, GIT_WIN_PATH_MAX - 2, src) < 0)
goto on_error;
}
/* Relative paths */
else {
int cwd_len;
- if ((cwd_len = win32_path_cwd(dest, MAX_PATH)) < 0)
+ if ((cwd_len = win32_path_cwd(dest, GIT_WIN_PATH_MAX)) < 0)
goto on_error;
dest[cwd_len++] = L'\\';
- if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0)
+ if (git__utf8_to_16(dest + cwd_len, GIT_WIN_PATH_MAX - cwd_len, src) < 0)
goto on_error;
}
return git_win32_path_from_utf8(out, src);
}
- if ((len = git__utf8_to_16(dest, MAX_PATH, src)) < 0)
+ if ((len = git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src)) < 0)
return -1;
for (p = dest; p < (dest + len); p++) {
switch (reparse_buf->ReparseTag) {
case IO_REPARSE_TAG_SYMLINK:
- target = reparse_buf->SymbolicLinkReparseBuffer.PathBuffer +
- (reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
- target_len = reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
+ target = reparse_buf->ReparseBuffer.SymbolicLink.PathBuffer +
+ (reparse_buf->ReparseBuffer.SymbolicLink.SubstituteNameOffset / sizeof(WCHAR));
+ target_len = reparse_buf->ReparseBuffer.SymbolicLink.SubstituteNameLength / sizeof(WCHAR);
break;
case IO_REPARSE_TAG_MOUNT_POINT:
- target = reparse_buf->MountPointReparseBuffer.PathBuffer +
- (reparse_buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
- target_len = reparse_buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
+ target = reparse_buf->ReparseBuffer.MountPoint.PathBuffer +
+ (reparse_buf->ReparseBuffer.MountPoint.SubstituteNameOffset / sizeof(WCHAR));
+ target_len = reparse_buf->ReparseBuffer.MountPoint.SubstituteNameLength / sizeof(WCHAR);
break;
default:
errno = EINVAL;
prefix_len = CONST_STRLEN(unc_prefix);
}
- if (remainder) {
- /*
- * Sanity check that the new string isn't longer than the old one.
- * (This could only happen due to programmer error introducing a
- * prefix longer than the namespace it replaces.)
- */
- assert(len >= remainder_len + prefix_len);
-
+ /*
+ * Sanity check that the new string isn't longer than the old one.
+ * (This could only happen due to programmer error introducing a
+ * prefix longer than the namespace it replaces.)
+ */
+ if (remainder && len >= remainder_len + prefix_len) {
if (prefix)
memmove(str, prefix, prefix_len * sizeof(wchar_t));
#define INCLUDE_win32_path_w32_h__
#include "common.h"
-#include "vector.h"
/**
* Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given
extern int p_fstat(int fd, struct stat *buf);
extern int p_lstat(const char *file_name, struct stat *buf);
-extern int p_stat(const char* path, struct stat *buf);
+extern int p_stat(const char *path, struct stat *buf);
extern int p_utimes(const char *filename, const struct p_timeval times[2]);
extern int p_futimes(int fd, const struct p_timeval times[2]);
extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
-extern int p_inet_pton(int af, const char* src, void* dst);
+extern int p_inet_pton(int af, const char *src, void* dst);
extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);
extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4);
extern int p_mkstemp(char *tmp_path);
-extern int p_chdir(const char* path);
-extern int p_chmod(const char* path, mode_t mode);
-extern int p_rmdir(const char* path);
-extern int p_access(const char* path, mode_t mode);
+extern int p_chdir(const char *path);
+extern int p_chmod(const char *path, mode_t mode);
+extern int p_rmdir(const char *path);
+extern int p_access(const char *path, mode_t mode);
extern int p_ftruncate(int fd, off64_t size);
/* p_lstat is almost but not quite POSIX correct. Specifically, the use of
#include "utf-conv.h"
#include "repository.h"
#include "reparse.h"
-#include "global.h"
#include "buffer.h"
#include <errno.h>
#include <io.h>
mode_t mode = 0;
struct open_opts opts = {0};
+ #ifdef GIT_DEBUG_STRICT_OPEN
+ if (strstr(path, "//") != NULL) {
+ errno = EACCES;
+ return -1;
+ }
+ #endif
+
if (git_win32_path_from_utf8(wpath, path) < 0)
return -1;
if (!cwd)
return -1;
+ git_win32_path_remove_namespace(cwd, wcslen(cwd));
+
/* Convert the working directory back to UTF-8 */
if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
DWORD code = GetLastError();
return -1;
}
+ git_path_mkposix(buffer_out);
return 0;
}
return (int)git_win32_path_remove_namespace(dest, dwChars);
}
-static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
+static int follow_and_lstat_link(git_win32_path path, struct stat *buf)
{
git_win32_path target_w;
return 0;
}
-int p_stat(const char* path, struct stat* buf)
+int p_stat(const char *path, struct stat *buf)
{
git_win32_path path_w;
int len;
return 0;
}
-int p_chdir(const char* path)
+int p_chdir(const char *path)
{
git_win32_path buf;
return _wchdir(buf);
}
-int p_chmod(const char* path, mode_t mode)
+int p_chmod(const char *path, mode_t mode)
{
git_win32_path buf;
return _wchmod(buf, mode);
}
-int p_rmdir(const char* path)
+int p_rmdir(const char *path)
{
git_win32_path buf;
int error;
return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); /* -V536 */
}
-int p_access(const char* path, mode_t mode)
+int p_access(const char *path, mode_t mode)
{
git_win32_path buf;
errno = EINVAL;
return -1;
}
+
+ssize_t p_pread(int fd, void *data, size_t size, off64_t offset)
+{
+ HANDLE fh;
+ DWORD rsize = 0;
+ OVERLAPPED ov = {0};
+ LARGE_INTEGER pos = {0};
+ off64_t final_offset = 0;
+
+ /* Fail if the final offset would have overflowed to match POSIX semantics. */
+ if (!git__is_ssizet(size) || git__add_int64_overflow(&final_offset, offset, (int64_t)size)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Truncate large writes to the maximum allowable size: the caller
+ * needs to always call this in a loop anyways.
+ */
+ if (size > INT32_MAX) {
+ size = INT32_MAX;
+ }
+
+ pos.QuadPart = offset;
+ ov.Offset = pos.LowPart;
+ ov.OffsetHigh = pos.HighPart;
+ fh = (HANDLE)_get_osfhandle(fd);
+
+ if (ReadFile(fh, data, (DWORD)size, &rsize, &ov)) {
+ return (ssize_t)rsize;
+ }
+
+ set_errno();
+ return -1;
+}
+
+ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset)
+{
+ HANDLE fh;
+ DWORD wsize = 0;
+ OVERLAPPED ov = {0};
+ LARGE_INTEGER pos = {0};
+ off64_t final_offset = 0;
+
+ /* Fail if the final offset would have overflowed to match POSIX semantics. */
+ if (!git__is_ssizet(size) || git__add_int64_overflow(&final_offset, offset, (int64_t)size)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Truncate large writes to the maximum allowable size: the caller
+ * needs to always call this in a loop anyways.
+ */
+ if (size > INT32_MAX) {
+ size = INT32_MAX;
+ }
+
+ pos.QuadPart = offset;
+ ov.Offset = pos.LowPart;
+ ov.OffsetHigh = pos.HighPart;
+ fh = (HANDLE)_get_osfhandle(fd);
+
+ if (WriteFile(fh, data, (DWORD)size, &wsize, &ov)) {
+ return (ssize_t)wsize;
+ }
+
+ set_errno();
+ return -1;
+}
#include "common.h"
-#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
- } SymbolicLinkReparseBuffer;
+ } SymbolicLink;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
- } MountPointReparseBuffer;
+ } MountPoint;
struct {
UCHAR DataBuffer[1];
- } GenericReparseBuffer;
- };
+ } Generic;
+ } ReparseBuffer;
} GIT_REPARSE_DATA_BUFFER;
#define REPARSE_DATA_HEADER_SIZE 8
*/
#include "thread.h"
-
-#include "../global.h"
+#include "runtime.h"
#define CLEAN_THREAD_EXIT 0x6F012842
static win32_srwlock_fn win32_srwlock_acquire_exclusive;
static win32_srwlock_fn win32_srwlock_release_exclusive;
+static DWORD fls_index;
+
/* The thread procedure stub used to invoke the caller's procedure
* and capture the return value for later collection. Windows will
* only hold a DWORD, but we need to be able to store an entire
git_thread *thread = lpParameter;
/* Set the current thread for `git_thread_exit` */
- GIT_GLOBAL->current_thread = thread;
+ FlsSetValue(fls_index, thread);
thread->result = thread->proc(thread->param);
return CLEAN_THREAD_EXIT;
}
-int git_threads_init(void)
+static void git_threads_global_shutdown(void)
+{
+ FlsFree(fls_index);
+}
+
+int git_threads_global_init(void)
{
HMODULE hModule = GetModuleHandleW(L"kernel32");
GetProcAddress(hModule, "ReleaseSRWLockExclusive");
}
- return 0;
+ if ((fls_index = FlsAlloc(NULL)) == FLS_OUT_OF_INDEXES)
+ return -1;
+
+ return git_runtime_shutdown_register(git_threads_global_shutdown);
}
int git_thread_create(
/* Check for the thread having exited uncleanly. If exit was unclean,
* then we don't have a return value to give back to the caller. */
- if (exit != CLEAN_THREAD_EXIT) {
- assert(false);
- thread->result = NULL;
- }
+ GIT_ASSERT(exit == CLEAN_THREAD_EXIT);
if (value_ptr)
*value_ptr = thread->result;
void git_thread_exit(void *value)
{
- assert(GIT_GLOBAL->current_thread);
- GIT_GLOBAL->current_thread->result = value;
+ git_thread *thread = FlsGetValue(fls_index);
+
+ if (thread)
+ thread->result = value;
+
ExitThread(CLEAN_THREAD_EXIT);
}
{
/* This is an auto-reset event. */
*cond = CreateEventW(NULL, FALSE, FALSE, NULL);
- assert(*cond);
+ GIT_ASSERT(*cond);
/* If we can't create the event, claim that the reason was out-of-memory.
* The actual reason can be fetched with GetLastError(). */
return EINVAL;
closed = CloseHandle(*cond);
- assert(closed);
+ GIT_ASSERT(closed);
GIT_UNUSED(closed);
*cond = NULL;
return error;
wait_result = WaitForSingleObject(*cond, INFINITE);
- assert(WAIT_OBJECT_0 == wait_result);
+ GIT_ASSERT(WAIT_OBJECT_0 == wait_result);
GIT_UNUSED(wait_result);
return git_mutex_lock(mutex);
return EINVAL;
signaled = SetEvent(*cond);
- assert(signaled);
+ GIT_ASSERT(signaled);
GIT_UNUSED(signaled);
return 0;
} native;
} git_rwlock;
-int git_threads_init(void);
+int git_threads_global_init(void);
int git_thread_create(git_thread *GIT_RESTRICT,
void *(*) (void *),
return -1;
}
- assert(string_w);
+ GIT_ASSERT(string_w);
/* Measure the string necessary for conversion */
if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string_w, (int)len_w, NULL, 0, NULL, NULL)) == 0)
return 0;
- assert(utf8_len > 0);
+ GIT_ASSERT(utf8_len > 0);
GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, (size_t)utf8_len);
GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
CP_UTF8, WC_ERR_INVALID_CHARS, string_w, (int)len_w, &buf->ptr[buf->size], utf8_len, NULL, NULL)) == 0)
return handle_wc_error();
- assert(utf8_write_len == utf8_len);
+ GIT_ASSERT(utf8_write_len == utf8_len);
buf->size += utf8_write_len;
buf->ptr[buf->size] = '\0';
#ifndef INCLUDE_win32_w32_common_h__
#define INCLUDE_win32_w32_common_h__
+#include <git2/common.h>
+
+/*
+ * 4096 is the max allowed Git path. `MAX_PATH` (260) is the typical max allowed
+ * Windows path length, however win32 Unicode APIs generally allow up to 32,767
+ * if prefixed with "\\?\" (i.e. converted to an NT-style name).
+ */
+#define GIT_WIN_PATH_MAX GIT_PATH_MAX
+
/*
- * Provides a large enough buffer to support Windows paths: MAX_PATH is
- * 260, corresponding to a maximum path length of 259 characters plus a
- * NULL terminator. Prefixing with "\\?\" adds 4 characters, but if the
- * original was a UNC path, then we turn "\\server\share" into
+ * Provides a large enough buffer to support Windows Git paths:
+ * GIT_WIN_PATH_MAX is 4096, corresponding to a maximum path length of 4095
+ * characters plus a NULL terminator. Prefixing with "\\?\" adds 4 characters,
+ * but if the original was a UNC path, then we turn "\\server\share" into
* "\\?\UNC\server\share". So we replace the first two characters with
- * 8 characters, a net gain of 6, so the maximum length is MAX_PATH+6.
+ * 8 characters, a net gain of 6, so the maximum length is GIT_WIN_PATH_MAX+6.
*/
-#define GIT_WIN_PATH_UTF16 MAX_PATH+6
+#define GIT_WIN_PATH_UTF16 GIT_WIN_PATH_MAX+6
-/* Maximum size of a UTF-8 Win32 path. We remove the "\\?\" or "\\?\UNC\"
- * prefixes for presentation, bringing us back to 259 (non-NULL)
+/* Maximum size of a UTF-8 Win32 Git path. We remove the "\\?\" or "\\?\UNC\"
+ * prefixes for presentation, bringing us back to 4095 (non-NULL)
* characters. UTF-8 does have 4-byte sequences, but they are encoded in
* UTF-16 using surrogate pairs, which takes up the space of two characters.
* Two characters in the range U+0800 -> U+FFFF take up more space in UTF-8
* (6 bytes) than one surrogate pair (4 bytes).
*/
-#define GIT_WIN_PATH_UTF8 (259 * 3 + 1)
+#define GIT_WIN_PATH_UTF8 ((GIT_WIN_PATH_MAX - 1) * 3 + 1)
/*
* The length of a Windows "shortname", for 8.3 compatibility.
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "w32_crtdbg_stacktrace.h"
-
-#if defined(GIT_MSVC_CRTDBG)
-#include "w32_stack.h"
-
-#define CRTDBG_STACKTRACE__UID_LEN (15)
-
-/**
- * The stacktrace of an allocation can be distilled
- * to a unique id based upon the stackframe pointers
- * and ignoring any size arguments. We will use these
- * UIDs as the (char const*) __FILE__ argument we
- * give to the CRT malloc routines.
- */
-typedef struct {
- char uid[CRTDBG_STACKTRACE__UID_LEN + 1];
-} git_win32__crtdbg_stacktrace__uid;
-
-/**
- * All mallocs with the same stacktrace will be de-duped
- * and aggregated into this row.
- */
-typedef struct {
- git_win32__crtdbg_stacktrace__uid uid; /* must be first */
- git_win32__stack__raw_data raw_data;
- unsigned int count_allocs; /* times this alloc signature seen since init */
- unsigned int count_allocs_at_last_checkpoint; /* times since last mark */
- unsigned int transient_count_leaks; /* sum of leaks */
-} git_win32__crtdbg_stacktrace__row;
-
-static CRITICAL_SECTION g_crtdbg_stacktrace_cs;
-
-/**
- * CRTDBG memory leak tracking takes a "char const * const file_name"
- * and stores the pointer in the heap data (instead of allocing a copy
- * for itself). Normally, this is not a problem, since we usually pass
- * in __FILE__. But I'm going to lie to it and pass in the address of
- * the UID in place of the file_name. Also, I do not want to alloc the
- * stacktrace data (because we are called from inside our alloc routines).
- * Therefore, I'm creating a very large static pool array to store row
- * data. This also eliminates the temptation to realloc it (and move the
- * UID pointers).
- *
- * And to efficiently look for duplicates we need an index on the rows
- * so we can bsearch it. Again, without mallocing.
- *
- * If we observe more than MY_ROW_LIMIT unique malloc signatures, we
- * fall through and use the traditional __FILE__ processing and don't
- * try to de-dup them. If your testing hits this limit, just increase
- * it and try again.
- */
-
-#define MY_ROW_LIMIT (1024 * 1024)
-static git_win32__crtdbg_stacktrace__row g_cs_rows[MY_ROW_LIMIT];
-static git_win32__crtdbg_stacktrace__row *g_cs_index[MY_ROW_LIMIT];
-
-static unsigned int g_cs_end = MY_ROW_LIMIT;
-static unsigned int g_cs_ins = 0; /* insertion point == unique allocs seen */
-static unsigned int g_count_total_allocs = 0; /* number of allocs seen */
-static unsigned int g_transient_count_total_leaks = 0; /* number of total leaks */
-static unsigned int g_transient_count_dedup_leaks = 0; /* number of unique leaks */
-static bool g_limit_reached = false; /* had allocs after we filled row table */
-
-static unsigned int g_checkpoint_id = 0; /* to better label leak checkpoints */
-static bool g_transient_leaks_since_mark = false; /* payload for hook */
-
-/**
- * Compare function for bsearch on g_cs_index table.
- */
-static int row_cmp(const void *v1, const void *v2)
-{
- git_win32__stack__raw_data *d1 = (git_win32__stack__raw_data*)v1;
- git_win32__crtdbg_stacktrace__row *r2 = (git_win32__crtdbg_stacktrace__row *)v2;
-
- return (git_win32__stack_compare(d1, &r2->raw_data));
-}
-
-/**
- * Unique insert the new data into the row and index tables.
- * We have to sort by the stackframe data itself, not the uid.
- */
-static git_win32__crtdbg_stacktrace__row * insert_unique(
- const git_win32__stack__raw_data *pdata)
-{
- size_t pos;
- if (git__bsearch(g_cs_index, g_cs_ins, pdata, row_cmp, &pos) < 0) {
- /* Append new unique item to row table. */
- memcpy(&g_cs_rows[g_cs_ins].raw_data, pdata, sizeof(*pdata));
- sprintf(g_cs_rows[g_cs_ins].uid.uid, "##%08lx", g_cs_ins);
-
- /* Insert pointer to it into the proper place in the index table. */
- if (pos < g_cs_ins)
- memmove(&g_cs_index[pos+1], &g_cs_index[pos], (g_cs_ins - pos)*sizeof(g_cs_index[0]));
- g_cs_index[pos] = &g_cs_rows[g_cs_ins];
-
- g_cs_ins++;
- }
-
- g_cs_index[pos]->count_allocs++;
-
- return g_cs_index[pos];
-}
-
-/**
- * Hook function to receive leak data from the CRT. (This includes
- * both "<file_name>:(<line_number>)" data, but also each of the
- * various headers and fields.
- *
- * Scan this for the special "##<pos>" UID forms that we substituted
- * for the "<file_name>". Map <pos> back to the row data and
- * increment its leak count.
- *
- * See https://msdn.microsoft.com/en-us/library/74kabxyx.aspx
- *
- * We suppress the actual crtdbg output.
- */
-static int __cdecl report_hook(int nRptType, char *szMsg, int *retVal)
-{
- static int hook_result = TRUE; /* FALSE to get stock dump; TRUE to suppress. */
- unsigned int pos;
-
- *retVal = 0; /* do not invoke debugger */
-
- if ((szMsg[0] != '#') || (szMsg[1] != '#'))
- return hook_result;
-
- if (sscanf(&szMsg[2], "%08lx", &pos) < 1)
- return hook_result;
- if (pos >= g_cs_ins)
- return hook_result;
-
- if (g_transient_leaks_since_mark) {
- if (g_cs_rows[pos].count_allocs == g_cs_rows[pos].count_allocs_at_last_checkpoint)
- return hook_result;
- }
-
- g_cs_rows[pos].transient_count_leaks++;
-
- if (g_cs_rows[pos].transient_count_leaks == 1)
- g_transient_count_dedup_leaks++;
-
- g_transient_count_total_leaks++;
-
- return hook_result;
-}
-
-/**
- * Write leak data to all of the various places we need.
- * We force the caller to sprintf() the message first
- * because we want to avoid fprintf() because it allocs.
- */
-static void my_output(const char *buf)
-{
- fwrite(buf, strlen(buf), 1, stderr);
- OutputDebugString(buf);
-}
-
-/**
- * For each row with leaks, dump a stacktrace for it.
- */
-static void dump_summary(const char *label)
-{
- unsigned int k;
- char buf[10 * 1024];
-
- if (g_transient_count_total_leaks == 0)
- return;
-
- fflush(stdout);
- fflush(stderr);
- my_output("\n");
-
- if (g_limit_reached) {
- sprintf(buf,
- "LEAK SUMMARY: de-dup row table[%d] filled. Increase MY_ROW_LIMIT.\n",
- MY_ROW_LIMIT);
- my_output(buf);
- }
-
- if (!label)
- label = "";
-
- if (g_transient_leaks_since_mark) {
- sprintf(buf, "LEAK CHECKPOINT %d: leaks %d unique %d: %s\n",
- g_checkpoint_id, g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
- my_output(buf);
- } else {
- sprintf(buf, "LEAK SUMMARY: TOTAL leaks %d de-duped %d: %s\n",
- g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
- my_output(buf);
- }
- my_output("\n");
-
- for (k = 0; k < g_cs_ins; k++) {
- if (g_cs_rows[k].transient_count_leaks > 0) {
- sprintf(buf, "LEAK: %s leaked %d of %d times:\n",
- g_cs_rows[k].uid.uid,
- g_cs_rows[k].transient_count_leaks,
- g_cs_rows[k].count_allocs);
- my_output(buf);
-
- if (git_win32__stack_format(
- buf, sizeof(buf), &g_cs_rows[k].raw_data,
- NULL, NULL) >= 0) {
- my_output(buf);
- }
-
- my_output("\n");
- }
- }
-
- fflush(stderr);
-}
-
-void git_win32__crtdbg_stacktrace_init(void)
-{
- InitializeCriticalSection(&g_crtdbg_stacktrace_cs);
-
- EnterCriticalSection(&g_crtdbg_stacktrace_cs);
-
- _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
-
- _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
- _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
- _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
-
- _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
- _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
- _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
-
- LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
-}
-
-int git_win32__crtdbg_stacktrace__dump(
- git_win32__crtdbg_stacktrace_options opt,
- const char *label)
-{
- _CRT_REPORT_HOOK old;
- unsigned int k;
- int r = 0;
-
-#define IS_BIT_SET(o,b) (((o) & (b)) != 0)
-
- bool b_set_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK);
- bool b_leaks_since_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK);
- bool b_leaks_total = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL);
- bool b_quiet = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET);
-
- if (b_leaks_since_mark && b_leaks_total) {
- git_error_set(GIT_ERROR_INVALID, "cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
- return GIT_ERROR;
- }
- if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) {
- git_error_set(GIT_ERROR_INVALID, "nothing to do.");
- return GIT_ERROR;
- }
-
- EnterCriticalSection(&g_crtdbg_stacktrace_cs);
-
- if (b_leaks_since_mark || b_leaks_total) {
- /* All variables with "transient" in the name are per-dump counters
- * and reset before each dump. This lets us handle checkpoints.
- */
- g_transient_count_total_leaks = 0;
- g_transient_count_dedup_leaks = 0;
- for (k = 0; k < g_cs_ins; k++) {
- g_cs_rows[k].transient_count_leaks = 0;
- }
- }
-
- g_transient_leaks_since_mark = b_leaks_since_mark;
-
- old = _CrtSetReportHook(report_hook);
- _CrtDumpMemoryLeaks();
- _CrtSetReportHook(old);
-
- if (b_leaks_since_mark || b_leaks_total) {
- r = g_transient_count_dedup_leaks;
-
- if (!b_quiet)
- dump_summary(label);
- }
-
- if (b_set_mark) {
- for (k = 0; k < g_cs_ins; k++) {
- g_cs_rows[k].count_allocs_at_last_checkpoint = g_cs_rows[k].count_allocs;
- }
-
- g_checkpoint_id++;
- }
-
- LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
-
- return r;
-}
-
-void git_win32__crtdbg_stacktrace_cleanup(void)
-{
- /* At shutdown/cleanup, dump cummulative leak info
- * with everything since startup. This might generate
- * extra noise if the caller has been doing checkpoint
- * dumps, but it might also eliminate some false
- * positives for resources previously reported during
- * checkpoints.
- */
- git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL,
- "CLEANUP");
-
- DeleteCriticalSection(&g_crtdbg_stacktrace_cs);
-}
-
-const char *git_win32__crtdbg_stacktrace(int skip, const char *file)
-{
- git_win32__stack__raw_data new_data;
- git_win32__crtdbg_stacktrace__row *row;
- const char * result = file;
-
- if (git_win32__stack_capture(&new_data, skip+1) < 0)
- return result;
-
- EnterCriticalSection(&g_crtdbg_stacktrace_cs);
-
- if (g_cs_ins < g_cs_end) {
- row = insert_unique(&new_data);
- result = row->uid.uid;
- } else {
- g_limit_reached = true;
- }
-
- g_count_total_allocs++;
-
- LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
-
- return result;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_win32_w32_crtdbg_stacktrace_h__
-#define INCLUDE_win32_w32_crtdbg_stacktrace_h__
-
-#include "common.h"
-
-#if defined(GIT_MSVC_CRTDBG)
-
-#include <stdlib.h>
-#include <crtdbg.h>
-
-#include "git2/errors.h"
-#include "strnlen.h"
-
-/* MSVC CRTDBG memory leak reporting.
- *
- * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC
- * documentation because all allocs/frees in libgit2 already go through
- * the "git__" routines defined in this file. Simply using the normal
- * reporting mechanism causes all leaks to be attributed to a routine
- * here in util.h (ie, the actual call to calloc()) rather than the
- * caller of git__calloc().
- *
- * Therefore, we declare a set of "git__crtdbg__" routines to replace
- * the corresponding "git__" routines and re-define the "git__" symbols
- * as macros. This allows us to get and report the file:line info of
- * the real caller.
- *
- * We DO NOT replace the "git__free" routine because it needs to remain
- * a function pointer because it is used as a function argument when
- * setting up various structure "destructors".
- *
- * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes
- * "free" to be remapped to "_free_dbg" and this causes problems for
- * structures which define a field named "free".
- *
- * Finally, CRTDBG must be explicitly enabled and configured at program
- * startup. See tests/main.c for an example.
- */
-
-/**
- * Initialize our memory leak tracking and de-dup data structures.
- * This should ONLY be called by git_libgit2_init().
- */
-void git_win32__crtdbg_stacktrace_init(void);
-
-/**
- * Shutdown our memory leak tracking and dump summary data.
- * This should ONLY be called by git_libgit2_shutdown().
- *
- * We explicitly call _CrtDumpMemoryLeaks() during here so
- * that we can compute summary data for the leaks. We print
- * the stacktrace of each unique leak.
- *
- * This cleanup does not happen if the app calls exit()
- * without calling the libgit2 shutdown code.
- *
- * This info we print here is independent of any automatic
- * reporting during exit() caused by _CRTDBG_LEAK_CHECK_DF.
- * Set it in your app if you also want traditional reporting.
- */
-void git_win32__crtdbg_stacktrace_cleanup(void);
-
-/**
- * Checkpoint options.
- */
-typedef enum git_win32__crtdbg_stacktrace_options {
- /**
- * Set checkpoint marker.
- */
- GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK = (1 << 0),
-
- /**
- * Dump leaks since last checkpoint marker.
- * May not be combined with __LEAKS_TOTAL.
- *
- * Note that this may generate false positives for global TLS
- * error state and other global caches that aren't cleaned up
- * until the thread/process terminates. So when using this
- * around a region of interest, also check the final (at exit)
- * dump before digging into leaks reported here.
- */
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK = (1 << 1),
-
- /**
- * Dump leaks since init. May not be combined
- * with __LEAKS_SINCE_MARK.
- */
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL = (1 << 2),
-
- /**
- * Suppress printing during dumps.
- * Just return leak count.
- */
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET = (1 << 3),
-
-} git_win32__crtdbg_stacktrace_options;
-
-/**
- * Checkpoint memory state and/or dump unique stack traces of
- * current memory leaks.
- *
- * @return number of unique leaks (relative to requested starting
- * point) or error.
- */
-GIT_EXTERN(int) git_win32__crtdbg_stacktrace__dump(
- git_win32__crtdbg_stacktrace_options opt,
- const char *label);
-
-/**
- * Construct stacktrace and append it to the global buffer.
- * Return pointer to start of this string. On any error or
- * lack of buffer space, just return the given file buffer
- * so it will behave as usual.
- *
- * This should ONLY be called by our internal memory allocations
- * routines.
- */
-const char *git_win32__crtdbg_stacktrace(int skip, const char *file);
-
-#endif
-#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "w32_leakcheck.h"
+
+#if defined(GIT_WIN32_LEAKCHECK)
+
+#include "Windows.h"
+#include "Dbghelp.h"
+#include "win32/posix.h"
+#include "hash.h"
+#include "runtime.h"
+
+/* Stack frames (for stack tracing, below) */
+
+static bool g_win32_stack_initialized = false;
+static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE;
+static git_win32_leakcheck_stack_aux_cb_alloc g_aux_cb_alloc = NULL;
+static git_win32_leakcheck_stack_aux_cb_lookup g_aux_cb_lookup = NULL;
+
+int git_win32_leakcheck_stack_set_aux_cb(
+ git_win32_leakcheck_stack_aux_cb_alloc cb_alloc,
+ git_win32_leakcheck_stack_aux_cb_lookup cb_lookup)
+{
+ g_aux_cb_alloc = cb_alloc;
+ g_aux_cb_lookup = cb_lookup;
+
+ return 0;
+}
+
+/**
+ * Load symbol table data. This should be done in the primary
+ * thread at startup (under a lock if there are other threads
+ * active).
+ */
+void git_win32_leakcheck_stack_init(void)
+{
+ if (!g_win32_stack_initialized) {
+ g_win32_stack_process = GetCurrentProcess();
+ SymSetOptions(SYMOPT_LOAD_LINES);
+ SymInitialize(g_win32_stack_process, NULL, TRUE);
+ g_win32_stack_initialized = true;
+ }
+}
+
+/**
+ * Cleanup symbol table data. This should be done in the
+ * primary thead at shutdown (under a lock if there are other
+ * threads active).
+ */
+void git_win32_leakcheck_stack_cleanup(void)
+{
+ if (g_win32_stack_initialized) {
+ SymCleanup(g_win32_stack_process);
+ g_win32_stack_process = INVALID_HANDLE_VALUE;
+ g_win32_stack_initialized = false;
+ }
+}
+
+int git_win32_leakcheck_stack_capture(git_win32_leakcheck_stack_raw_data *pdata, int skip)
+{
+ if (!g_win32_stack_initialized) {
+ git_error_set(GIT_ERROR_INVALID, "git_win32_stack not initialized.");
+ return GIT_ERROR;
+ }
+
+ memset(pdata, 0, sizeof(*pdata));
+ pdata->nr_frames = RtlCaptureStackBackTrace(
+ skip+1, GIT_WIN32_LEAKCHECK_STACK_MAX_FRAMES, pdata->frames, NULL);
+
+ /* If an "aux" data provider was registered, ask it to capture
+ * whatever data it needs and give us an "aux_id" to it so that
+ * we can refer to it later when reporting.
+ */
+ if (g_aux_cb_alloc)
+ (g_aux_cb_alloc)(&pdata->aux_id);
+
+ return 0;
+}
+
+int git_win32_leakcheck_stack_compare(
+ git_win32_leakcheck_stack_raw_data *d1,
+ git_win32_leakcheck_stack_raw_data *d2)
+{
+ return memcmp(d1, d2, sizeof(*d1));
+}
+
+int git_win32_leakcheck_stack_format(
+ char *pbuf, size_t buf_len,
+ const git_win32_leakcheck_stack_raw_data *pdata,
+ const char *prefix, const char *suffix)
+{
+#define MY_MAX_FILENAME 255
+
+ /* SYMBOL_INFO has char FileName[1] at the end. The docs say to
+ * to malloc it with extra space for your desired max filename.
+ */
+ struct {
+ SYMBOL_INFO symbol;
+ char extra[MY_MAX_FILENAME + 1];
+ } s;
+
+ IMAGEHLP_LINE64 line;
+ size_t buf_used = 0;
+ unsigned int k;
+ char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */
+ size_t detail_len;
+
+ if (!g_win32_stack_initialized) {
+ git_error_set(GIT_ERROR_INVALID, "git_win32_stack not initialized.");
+ return GIT_ERROR;
+ }
+
+ if (!prefix)
+ prefix = "\t";
+ if (!suffix)
+ suffix = "\n";
+
+ memset(pbuf, 0, buf_len);
+
+ memset(&s, 0, sizeof(s));
+ s.symbol.MaxNameLen = MY_MAX_FILENAME;
+ s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
+
+ memset(&line, 0, sizeof(line));
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+
+ for (k=0; k < pdata->nr_frames; k++) {
+ DWORD64 frame_k = (DWORD64)pdata->frames[k];
+ DWORD dwUnused;
+
+ if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) &&
+ SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) {
+ const char *pslash;
+ const char *pfile;
+
+ pslash = strrchr(line.FileName, '\\');
+ pfile = ((pslash) ? (pslash+1) : line.FileName);
+ p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s",
+ prefix, pfile, line.LineNumber, s.symbol.Name, suffix);
+ } else {
+ /* This happens when we cross into another module.
+ * For example, in CLAR tests, this is typically
+ * the CRT startup code. Just print an unknown
+ * frame and continue.
+ */
+ p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix);
+ }
+ detail_len = strlen(detail);
+
+ if (buf_len < (buf_used + detail_len + 1)) {
+ /* we don't have room for this frame in the buffer, so just stop. */
+ break;
+ }
+
+ memcpy(&pbuf[buf_used], detail, detail_len);
+ buf_used += detail_len;
+ }
+
+ /* "aux_id" 0 is reserved to mean no aux data. This is needed to handle
+ * allocs that occur before the aux callbacks were registered.
+ */
+ if (pdata->aux_id > 0) {
+ p_snprintf(detail, sizeof(detail), "%saux_id: %d%s",
+ prefix, pdata->aux_id, suffix);
+ detail_len = strlen(detail);
+ if ((buf_used + detail_len + 1) < buf_len) {
+ memcpy(&pbuf[buf_used], detail, detail_len);
+ buf_used += detail_len;
+ }
+
+ /* If an "aux" data provider is still registered, ask it to append its detailed
+ * data to the end of ours using the "aux_id" it gave us when this de-duped
+ * item was created.
+ */
+ if (g_aux_cb_lookup)
+ (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1));
+ }
+
+ return GIT_OK;
+}
+
+int git_win32_leakcheck_stack(
+ char * pbuf, size_t buf_len,
+ int skip,
+ const char *prefix, const char *suffix)
+{
+ git_win32_leakcheck_stack_raw_data data;
+ int error;
+
+ if ((error = git_win32_leakcheck_stack_capture(&data, skip)) < 0)
+ return error;
+ if ((error = git_win32_leakcheck_stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0)
+ return error;
+ return 0;
+}
+
+/* Strack tracing */
+
+#define STACKTRACE_UID_LEN (15)
+
+/**
+ * The stacktrace of an allocation can be distilled
+ * to a unique id based upon the stackframe pointers
+ * and ignoring any size arguments. We will use these
+ * UIDs as the (char const*) __FILE__ argument we
+ * give to the CRT malloc routines.
+ */
+typedef struct {
+ char uid[STACKTRACE_UID_LEN + 1];
+} git_win32_leakcheck_stacktrace_uid;
+
+/**
+ * All mallocs with the same stacktrace will be de-duped
+ * and aggregated into this row.
+ */
+typedef struct {
+ git_win32_leakcheck_stacktrace_uid uid; /* must be first */
+ git_win32_leakcheck_stack_raw_data raw_data;
+ unsigned int count_allocs; /* times this alloc signature seen since init */
+ unsigned int count_allocs_at_last_checkpoint; /* times since last mark */
+ unsigned int transient_count_leaks; /* sum of leaks */
+} git_win32_leakcheck_stacktrace_row;
+
+static CRITICAL_SECTION g_crtdbg_stacktrace_cs;
+
+/**
+ * CRTDBG memory leak tracking takes a "char const * const file_name"
+ * and stores the pointer in the heap data (instead of allocing a copy
+ * for itself). Normally, this is not a problem, since we usually pass
+ * in __FILE__. But I'm going to lie to it and pass in the address of
+ * the UID in place of the file_name. Also, I do not want to alloc the
+ * stacktrace data (because we are called from inside our alloc routines).
+ * Therefore, I'm creating a very large static pool array to store row
+ * data. This also eliminates the temptation to realloc it (and move the
+ * UID pointers).
+ *
+ * And to efficiently look for duplicates we need an index on the rows
+ * so we can bsearch it. Again, without mallocing.
+ *
+ * If we observe more than MY_ROW_LIMIT unique malloc signatures, we
+ * fall through and use the traditional __FILE__ processing and don't
+ * try to de-dup them. If your testing hits this limit, just increase
+ * it and try again.
+ */
+
+#define MY_ROW_LIMIT (2 * 1024 * 1024)
+static git_win32_leakcheck_stacktrace_row g_cs_rows[MY_ROW_LIMIT];
+static git_win32_leakcheck_stacktrace_row *g_cs_index[MY_ROW_LIMIT];
+
+static unsigned int g_cs_end = MY_ROW_LIMIT;
+static unsigned int g_cs_ins = 0; /* insertion point == unique allocs seen */
+static unsigned int g_count_total_allocs = 0; /* number of allocs seen */
+static unsigned int g_transient_count_total_leaks = 0; /* number of total leaks */
+static unsigned int g_transient_count_dedup_leaks = 0; /* number of unique leaks */
+static bool g_limit_reached = false; /* had allocs after we filled row table */
+
+static unsigned int g_checkpoint_id = 0; /* to better label leak checkpoints */
+static bool g_transient_leaks_since_mark = false; /* payload for hook */
+
+/**
+ * Compare function for bsearch on g_cs_index table.
+ */
+static int row_cmp(const void *v1, const void *v2)
+{
+ git_win32_leakcheck_stack_raw_data *d1 = (git_win32_leakcheck_stack_raw_data*)v1;
+ git_win32_leakcheck_stacktrace_row *r2 = (git_win32_leakcheck_stacktrace_row *)v2;
+
+ return (git_win32_leakcheck_stack_compare(d1, &r2->raw_data));
+}
+
+/**
+ * Unique insert the new data into the row and index tables.
+ * We have to sort by the stackframe data itself, not the uid.
+ */
+static git_win32_leakcheck_stacktrace_row * insert_unique(
+ const git_win32_leakcheck_stack_raw_data *pdata)
+{
+ size_t pos;
+ if (git__bsearch(g_cs_index, g_cs_ins, pdata, row_cmp, &pos) < 0) {
+ /* Append new unique item to row table. */
+ memcpy(&g_cs_rows[g_cs_ins].raw_data, pdata, sizeof(*pdata));
+ sprintf(g_cs_rows[g_cs_ins].uid.uid, "##%08lx", g_cs_ins);
+
+ /* Insert pointer to it into the proper place in the index table. */
+ if (pos < g_cs_ins)
+ memmove(&g_cs_index[pos+1], &g_cs_index[pos], (g_cs_ins - pos)*sizeof(g_cs_index[0]));
+ g_cs_index[pos] = &g_cs_rows[g_cs_ins];
+
+ g_cs_ins++;
+ }
+
+ g_cs_index[pos]->count_allocs++;
+
+ return g_cs_index[pos];
+}
+
+/**
+ * Hook function to receive leak data from the CRT. (This includes
+ * both "<file_name>:(<line_number>)" data, but also each of the
+ * various headers and fields.
+ *
+ * Scan this for the special "##<pos>" UID forms that we substituted
+ * for the "<file_name>". Map <pos> back to the row data and
+ * increment its leak count.
+ *
+ * See https://msdn.microsoft.com/en-us/library/74kabxyx.aspx
+ *
+ * We suppress the actual crtdbg output.
+ */
+static int __cdecl report_hook(int nRptType, char *szMsg, int *retVal)
+{
+ static int hook_result = TRUE; /* FALSE to get stock dump; TRUE to suppress. */
+ unsigned int pos;
+
+ *retVal = 0; /* do not invoke debugger */
+
+ if ((szMsg[0] != '#') || (szMsg[1] != '#'))
+ return hook_result;
+
+ if (sscanf(&szMsg[2], "%08lx", &pos) < 1)
+ return hook_result;
+ if (pos >= g_cs_ins)
+ return hook_result;
+
+ if (g_transient_leaks_since_mark) {
+ if (g_cs_rows[pos].count_allocs == g_cs_rows[pos].count_allocs_at_last_checkpoint)
+ return hook_result;
+ }
+
+ g_cs_rows[pos].transient_count_leaks++;
+
+ if (g_cs_rows[pos].transient_count_leaks == 1)
+ g_transient_count_dedup_leaks++;
+
+ g_transient_count_total_leaks++;
+
+ return hook_result;
+}
+
+/**
+ * Write leak data to all of the various places we need.
+ * We force the caller to sprintf() the message first
+ * because we want to avoid fprintf() because it allocs.
+ */
+static void my_output(const char *buf)
+{
+ fwrite(buf, strlen(buf), 1, stderr);
+ OutputDebugString(buf);
+}
+
+/**
+ * For each row with leaks, dump a stacktrace for it.
+ */
+static void dump_summary(const char *label)
+{
+ unsigned int k;
+ char buf[10 * 1024];
+
+ if (g_transient_count_total_leaks == 0)
+ return;
+
+ fflush(stdout);
+ fflush(stderr);
+ my_output("\n");
+
+ if (g_limit_reached) {
+ sprintf(buf,
+ "LEAK SUMMARY: de-dup row table[%d] filled. Increase MY_ROW_LIMIT.\n",
+ MY_ROW_LIMIT);
+ my_output(buf);
+ }
+
+ if (!label)
+ label = "";
+
+ if (g_transient_leaks_since_mark) {
+ sprintf(buf, "LEAK CHECKPOINT %d: leaks %d unique %d: %s\n",
+ g_checkpoint_id, g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
+ my_output(buf);
+ } else {
+ sprintf(buf, "LEAK SUMMARY: TOTAL leaks %d de-duped %d: %s\n",
+ g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
+ my_output(buf);
+ }
+ my_output("\n");
+
+ for (k = 0; k < g_cs_ins; k++) {
+ if (g_cs_rows[k].transient_count_leaks > 0) {
+ sprintf(buf, "LEAK: %s leaked %d of %d times:\n",
+ g_cs_rows[k].uid.uid,
+ g_cs_rows[k].transient_count_leaks,
+ g_cs_rows[k].count_allocs);
+ my_output(buf);
+
+ if (git_win32_leakcheck_stack_format(
+ buf, sizeof(buf), &g_cs_rows[k].raw_data,
+ NULL, NULL) >= 0) {
+ my_output(buf);
+ }
+
+ my_output("\n");
+ }
+ }
+
+ fflush(stderr);
+}
+
+/**
+ * Initialize our memory leak tracking and de-dup data structures.
+ * This should ONLY be called by git_libgit2_init().
+ */
+void git_win32_leakcheck_stacktrace_init(void)
+{
+ InitializeCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+}
+
+int git_win32_leakcheck_stacktrace_dump(
+ git_win32_leakcheck_stacktrace_options opt,
+ const char *label)
+{
+ _CRT_REPORT_HOOK old;
+ unsigned int k;
+ int r = 0;
+
+#define IS_BIT_SET(o,b) (((o) & (b)) != 0)
+
+ bool b_set_mark = IS_BIT_SET(opt, GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK);
+ bool b_leaks_since_mark = IS_BIT_SET(opt, GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK);
+ bool b_leaks_total = IS_BIT_SET(opt, GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL);
+ bool b_quiet = IS_BIT_SET(opt, GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET);
+
+ if (b_leaks_since_mark && b_leaks_total) {
+ git_error_set(GIT_ERROR_INVALID, "cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
+ return GIT_ERROR;
+ }
+ if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) {
+ git_error_set(GIT_ERROR_INVALID, "nothing to do.");
+ return GIT_ERROR;
+ }
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ if (b_leaks_since_mark || b_leaks_total) {
+ /* All variables with "transient" in the name are per-dump counters
+ * and reset before each dump. This lets us handle checkpoints.
+ */
+ g_transient_count_total_leaks = 0;
+ g_transient_count_dedup_leaks = 0;
+ for (k = 0; k < g_cs_ins; k++) {
+ g_cs_rows[k].transient_count_leaks = 0;
+ }
+ }
+
+ g_transient_leaks_since_mark = b_leaks_since_mark;
+
+ old = _CrtSetReportHook(report_hook);
+ _CrtDumpMemoryLeaks();
+ _CrtSetReportHook(old);
+
+ if (b_leaks_since_mark || b_leaks_total) {
+ r = g_transient_count_dedup_leaks;
+
+ if (!b_quiet)
+ dump_summary(label);
+ }
+
+ if (b_set_mark) {
+ for (k = 0; k < g_cs_ins; k++) {
+ g_cs_rows[k].count_allocs_at_last_checkpoint = g_cs_rows[k].count_allocs;
+ }
+
+ g_checkpoint_id++;
+ }
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ return r;
+}
+
+/**
+ * Shutdown our memory leak tracking and dump summary data.
+ * This should ONLY be called by git_libgit2_shutdown().
+ *
+ * We explicitly call _CrtDumpMemoryLeaks() during here so
+ * that we can compute summary data for the leaks. We print
+ * the stacktrace of each unique leak.
+ *
+ * This cleanup does not happen if the app calls exit()
+ * without calling the libgit2 shutdown code.
+ *
+ * This info we print here is independent of any automatic
+ * reporting during exit() caused by _CRTDBG_LEAK_CHECK_DF.
+ * Set it in your app if you also want traditional reporting.
+ */
+void git_win32_leakcheck_stacktrace_cleanup(void)
+{
+ /* At shutdown/cleanup, dump cumulative leak info
+ * with everything since startup. This might generate
+ * extra noise if the caller has been doing checkpoint
+ * dumps, but it might also eliminate some false
+ * positives for resources previously reported during
+ * checkpoints.
+ */
+ git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL,
+ "CLEANUP");
+
+ DeleteCriticalSection(&g_crtdbg_stacktrace_cs);
+}
+
+const char *git_win32_leakcheck_stacktrace(int skip, const char *file)
+{
+ git_win32_leakcheck_stack_raw_data new_data;
+ git_win32_leakcheck_stacktrace_row *row;
+ const char * result = file;
+
+ if (git_win32_leakcheck_stack_capture(&new_data, skip+1) < 0)
+ return result;
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ if (g_cs_ins < g_cs_end) {
+ row = insert_unique(&new_data);
+ result = row->uid.uid;
+ } else {
+ g_limit_reached = true;
+ }
+
+ g_count_total_allocs++;
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ return result;
+}
+
+static void git_win32_leakcheck_global_shutdown(void)
+{
+ git_win32_leakcheck_stacktrace_cleanup();
+ git_win32_leakcheck_stack_cleanup();
+}
+
+bool git_win32_leakcheck_has_leaks(void)
+{
+ return (g_transient_count_total_leaks > 0);
+}
+
+int git_win32_leakcheck_global_init(void)
+{
+ git_win32_leakcheck_stacktrace_init();
+ git_win32_leakcheck_stack_init();
+
+ return git_runtime_shutdown_register(git_win32_leakcheck_global_shutdown);
+}
+
+#else
+
+int git_win32_leakcheck_global_init(void)
+{
+ return 0;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_win32_leakcheck_h__
+#define INCLUDE_win32_leakcheck_h__
+
+#include "common.h"
+
+/* Initialize the win32 leak checking system. */
+int git_win32_leakcheck_global_init(void);
+
+#if defined(GIT_WIN32_LEAKCHECK)
+
+#include <stdlib.h>
+#include <crtdbg.h>
+
+#include "git2/errors.h"
+#include "strnlen.h"
+
+bool git_win32_leakcheck_has_leaks(void);
+
+/* Stack frames (for stack tracing, below) */
+
+/**
+ * This type defines a callback to be used to augment a C stacktrace
+ * with "aux" data. This can be used, for example, to allow LibGit2Sharp
+ * (or other interpreted consumer libraries) to give us C# stacktrace
+ * data for the PInvoke.
+ *
+ * This callback will be called during crtdbg-instrumented allocs.
+ *
+ * @param aux_id [out] A returned "aux_id" representing a unique
+ * (de-duped at the C# layer) stacktrace. "aux_id" 0 is reserved
+ * to mean no aux stacktrace data.
+ */
+typedef void (*git_win32_leakcheck_stack_aux_cb_alloc)(unsigned int *aux_id);
+
+/**
+ * This type defines a callback to be used to augment the output of
+ * a stacktrace. This will be used to request the C# layer format
+ * the C# stacktrace associated with "aux_id" into the provided
+ * buffer.
+ *
+ * This callback will be called during leak reporting.
+ *
+ * @param aux_id The "aux_id" key associated with a stacktrace.
+ * @param aux_msg A buffer where a formatted message should be written.
+ * @param aux_msg_len The size of the buffer.
+ */
+typedef void (*git_win32_leakcheck_stack_aux_cb_lookup)(unsigned int aux_id, char *aux_msg, size_t aux_msg_len);
+
+/**
+ * Register an "aux" data provider to augment our C stacktrace data.
+ *
+ * This can be used, for example, to allow LibGit2Sharp (or other
+ * interpreted consumer libraries) to give us the C# stacktrace of
+ * the PInvoke.
+ *
+ * If you choose to use this feature, it should be registered during
+ * initialization and not changed for the duration of the process.
+ */
+int git_win32_leakcheck_stack_set_aux_cb(
+ git_win32_leakcheck_stack_aux_cb_alloc cb_alloc,
+ git_win32_leakcheck_stack_aux_cb_lookup cb_lookup);
+
+/**
+ * Maximum number of stackframes to record for a
+ * single stacktrace.
+ */
+#define GIT_WIN32_LEAKCHECK_STACK_MAX_FRAMES 30
+
+/**
+ * Wrapper containing the raw unprocessed stackframe
+ * data for a single stacktrace and any "aux_id".
+ *
+ * I put the aux_id first so leaks will be sorted by it.
+ * So, for example, if a specific callstack in C# leaks
+ * a repo handle, all of the pointers within the associated
+ * repo pointer will be grouped together.
+ */
+typedef struct {
+ unsigned int aux_id;
+ unsigned int nr_frames;
+ void *frames[GIT_WIN32_LEAKCHECK_STACK_MAX_FRAMES];
+} git_win32_leakcheck_stack_raw_data;
+
+/**
+ * Capture raw stack trace data for the current process/thread.
+ *
+ * @param skip Number of initial frames to skip. Pass 0 to
+ * begin with the caller of this routine. Pass 1 to begin
+ * with its caller. And so on.
+ */
+int git_win32_leakcheck_stack_capture(git_win32_leakcheck_stack_raw_data *pdata, int skip);
+
+/**
+ * Compare 2 raw stacktraces with the usual -1,0,+1 result.
+ * This includes any "aux_id" values in the comparison, so that
+ * our de-dup is also "aux" context relative.
+ */
+int git_win32_leakcheck_stack_compare(
+ git_win32_leakcheck_stack_raw_data *d1,
+ git_win32_leakcheck_stack_raw_data *d2);
+
+/**
+ * Format raw stacktrace data into buffer WITHOUT using any mallocs.
+ *
+ * @param prefix String written before each frame; defaults to "\t".
+ * @param suffix String written after each frame; defaults to "\n".
+ */
+int git_win32_leakcheck_stack_format(
+ char *pbuf, size_t buf_len,
+ const git_win32_leakcheck_stack_raw_data *pdata,
+ const char *prefix, const char *suffix);
+
+/**
+ * Convenience routine to capture and format stacktrace into
+ * a buffer WITHOUT using any mallocs. This is primarily a
+ * wrapper for testing.
+ *
+ * @param skip Number of initial frames to skip. Pass 0 to
+ * begin with the caller of this routine. Pass 1 to begin
+ * with its caller. And so on.
+ * @param prefix String written before each frame; defaults to "\t".
+ * @param suffix String written after each frame; defaults to "\n".
+ */
+int git_win32_leakcheck_stack(
+ char * pbuf, size_t buf_len,
+ int skip,
+ const char *prefix, const char *suffix);
+
+/* Stack tracing */
+
+/* MSVC CRTDBG memory leak reporting.
+ *
+ * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC
+ * documentation because all allocs/frees in libgit2 already go through
+ * the "git__" routines defined in this file. Simply using the normal
+ * reporting mechanism causes all leaks to be attributed to a routine
+ * here in util.h (ie, the actual call to calloc()) rather than the
+ * caller of git__calloc().
+ *
+ * Therefore, we declare a set of "git__crtdbg__" routines to replace
+ * the corresponding "git__" routines and re-define the "git__" symbols
+ * as macros. This allows us to get and report the file:line info of
+ * the real caller.
+ *
+ * We DO NOT replace the "git__free" routine because it needs to remain
+ * a function pointer because it is used as a function argument when
+ * setting up various structure "destructors".
+ *
+ * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes
+ * "free" to be remapped to "_free_dbg" and this causes problems for
+ * structures which define a field named "free".
+ *
+ * Finally, CRTDBG must be explicitly enabled and configured at program
+ * startup. See tests/main.c for an example.
+ */
+
+/**
+ * Checkpoint options.
+ */
+typedef enum git_win32_leakcheck_stacktrace_options {
+ /**
+ * Set checkpoint marker.
+ */
+ GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK = (1 << 0),
+
+ /**
+ * Dump leaks since last checkpoint marker.
+ * May not be combined with _LEAKS_TOTAL.
+ *
+ * Note that this may generate false positives for global TLS
+ * error state and other global caches that aren't cleaned up
+ * until the thread/process terminates. So when using this
+ * around a region of interest, also check the final (at exit)
+ * dump before digging into leaks reported here.
+ */
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK = (1 << 1),
+
+ /**
+ * Dump leaks since init. May not be combined
+ * with _LEAKS_SINCE_MARK.
+ */
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL = (1 << 2),
+
+ /**
+ * Suppress printing during dumps.
+ * Just return leak count.
+ */
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET = (1 << 3),
+
+} git_win32_leakcheck_stacktrace_options;
+
+/**
+ * Checkpoint memory state and/or dump unique stack traces of
+ * current memory leaks.
+ *
+ * @return number of unique leaks (relative to requested starting
+ * point) or error.
+ */
+int git_win32_leakcheck_stacktrace_dump(
+ git_win32_leakcheck_stacktrace_options opt,
+ const char *label);
+
+/**
+ * Construct stacktrace and append it to the global buffer.
+ * Return pointer to start of this string. On any error or
+ * lack of buffer space, just return the given file buffer
+ * so it will behave as usual.
+ *
+ * This should ONLY be called by our internal memory allocations
+ * routines.
+ */
+const char *git_win32_leakcheck_stacktrace(int skip, const char *file);
+
+#endif
+#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "w32_stack.h"
-
-#if defined(GIT_MSVC_CRTDBG)
-#include "Windows.h"
-#include "Dbghelp.h"
-#include "win32/posix.h"
-#include "hash.h"
-
-static bool g_win32_stack_initialized = false;
-static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE;
-static git_win32__stack__aux_cb_alloc g_aux_cb_alloc = NULL;
-static git_win32__stack__aux_cb_lookup g_aux_cb_lookup = NULL;
-
-int git_win32__stack__set_aux_cb(
- git_win32__stack__aux_cb_alloc cb_alloc,
- git_win32__stack__aux_cb_lookup cb_lookup)
-{
- g_aux_cb_alloc = cb_alloc;
- g_aux_cb_lookup = cb_lookup;
-
- return 0;
-}
-
-void git_win32__stack_init(void)
-{
- if (!g_win32_stack_initialized) {
- g_win32_stack_process = GetCurrentProcess();
- SymSetOptions(SYMOPT_LOAD_LINES);
- SymInitialize(g_win32_stack_process, NULL, TRUE);
- g_win32_stack_initialized = true;
- }
-}
-
-void git_win32__stack_cleanup(void)
-{
- if (g_win32_stack_initialized) {
- SymCleanup(g_win32_stack_process);
- g_win32_stack_process = INVALID_HANDLE_VALUE;
- g_win32_stack_initialized = false;
- }
-}
-
-int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip)
-{
- if (!g_win32_stack_initialized) {
- git_error_set(GIT_ERROR_INVALID, "git_win32_stack not initialized.");
- return GIT_ERROR;
- }
-
- memset(pdata, 0, sizeof(*pdata));
- pdata->nr_frames = RtlCaptureStackBackTrace(
- skip+1, GIT_WIN32__STACK__MAX_FRAMES, pdata->frames, NULL);
-
- /* If an "aux" data provider was registered, ask it to capture
- * whatever data it needs and give us an "aux_id" to it so that
- * we can refer to it later when reporting.
- */
- if (g_aux_cb_alloc)
- (g_aux_cb_alloc)(&pdata->aux_id);
-
- return 0;
-}
-
-int git_win32__stack_compare(
- git_win32__stack__raw_data *d1,
- git_win32__stack__raw_data *d2)
-{
- return memcmp(d1, d2, sizeof(*d1));
-}
-
-int git_win32__stack_format(
- char *pbuf, size_t buf_len,
- const git_win32__stack__raw_data *pdata,
- const char *prefix, const char *suffix)
-{
-#define MY_MAX_FILENAME 255
-
- /* SYMBOL_INFO has char FileName[1] at the end. The docs say to
- * to malloc it with extra space for your desired max filename.
- */
- struct {
- SYMBOL_INFO symbol;
- char extra[MY_MAX_FILENAME + 1];
- } s;
-
- IMAGEHLP_LINE64 line;
- size_t buf_used = 0;
- unsigned int k;
- char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */
- size_t detail_len;
-
- if (!g_win32_stack_initialized) {
- git_error_set(GIT_ERROR_INVALID, "git_win32_stack not initialized.");
- return GIT_ERROR;
- }
-
- if (!prefix)
- prefix = "\t";
- if (!suffix)
- suffix = "\n";
-
- memset(pbuf, 0, buf_len);
-
- memset(&s, 0, sizeof(s));
- s.symbol.MaxNameLen = MY_MAX_FILENAME;
- s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
-
- memset(&line, 0, sizeof(line));
- line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-
- for (k=0; k < pdata->nr_frames; k++) {
- DWORD64 frame_k = (DWORD64)pdata->frames[k];
- DWORD dwUnused;
-
- if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) &&
- SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) {
- const char *pslash;
- const char *pfile;
-
- pslash = strrchr(line.FileName, '\\');
- pfile = ((pslash) ? (pslash+1) : line.FileName);
- p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s",
- prefix, pfile, line.LineNumber, s.symbol.Name, suffix);
- } else {
- /* This happens when we cross into another module.
- * For example, in CLAR tests, this is typically
- * the CRT startup code. Just print an unknown
- * frame and continue.
- */
- p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix);
- }
- detail_len = strlen(detail);
-
- if (buf_len < (buf_used + detail_len + 1)) {
- /* we don't have room for this frame in the buffer, so just stop. */
- break;
- }
-
- memcpy(&pbuf[buf_used], detail, detail_len);
- buf_used += detail_len;
- }
-
- /* "aux_id" 0 is reserved to mean no aux data. This is needed to handle
- * allocs that occur before the aux callbacks were registered.
- */
- if (pdata->aux_id > 0) {
- p_snprintf(detail, sizeof(detail), "%saux_id: %d%s",
- prefix, pdata->aux_id, suffix);
- detail_len = strlen(detail);
- if ((buf_used + detail_len + 1) < buf_len) {
- memcpy(&pbuf[buf_used], detail, detail_len);
- buf_used += detail_len;
- }
-
- /* If an "aux" data provider is still registered, ask it to append its detailed
- * data to the end of ours using the "aux_id" it gave us when this de-duped
- * item was created.
- */
- if (g_aux_cb_lookup)
- (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1));
- }
-
- return GIT_OK;
-}
-
-int git_win32__stack(
- char * pbuf, size_t buf_len,
- int skip,
- const char *prefix, const char *suffix)
-{
- git_win32__stack__raw_data data;
- int error;
-
- if ((error = git_win32__stack_capture(&data, skip)) < 0)
- return error;
- if ((error = git_win32__stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0)
- return error;
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_win32_w32_stack_h__
-#define INCLUDE_win32_w32_stack_h__
-
-#include "common.h"
-
-#if defined(GIT_MSVC_CRTDBG)
-
-/**
- * This type defines a callback to be used to augment a C stacktrace
- * with "aux" data. This can be used, for example, to allow LibGit2Sharp
- * (or other interpreted consumer libraries) to give us C# stacktrace
- * data for the PInvoke.
- *
- * This callback will be called during crtdbg-instrumented allocs.
- *
- * @param aux_id [out] A returned "aux_id" representing a unique
- * (de-duped at the C# layer) stacktrace. "aux_id" 0 is reserved
- * to mean no aux stacktrace data.
- */
-typedef void (*git_win32__stack__aux_cb_alloc)(unsigned int *aux_id);
-
-/**
- * This type defines a callback to be used to augment the output of
- * a stacktrace. This will be used to request the C# layer format
- * the C# stacktrace associated with "aux_id" into the provided
- * buffer.
- *
- * This callback will be called during leak reporting.
- *
- * @param aux_id The "aux_id" key associated with a stacktrace.
- * @param aux_msg A buffer where a formatted message should be written.
- * @param aux_msg_len The size of the buffer.
- */
-typedef void (*git_win32__stack__aux_cb_lookup)(unsigned int aux_id, char *aux_msg, size_t aux_msg_len);
-
-/**
- * Register an "aux" data provider to augment our C stacktrace data.
- *
- * This can be used, for example, to allow LibGit2Sharp (or other
- * interpreted consumer libraries) to give us the C# stacktrace of
- * the PInvoke.
- *
- * If you choose to use this feature, it should be registered during
- * initialization and not changed for the duration of the process.
- */
-GIT_EXTERN(int) git_win32__stack__set_aux_cb(
- git_win32__stack__aux_cb_alloc cb_alloc,
- git_win32__stack__aux_cb_lookup cb_lookup);
-
-/**
- * Maximum number of stackframes to record for a
- * single stacktrace.
- */
-#define GIT_WIN32__STACK__MAX_FRAMES 30
-
-/**
- * Wrapper containing the raw unprocessed stackframe
- * data for a single stacktrace and any "aux_id".
- *
- * I put the aux_id first so leaks will be sorted by it.
- * So, for example, if a specific callstack in C# leaks
- * a repo handle, all of the pointers within the associated
- * repo pointer will be grouped together.
- */
-typedef struct {
- unsigned int aux_id;
- unsigned int nr_frames;
- void *frames[GIT_WIN32__STACK__MAX_FRAMES];
-} git_win32__stack__raw_data;
-
-
-/**
- * Load symbol table data. This should be done in the primary
- * thread at startup (under a lock if there are other threads
- * active).
- */
-void git_win32__stack_init(void);
-
-/**
- * Cleanup symbol table data. This should be done in the
- * primary thead at shutdown (under a lock if there are other
- * threads active).
- */
-void git_win32__stack_cleanup(void);
-
-
-/**
- * Capture raw stack trace data for the current process/thread.
- *
- * @param skip Number of initial frames to skip. Pass 0 to
- * begin with the caller of this routine. Pass 1 to begin
- * with its caller. And so on.
- */
-int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip);
-
-/**
- * Compare 2 raw stacktraces with the usual -1,0,+1 result.
- * This includes any "aux_id" values in the comparison, so that
- * our de-dup is also "aux" context relative.
- */
-int git_win32__stack_compare(
- git_win32__stack__raw_data *d1,
- git_win32__stack__raw_data *d2);
-
-/**
- * Format raw stacktrace data into buffer WITHOUT using any mallocs.
- *
- * @param prefix String written before each frame; defaults to "\t".
- * @param suffix String written after each frame; defaults to "\n".
- */
-int git_win32__stack_format(
- char *pbuf, size_t buf_len,
- const git_win32__stack__raw_data *pdata,
- const char *prefix, const char *suffix);
-
-/**
- * Convenience routine to capture and format stacktrace into
- * a buffer WITHOUT using any mallocs. This is primarily a
- * wrapper for testing.
- *
- * @param skip Number of initial frames to skip. Pass 0 to
- * begin with the caller of this routine. Pass 1 to begin
- * with its caller. And so on.
- * @param prefix String written before each frame; defaults to "\t".
- * @param suffix String written after each frame; defaults to "\n".
- */
-int git_win32__stack(
- char * pbuf, size_t buf_len,
- int skip,
- const char *prefix, const char *suffix);
-
-#endif /* GIT_MSVC_CRTDBG */
-#endif
const FILETIME *ft,
struct timespec *ts)
{
- long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
- winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
+ int64_t winTime = ((int64_t)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
+ winTime -= INT64_C(116444736000000000); /* Windows to Unix Epoch conversion */
ts->tv_sec = (time_t)(winTime / 10000000);
#ifdef GIT_USE_NSEC
ts->tv_nsec = (winTime % 10000000) * 100;
GIT_INLINE(void) git_win32__timeval_to_filetime(
FILETIME *ft, const struct p_timeval tv)
{
- long long ticks = (tv.tv_sec * 10000000LL) +
- (tv.tv_usec * 10LL) + 116444736000000000LL;
+ int64_t ticks = (tv.tv_sec * INT64_C(10000000)) +
+ (tv.tv_usec * INT64_C(10)) + INT64_C(116444736000000000);
- ft->dwHighDateTime = ((ticks >> 32) & 0xffffffffLL);
- ft->dwLowDateTime = (ticks & 0xffffffffLL);
+ ft->dwHighDateTime = ((ticks >> 32) & INT64_C(0xffffffff));
+ ft->dwLowDateTime = (ticks & INT64_C(0xffffffff));
}
GIT_INLINE(void) git_win32__stat_init(
size_t i, len;
int error;
- assert(wts && repo);
+ GIT_ASSERT_ARG(wts);
+ GIT_ASSERT_ARG(repo);
wts->count = 0;
wts->strings = NULL;
- if ((error = git_buf_printf(&path, "%s/worktrees/", repo->commondir)) < 0)
+ if ((error = git_buf_joinpath(&path, repo->commondir, "worktrees/")) < 0)
goto exit;
if (!git_path_exists(path.ptr) || git_path_is_empty_dir(path.ptr))
goto exit;
{
git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT;
- assert(base && file);
+ GIT_ASSERT_ARG_WITH_RETVAL(base, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(file, NULL);
if (git_buf_joinpath(&path, base, file) < 0)
goto err;
git_buf path = GIT_BUF_INIT;
int err;
- assert(base && file && buf);
+ GIT_ASSERT_ARG(base);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(buf);
if ((err = git_buf_joinpath(&path, base, file)) < 0)
goto out;
goto out;
}
+ if ((error = git_path_validate_workdir(NULL, dir)) < 0)
+ goto out;
+
if ((wt = git__calloc(1, sizeof(*wt))) == NULL) {
error = -1;
goto out;
git_worktree *wt = NULL;
int error;
- assert(repo && name);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
*out = NULL;
- if ((error = git_buf_printf(&path, "%s/worktrees/%s", repo->commondir, name)) < 0)
+ if ((error = git_buf_join3(&path, '/', repo->commondir, "worktrees", name)) < 0)
goto out;
if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0)
int git_worktree_validate(const git_worktree *wt)
{
- assert(wt);
+ GIT_ASSERT_ARG(wt);
if (!is_worktree_dir(wt->gitdir_path)) {
git_error_set(GIT_ERROR_WORKTREE,
return GIT_ERROR;
}
+ if (!git_path_exists(wt->worktree_path)) {
+ git_error_set(GIT_ERROR_WORKTREE,
+ "worktree directory '%s' does not exist",
+ wt->worktree_path);
+ return GIT_ERROR;
+ }
+
return 0;
}
if (opts)
memcpy(&wtopts, opts, sizeof(wtopts));
- assert(out && repo && name && worktree);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(worktree);
*out = NULL;
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
int error;
- assert(wt);
+ GIT_ASSERT_ARG(wt);
if ((error = git_worktree_is_locked(NULL, wt)) < 0)
goto out;
git_buf path = GIT_BUF_INIT;
int error;
- assert(wt);
+ GIT_ASSERT_ARG(wt);
if ((error = git_worktree_is_locked(NULL, wt)) < 0)
return error;
git_buf path = GIT_BUF_INIT;
int error, locked;
- assert(wt);
+ GIT_ASSERT_ARG(wt);
if (reason)
git_buf_clear(reason);
const char *git_worktree_name(const git_worktree *wt)
{
- assert(wt);
+ GIT_ASSERT_ARG_WITH_RETVAL(wt, NULL);
return wt->name;
}
const char *git_worktree_path(const git_worktree *wt)
{
- assert(wt);
+ GIT_ASSERT_ARG_WITH_RETVAL(wt, NULL);
return wt->worktree_path;
}
}
/* Delete gitdir in parent repository */
- if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name)) < 0)
+ if ((err = git_buf_join3(&path, '/', wt->commondir_path, "worktrees", wt->name)) < 0)
goto out;
if (!git_path_exists(path.ptr))
{
}
/* either we finished the input or we did not flush the data */
- assert(zstream->in_len > 0 || zstream->flush == Z_FINISH);
+ GIT_ASSERT(zstream->in_len > 0 || zstream->flush == Z_FINISH);
/* set out_size to number of bytes actually written to output */
*out_len = *out_len - out_remain;
+SET(Python_ADDITIONAL_VERSIONS 3 2.7)
FIND_PACKAGE(PythonInterp)
IF(NOT PYTHONINTERP_FOUND)
SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
ADD_DEFINITIONS(-DCLAR_TMPDIR=\"libgit2_tests\")
+ADD_DEFINITIONS(-DCLAR_WIN32_LONGPATHS)
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
# Ensure that we do not use deprecated functions internally
ADD_CLAR_TEST(offline -v -xonline)
ADD_CLAR_TEST(invasive -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root)
-ADD_CLAR_TEST(online -v -sonline)
+ADD_CLAR_TEST(online -v -sonline -xonline::customcert)
+ADD_CLAR_TEST(online_customcert -v -sonline::customcert)
ADD_CLAR_TEST(gitdaemon -v -sonline::push)
ADD_CLAR_TEST(ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh)
ADD_CLAR_TEST(proxy -v -sonline::clone::proxy)
"-asparagus which had been laid by, boil it until these last articles are\n" \
"-sufficiently done, thicken with flour, butter and milk, and serve it up.\n"
+#define DIFF_ADD_INVALID_FILENAME \
+ "diff --git a/.git/hello_world.txt b/.git/hello_world.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..f75ba05\n" \
+ "--- /dev/null\n" \
+ "+++ b/.git/hello_world.txt\n" \
+ "@@ -0,0 +1 @@\n" \
+ "+Hello, world.\n"
+
void validate_apply_workdir(
git_repository *repo,
struct merge_index_entry *workdir_entries,
git_diff_free(diff);
}
+
+void test_apply_both__cant_add_invalid_filename(void)
+{
+ git_diff *diff;
+
+ cl_git_pass(git_diff_from_buffer(&diff, DIFF_ADD_INVALID_FILENAME,
+ strlen(DIFF_ADD_INVALID_FILENAME)));
+ cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ git_diff_free(diff);
+}
#include "apply.h"
#include "repository.h"
-#include "buf_text.h"
#include "../patch/patch_common.h"
#include "patch.h"
#include "patch_parse.h"
#include "repository.h"
-#include "buf_text.h"
#include "../patch/patch_common.h"
#include "apply.h"
#include "repository.h"
-#include "buf_text.h"
#include "../patch/patch_common.h"
void test_attr_lookup__from_buffer(void)
{
git_attr_file *file;
+ git_attr_file_source source = {0};
struct attr_expected cases[] = {
{ "abc", "foo", EXPECT_TRUE, NULL },
{ NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file, NULL, 0));
+ cl_git_pass(git_attr_file__new(&file, NULL, &source));
cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz", true));
}
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes"));
}
void test_attr_repo__get_one_start_deep(void)
}
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes"));
}
void test_attr_repo__get_many(void)
g_repo = cl_git_sandbox_reopen();
cl_git_pass(git_attr_session__init(&session, g_repo));
- cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, 0, "file", ARRAY_SIZE(attrs), attrs));
+ cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, NULL, "file", ARRAY_SIZE(attrs), attrs));
cl_assert_equal_s(values[0], "1");
cl_assert_equal_s(values[1], "2");
modify_index_and_checkout_tree(&opts);
assert_status_entrycount(g_repo, 0);
}
+
+void test_checkout_tree__dry_run(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+ checkout_counts ct;
+
+ /* first let's get things into a known state - by checkout out the HEAD */
+
+ assert_on_branch(g_repo, "master");
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_path_isdir("testrepo/a"));
+
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+ /* now checkout branch but with dry run enabled */
+
+ memset(&ct, 0, sizeof(ct));
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DRY_RUN;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &ct;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ assert_on_branch(g_repo, "dir");
+
+ /* these normally would have been created and updated, but with
+ * DRY_RUN they will be unchanged.
+ */
+ cl_assert(!git_path_isdir("testrepo/a"));
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+ /* check that notify callback was invoked */
+ cl_assert_equal_i(ct.n_updates, 2);
+
+ git_object_free(obj);
+}
#ifdef _WIN32
+#ifdef CLAR_WIN32_LONGPATHS
+# define CLAR_MAX_PATH 4096
+#else
+# define CLAR_MAX_PATH MAX_PATH
+#endif
+
#define RM_RETRY_COUNT 5
#define RM_RETRY_DELAY 10
return 0;
}
+static void translate_path(WCHAR *path, size_t path_size)
+{
+ size_t path_len, i;
+
+ if (wcsncmp(path, L"\\\\?\\", 4) == 0)
+ return;
+
+ path_len = wcslen(path);
+ cl_assert(path_size > path_len + 4);
+
+ for (i = path_len; i > 0; i--) {
+ WCHAR c = path[i - 1];
+
+ if (c == L'/')
+ path[i + 3] = L'\\';
+ else
+ path[i + 3] = path[i - 1];
+ }
+
+ path[0] = L'\\';
+ path[1] = L'\\';
+ path[2] = L'?';
+ path[3] = L'\\';
+ path[path_len + 4] = L'\0';
+}
+
static void
fs_rmdir_helper(WCHAR *_wsource)
{
- WCHAR buffer[MAX_PATH];
+ WCHAR buffer[CLAR_MAX_PATH];
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buffer_prefix_len;
/* Set up the buffer and capture the length */
- wcscpy_s(buffer, MAX_PATH, _wsource);
- wcscat_s(buffer, MAX_PATH, L"\\");
+ wcscpy_s(buffer, CLAR_MAX_PATH, _wsource);
+ translate_path(buffer, CLAR_MAX_PATH);
+ wcscat_s(buffer, CLAR_MAX_PATH, L"\\");
buffer_prefix_len = wcslen(buffer);
/* FindFirstFile needs a wildcard to match multiple items */
- wcscat_s(buffer, MAX_PATH, L"*");
+ wcscat_s(buffer, CLAR_MAX_PATH, L"*");
find_handle = FindFirstFileW(buffer, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
if (fs__dotordotdot(find_data.cFileName))
continue;
- wcscpy_s(buffer + buffer_prefix_len, MAX_PATH - buffer_prefix_len, find_data.cFileName);
+ wcscpy_s(buffer + buffer_prefix_len, CLAR_MAX_PATH - buffer_prefix_len, find_data.cFileName);
if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
fs_rmdir_helper(buffer);
static void
fs_rm(const char *_source)
{
- WCHAR wsource[MAX_PATH];
+ WCHAR wsource[CLAR_MAX_PATH];
DWORD attrs;
/* The input path is UTF-8. Convert it to wide characters
_source,
-1, /* Indicates NULL termination */
wsource,
- MAX_PATH));
+ CLAR_MAX_PATH));
+
+ translate_path(wsource, CLAR_MAX_PATH);
/* Does the item exist? If not, we have no work to do */
attrs = GetFileAttributesW(wsource);
static void
fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest)
{
- WCHAR buf_source[MAX_PATH], buf_dest[MAX_PATH];
+ WCHAR buf_source[CLAR_MAX_PATH], buf_dest[CLAR_MAX_PATH];
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buf_source_prefix_len, buf_dest_prefix_len;
- wcscpy_s(buf_source, MAX_PATH, _wsource);
- wcscat_s(buf_source, MAX_PATH, L"\\");
+ wcscpy_s(buf_source, CLAR_MAX_PATH, _wsource);
+ wcscat_s(buf_source, CLAR_MAX_PATH, L"\\");
+ translate_path(buf_source, CLAR_MAX_PATH);
buf_source_prefix_len = wcslen(buf_source);
- wcscpy_s(buf_dest, MAX_PATH, _wdest);
- wcscat_s(buf_dest, MAX_PATH, L"\\");
+ wcscpy_s(buf_dest, CLAR_MAX_PATH, _wdest);
+ wcscat_s(buf_dest, CLAR_MAX_PATH, L"\\");
+ translate_path(buf_dest, CLAR_MAX_PATH);
buf_dest_prefix_len = wcslen(buf_dest);
/* Get an enumerator for the items in the source. */
- wcscat_s(buf_source, MAX_PATH, L"*");
+ wcscat_s(buf_source, CLAR_MAX_PATH, L"*");
find_handle = FindFirstFileW(buf_source, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
if (fs__dotordotdot(find_data.cFileName))
continue;
- wcscpy_s(buf_source + buf_source_prefix_len, MAX_PATH - buf_source_prefix_len, find_data.cFileName);
- wcscpy_s(buf_dest + buf_dest_prefix_len, MAX_PATH - buf_dest_prefix_len, find_data.cFileName);
+ wcscpy_s(buf_source + buf_source_prefix_len, CLAR_MAX_PATH - buf_source_prefix_len, find_data.cFileName);
+ wcscpy_s(buf_dest + buf_dest_prefix_len, CLAR_MAX_PATH - buf_dest_prefix_len, find_data.cFileName);
if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
fs_copydir_helper(buf_source, buf_dest);
static void
fs_copy(const char *_source, const char *_dest)
{
- WCHAR wsource[MAX_PATH], wdest[MAX_PATH];
+ WCHAR wsource[CLAR_MAX_PATH], wdest[CLAR_MAX_PATH];
DWORD source_attrs, dest_attrs;
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
_source,
-1,
wsource,
- MAX_PATH));
+ CLAR_MAX_PATH));
cl_assert(MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
_dest,
-1,
wdest,
- MAX_PATH));
+ CLAR_MAX_PATH));
+
+ translate_path(wsource, CLAR_MAX_PATH);
+ translate_path(wdest, CLAR_MAX_PATH);
/* Check the source for existence */
source_attrs = GetFileAttributesW(wsource);
* Use FindFirstFile to parse the path */
find_handle = FindFirstFileW(wsource, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
- wcscat_s(wdest, MAX_PATH, L"\\");
- wcscat_s(wdest, MAX_PATH, find_data.cFileName);
+ wcscat_s(wdest, CLAR_MAX_PATH, L"\\");
+ wcscat_s(wdest, CLAR_MAX_PATH, find_data.cFileName);
FindClose(find_handle);
/* Check the new target for existence */
fs_copy(const char *source, const char *_dest)
{
char *dbuf = NULL;
- const char *dest;
+ const char *dest = NULL;
struct stat source_st, dest_st;
cl_must_pass_(lstat(source, &source_st), "Failed to stat copy source");
#include <sys/syslimits.h>
#endif
-static char _clar_path[4096];
+static char _clar_path[4096 + 1];
static int
is_valid_tmp_path(const char *path)
if (length >= PATH_MAX && realpath(env, buffer) != NULL)
return 0;
#endif
- strncpy(buffer, env, length);
+ strncpy(buffer, env, length - 1);
+ buffer[length - 1] = '\0';
return 0;
}
}
if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
return 0;
#endif
- strncpy(buffer, "/tmp", length);
+ strncpy(buffer, "/tmp", length - 1);
+ buffer[length - 1] = '\0';
return 0;
}
/* This system doesn't like us, try to use the current directory */
if (is_valid_tmp_path(".")) {
- strncpy(buffer, ".", length);
+ strncpy(buffer, ".", length - 1);
+ buffer[length - 1] = '\0';
return 0;
}
const char* cl_git_path_url(const char *path)
{
- static char url[4096];
+ static char url[4096 + 1];
const char *in_buf;
git_buf path_buf = GIT_BUF_INIT;
in_buf++;
}
- cl_assert(url_buf.size < 4096);
+ cl_assert(url_buf.size < sizeof(url) - 1);
- strncpy(url, git_buf_cstr(&url_buf), 4096);
+ strncpy(url, git_buf_cstr(&url_buf), sizeof(url) - 1);
+ url[sizeof(url) - 1] = '\0';
git_buf_dispose(&url_buf);
git_buf_dispose(&path_buf);
return url;
switch (ev) {
case CL_TRACE__SUITE_BEGIN:
git_trace(GIT_TRACE_TRACE, "\n\n%s\n%s: Begin Suite", HR, suite_name);
-#if 0 && defined(GIT_MSVC_CRTDBG)
+#if 0 && defined(GIT_WIN32_LEAKCHECK)
git_win32__crtdbg_stacktrace__dump(
GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
suite_name);
break;
case CL_TRACE__SUITE_END:
-#if 0 && defined(GIT_MSVC_CRTDBG)
+#if 0 && defined(GIT_WIN32_LEAKCHECK)
/* As an example of checkpointing, dump leaks within this suite.
* This may generate false positives for things like the global
* TLS error state and maybe the odb cache since they aren't
void test_clone_nonetwork__can_checkout_given_branch(void)
{
+ git_reference *remote_head;
+
g_options.checkout_branch = "test";
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test");
cl_assert(git_path_exists("foo/readme.txt"));
+
+ cl_git_pass(git_reference_lookup(&remote_head, g_repo, "refs/remotes/origin/HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(remote_head));
+ cl_assert_equal_s("refs/remotes/origin/master", git_reference_symbolic_target(remote_head));
+
+ git_reference_free(remote_head);
}
static int clone_cancel_fetch_transfer_progress_cb(
cl_git_pass(p_unlink("second"));
}
+void test_config_include__rewriting_include_twice_refreshes_values(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = included");
+ cl_git_mkfile("included", "[foo]\nbar = first-value");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+
+ git_buf_clear(&buf);
+ cl_git_mkfile("included", "[foo]\nother = value2");
+ cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.other"));
+ cl_assert_equal_s(buf.ptr, "value2");
+
+ git_buf_clear(&buf);
+ cl_git_mkfile("included", "[foo]\nanother = bar");
+ cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.other"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.another"));
+ cl_assert_equal_s(buf.ptr, "bar");
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("included"));
+}
+
void test_config_include__included_variables_cannot_be_deleted(void)
{
cl_git_mkfile("top-level", "[include]\npath = included\n");
git_config_free(cfg);
}
+void test_config_read__multiline_multiple_quoted_comment_chars(void)
+{
+ git_config *cfg;
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config21")));
+ git_config_free(cfg);
+}
+
void test_config_read__header_in_last_line(void)
{
git_config *cfg;
" key9 = off\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
- // check parsing bool and string
+ /* check parsing bool and string */
cl_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map1, ARRAY_SIZE(_test_map1)));
cl_assert_equal_i(val, MAP_TRUE);
cl_git_pass(git_config_get_mapped(&val, cfg, "header.key2", _test_map1, ARRAY_SIZE(_test_map1)));
cl_git_fail(git_config_get_mapped(&val, cfg, "header.key7", _test_map1, ARRAY_SIZE(_test_map1)));
- // check parsing int values
+ /* check parsing int values */
cl_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map2, ARRAY_SIZE(_test_map2)));
cl_git_pass(git_config_get_int32(&known_good, cfg, "header.key1"));
cl_assert_equal_i(val, known_good);
#include "clar_libgit2.h"
#include "buffer.h"
-#include "buf_text.h"
#include "git2/sys/hashsig.h"
#include "futils.h"
void test_core_buffer__11(void)
{
git_buf a = GIT_BUF_INIT;
- git_strarray t;
char *t1[] = { "nothing", "in", "common" };
char *t2[] = { "something", "something else", "some other" };
char *t3[] = { "something", "some fun", "no fun" };
char *t6[] = { "no", "nope", "" };
char *t7[] = { "", "doesn't matter" };
- t.strings = t1;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_git_pass(git_buf_common_prefix(&a, t1, 3));
cl_assert_equal_s(a.ptr, "");
- t.strings = t2;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_git_pass(git_buf_common_prefix(&a, t2, 3));
cl_assert_equal_s(a.ptr, "some");
- t.strings = t3;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_git_pass(git_buf_common_prefix(&a, t3, 3));
cl_assert_equal_s(a.ptr, "");
- t.strings = t4;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_git_pass(git_buf_common_prefix(&a, t4, 3));
cl_assert_equal_s(a.ptr, "happ");
- t.strings = t5;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_git_pass(git_buf_common_prefix(&a, t5, 3));
cl_assert_equal_s(a.ptr, "happ");
- t.strings = t6;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_git_pass(git_buf_common_prefix(&a, t6, 3));
cl_assert_equal_s(a.ptr, "");
- t.strings = t7;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_git_pass(git_buf_common_prefix(&a, t7, 3));
cl_assert_equal_s(a.ptr, "");
git_buf_dispose(&a);
git_buf a = GIT_BUF_INIT;
git_buf_clear(&a);
- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "", ""));
+ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "", ""));
cl_assert_equal_s("this is a test", a.ptr);
git_buf_clear(&a);
- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "t", "\\"));
+ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "t", "\\"));
cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
git_buf_clear(&a);
- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "i ", "__"));
+ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "i ", "__"));
cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
git_buf_clear(&a);
- cl_git_pass(git_buf_text_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
+ cl_git_pass(git_buf_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);
git_buf_dispose(&a);
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_sets(&buf, to_unescape));
- git_buf_text_unescape(&buf);
+ git_buf_unescape(&buf);
cl_assert_equal_s(expected, buf.ptr);
cl_assert_equal_sz(strlen(expected), buf.size);
git_buf b;
b.ptr = data0; b.size = b.asize = data0len;
- cl_assert(!git_buf_text_is_binary(&b));
- cl_assert(!git_buf_text_contains_nul(&b));
+ cl_assert(!git_buf_is_binary(&b));
+ cl_assert(!git_buf_contains_nul(&b));
b.ptr = data1; b.size = b.asize = data1len;
- cl_assert(!git_buf_text_is_binary(&b));
- cl_assert(!git_buf_text_contains_nul(&b));
+ cl_assert(!git_buf_is_binary(&b));
+ cl_assert(!git_buf_contains_nul(&b));
b.ptr = data2; b.size = b.asize = data2len;
- cl_assert(git_buf_text_is_binary(&b));
- cl_assert(git_buf_text_contains_nul(&b));
+ cl_assert(git_buf_is_binary(&b));
+ cl_assert(git_buf_contains_nul(&b));
b.ptr = data3; b.size = b.asize = data3len;
- cl_assert(!git_buf_text_is_binary(&b));
- cl_assert(!git_buf_text_contains_nul(&b));
+ cl_assert(!git_buf_is_binary(&b));
+ cl_assert(!git_buf_contains_nul(&b));
}
#define SIMILARITY_TEST_DATA_1 \
git_buf_sets(&src, "lf\nlf\nlf\nlf\n");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf(src.ptr, tgt);
git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf(src.ptr, tgt);
/* CRLF source */
git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
/* CRLF in LF text */
git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
/* LF in CRLF text */
git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
/* bare CR test */
git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
git_buf_sets(&src, "\rcr\r");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf(src.ptr, tgt);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf("\rcr\r", tgt);
git_buf_dispose(&src);
/* blob correspondence tests */
git_buf_sets(&src, ALL_CRLF_TEXT_RAW);
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt);
git_buf_sets(&src, ALL_CRLF_TEXT_RAW);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf(ALL_CRLF_TEXT_AS_LF, tgt);
git_buf_dispose(&src);
git_buf_dispose(&tgt);
git_buf_sets(&src, ALL_LF_TEXT_RAW);
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf(ALL_LF_TEXT_AS_CRLF, tgt);
git_buf_sets(&src, ALL_LF_TEXT_RAW);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf(ALL_LF_TEXT_AS_LF, tgt);
git_buf_dispose(&src);
git_buf_dispose(&tgt);
git_buf_sets(&src, MORE_CRLF_TEXT_RAW);
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt);
git_buf_sets(&src, MORE_CRLF_TEXT_RAW);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf(MORE_CRLF_TEXT_AS_LF, tgt);
git_buf_dispose(&src);
git_buf_dispose(&tgt);
git_buf_sets(&src, MORE_LF_TEXT_RAW);
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_lf_to_crlf(&tgt, &src));
check_buf(MORE_LF_TEXT_AS_CRLF, tgt);
git_buf_sets(&src, MORE_LF_TEXT_RAW);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ cl_git_pass(git_buf_crlf_to_lf(&tgt, &src));
check_buf(MORE_LF_TEXT_AS_LF, tgt);
git_buf_dispose(&src);
git_buf_dispose(&tgt);
cl_assert(size == 4);
buf = (unsigned char *)"\xaa\xaa\xfe\xdc\xbaXY";
- cl_assert(git_decode_varint(buf, &size) == 1489279344088ULL);
+ cl_assert(git_decode_varint(buf, &size) == UINT64_C(1489279344088));
cl_assert(size == 6);
buf = (unsigned char *)"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xfe\xdc\xbaXY";
cl_assert(git_encode_varint(buf, 100, 267869656) == 4);
cl_assert(!memcmp(buf, "\xfe\xdc\xbaX", 4));
- cl_assert(git_encode_varint(buf, 100, 1489279344088ULL) == 6);
+ cl_assert(git_encode_varint(buf, 100, UINT64_C(1489279344088)) == 6);
cl_assert(!memcmp(buf, "\xaa\xaa\xfe\xdc\xbaX", 6));
- cl_assert(git_encode_varint(buf, 1, 1489279344088ULL) == -1);
+ cl_assert(git_encode_varint(buf, 1, UINT64_C(1489279344088)) == -1);
}
--- /dev/null
+#include "clar_libgit2.h"
+
+void test_core_integer__multiply_int64_no_overflow(void)
+{
+#if !defined(git__multiply_int64_overflow)
+ int64_t result = 0;
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x8000000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x1));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x1));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x1));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x1));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0x4));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x4));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x4));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0x4));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x4000000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x8000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x4000000000000000), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x8000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+#endif
+}
+
+void test_core_integer__multiply_int64_overflow(void)
+{
+#if !defined(git__multiply_int64_overflow)
+ int64_t result = 0;
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x4000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x4000000000000000), INT64_C(0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x8000000000000000)));
+#endif
+}
+
+void test_core_integer__multiply_int64_edge_cases(void)
+{
+#if !defined(git__multiply_int64_overflow)
+ int64_t result = 0;
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x8000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x8000000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x8000000000000000));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x1)));
+#endif
+}
reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen);
cl_assert(reparse_buf);
- subst_utf16 = reparse_buf->MountPointReparseBuffer.PathBuffer;
+ subst_utf16 = reparse_buf->ReparseBuffer.MountPoint.PathBuffer;
print_utf16 = subst_utf16 + subst_utf16_len + 1;
ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1,
cl_assert_equal_i(print_utf16_len, ret);
reparse_buf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
- reparse_buf->MountPointReparseBuffer.SubstituteNameOffset = 0;
- reparse_buf->MountPointReparseBuffer.SubstituteNameLength = subst_byte_len;
- reparse_buf->MountPointReparseBuffer.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR));
- reparse_buf->MountPointReparseBuffer.PrintNameLength = print_byte_len;
+ reparse_buf->ReparseBuffer.MountPoint.SubstituteNameOffset = 0;
+ reparse_buf->ReparseBuffer.MountPoint.SubstituteNameLength = subst_byte_len;
+ reparse_buf->ReparseBuffer.MountPoint.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR));
+ reparse_buf->ReparseBuffer.MountPoint.PrintNameLength = print_byte_len;
reparse_buf->ReparseDataLength = reparse_buflen - REPARSE_DATA_HEADER_SIZE;
cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT,
#include "clar_libgit2.h"
#include "cache.h"
+void test_core_opts__cleanup(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, NULL, 0));
+}
+
void test_core_opts__readwrite(void)
{
size_t old_val = 0;
cl_git_fail(git_libgit2_opts(-1, "foobar"));
}
+void test_core_opts__extensions_query(void)
+{
+ git_strarray out = { 0 };
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
+
+ cl_assert_equal_sz(out.count, 1);
+ cl_assert_equal_s("noop", out.strings[0]);
+
+ git_strarray_dispose(&out);
+}
+
+void test_core_opts__extensions_add(void)
+{
+ const char *in[] = { "foo" };
+ git_strarray out = { 0 };
+
+ 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, 2);
+ cl_assert_equal_s("noop", out.strings[0]);
+ cl_assert_equal_s("foo", out.strings[1]);
+
+ git_strarray_dispose(&out);
+}
+
+void test_core_opts__extensions_remove(void)
+{
+ const char *in[] = { "bar", "!negate", "!noop", "baz" };
+ git_strarray out = { 0 };
+
+ 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, 2);
+ cl_assert_equal_s("bar", out.strings[0]);
+ cl_assert_equal_s("baz", out.strings[1]);
+
+ git_strarray_dispose(&out);
+}
git__free(base2);
}
-static void
-check_topdir(const char *A, const char *B)
-{
- const char *dir;
-
- cl_assert((dir = git_path_topdir(A)) != NULL);
- cl_assert_equal_s(B, dir);
-}
-
static void
check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
{
check_basename(REP1024("/abc"), "abc");
}
-/* get the latest component in a path */
-void test_core_path__02_topdir(void)
-{
- check_topdir(".git/", ".git/");
- check_topdir("/.git/", ".git/");
- check_topdir("usr/local/.git/", ".git/");
- check_topdir("./.git/", ".git/");
- check_topdir("/usr/.git/", ".git/");
- check_topdir("/", "/");
- check_topdir("a/", "a/");
-
- cl_assert(git_path_topdir("/usr/.git") == NULL);
- cl_assert(git_path_topdir(".") == NULL);
- cl_assert(git_path_topdir("") == NULL);
- cl_assert(git_path_topdir("a") == NULL);
-}
-
/* properly join path components */
void test_core_path__05_joins(void)
{
cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
cl_assert(asize < path.asize);
-
+
git_buf_dispose(&path);
}
git_oid oid, expected;
#ifdef GIT_SHA1_COLLISIONDETECT
- GIT_UNUSED(expected);
+ GIT_UNUSED(&expected);
cl_git_fail(sha1_file(&oid, FIXTURE_DIR "/shattered-1.pdf"));
cl_assert_equal_s("SHA1 collision attack detected", git_error_last()->message);
#else
cl_assert_equal_i(
GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "abc"));
- git_sortedcache_clear(sc, true);
+ cl_git_pass(git_sortedcache_clear(sc, true));
cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
cl_assert(git_sortedcache_entry(sc, 0) == NULL);
cl_assert_equal_i(0, free_count);
- git_sortedcache_clear(sc, true);
+ cl_git_pass(git_sortedcache_clear(sc, true));
cl_assert_equal_i(5, free_count);
cl_assert(git_sortedcache_lockandload(sc, &buf) > 0);
- git_sortedcache_clear(sc, false); /* clear once we already have lock */
+ cl_git_pass(git_sortedcache_clear(sc, false)); /* clear once we already have lock */
for (scan = buf.ptr; *scan; scan = after + 1) {
int val = strtol(scan, &after, 0);
cl_assert(git__strcasecmp("et", "e\342\202\254ghi=") < 0);
cl_assert(git__strcasecmp("\303\215", "\303\255") < 0);
}
+
+void test_core_string__strlcmp(void)
+{
+ const char foo[3] = { 'f', 'o', 'o' };
+
+ cl_assert(git__strlcmp("foo", "foo", 3) == 0);
+ cl_assert(git__strlcmp("foo", foo, 3) == 0);
+ cl_assert(git__strlcmp("foo", "foobar", 3) == 0);
+ cl_assert(git__strlcmp("foobar", "foo", 3) > 0);
+ cl_assert(git__strlcmp("foo", "foobar", 6) < 0);
+}
{
assert_l32_parses("123", 123, 10);
assert_l32_parses(" +123 ", 123, 10);
+ assert_l32_parses(" -123 ", -123, 10);
assert_l32_parses(" +2147483647 ", 2147483647, 10);
- assert_l32_parses(" -2147483648 ", -2147483648LL, 10);
+ assert_l32_parses(" -2147483648 ", INT64_C(-2147483648), 10);
assert_l32_parses("A", 10, 16);
assert_l32_parses("1x1", 1, 10);
{
assert_l64_parses("123", 123, 10);
assert_l64_parses(" +123 ", 123, 10);
+ assert_l64_parses(" -123 ", -123, 10);
assert_l64_parses(" +2147483647 ", 2147483647, 10);
- assert_l64_parses(" -2147483648 ", -2147483648LL, 10);
- assert_l64_parses(" 2147483657 ", 2147483657LL, 10);
- assert_l64_parses(" -2147483657 ", -2147483657LL, 10);
+ assert_l64_parses(" -2147483648 ", INT64_C(-2147483648), 10);
+ assert_l64_parses(" 2147483657 ", INT64_C(2147483657), 10);
+ assert_l64_parses(" -2147483657 ", INT64_C(-2147483657), 10);
assert_l64_parses(" 9223372036854775807 ", INT64_MAX, 10);
assert_l64_parses(" -9223372036854775808 ", INT64_MIN, 10);
assert_l64_parses(" 0x7fffffffffffffff ", INT64_MAX, 16);
#include "clar_libgit2.h"
+#include <git2/sys/commit_graph.h>
#include <git2/sys/config.h>
#include <git2/sys/filter.h>
#include <git2/sys/odb_backend.h>
git_blame_options, GIT_BLAME_OPTIONS_VERSION, \
GIT_BLAME_OPTIONS_INIT, git_blame_options_init);
+ /* blob_filter_options */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_blob_filter_options, GIT_BLOB_FILTER_OPTIONS_VERSION, \
+ GIT_BLOB_FILTER_OPTIONS_INIT, git_blob_filter_options_init);
+
/* checkout */
CHECK_MACRO_FUNC_INIT_EQUAL( \
git_checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, \
git_clone_options, GIT_CLONE_OPTIONS_VERSION, \
GIT_CLONE_OPTIONS_INIT, git_clone_options_init);
+ /* commit_graph_writer */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_commit_graph_writer_options, \
+ GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION, \
+ GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT, \
+ git_commit_graph_writer_options_init);
+
/* diff */
CHECK_MACRO_FUNC_INIT_EQUAL( \
git_diff_options, GIT_DIFF_OPTIONS_VERSION, \
#include "clar_libgit2.h"
-#include "global.h"
+#include "settings.h"
void test_core_useragent__get(void)
{
--- /dev/null
+#include "clar_libgit2.h"
+
+void test_core_utf8__char_length(void)
+{
+ cl_assert_equal_i(0, git_utf8_char_length("", 0));
+ cl_assert_equal_i(1, git_utf8_char_length("$", 1));
+ cl_assert_equal_i(5, git_utf8_char_length("abcde", 5));
+ cl_assert_equal_i(1, git_utf8_char_length("\xc2\xa2", 2));
+ cl_assert_equal_i(2, git_utf8_char_length("\x24\xc2\xa2", 3));
+ cl_assert_equal_i(1, git_utf8_char_length("\xf0\x90\x8d\x88", 4));
+
+ /* uncontinued character counted as single characters */
+ cl_assert_equal_i(2, git_utf8_char_length("\x24\xc2", 2));
+ cl_assert_equal_i(3, git_utf8_char_length("\x24\xc2\xc2\xa2", 4));
+
+ /* invalid characters are counted as single characters */
+ cl_assert_equal_i(4, git_utf8_char_length("\x24\xc0\xc0\x34", 4));
+ cl_assert_equal_i(4, git_utf8_char_length("\x24\xf5\xfd\xc2", 4));
+}
{
git_vector x;
int i;
- git_vector_init(&x, 1, NULL);
+ cl_git_pass(git_vector_init(&x, 1, NULL));
for (i = 0; i < 10; ++i) {
git_vector_insert(&x, (void*) 0xabc);
}
{
git_vector x;
/* make initial capacity exact for our insertions. */
- git_vector_init(&x, 3, NULL);
+ cl_git_pass(git_vector_init(&x, 3, NULL));
git_vector_insert(&x, (void*) 0xabc);
git_vector_insert(&x, (void*) 0xdef);
git_vector_insert(&x, (void*) 0x123);
{
git_vector x;
intptr_t i;
- git_vector_init(&x, 1, &compare_them);
+ cl_git_pass(git_vector_init(&x, 1, &compare_them));
for (i = 0; i < 10; i += 2) {
git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
{
git_vector x;
intptr_t i;
- git_vector_init(&x, 1, &compare_them);
+ cl_git_pass(git_vector_init(&x, 1, &compare_them));
for (i = 0; i < 10; i += 2) {
git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
git_vector x;
int i;
- git_vector_init(&x, 1, &compare_structs);
+ cl_git_pass(git_vector_init(&x, 1, &compare_structs));
for (i = 0; i < 10; i += 2)
git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
size_t i;
void *compare;
- git_vector_init(&x, 1, NULL);
+ cl_git_pass(git_vector_init(&x, 1, NULL));
git_vector_insert(&x, (void*) 0x001);
cl_assert(x.length == 1);
cl_git_sandbox_cleanup();
}
+#ifndef GIT_DEPRECATE_HARD
static void assert_email_match(
const char *expected,
const char *oidstr,
git_commit_free(commit);
git_buf_dispose(&buf);
}
+#endif
void test_diff_format_email__simple(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
assert_email_match(
email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+#endif
}
void test_diff_format_email__with_message(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \
"From: Patrick Steinhardt <ps@pks.im>\n" \
assert_email_match(
email, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270", &opts);
+#endif
}
void test_diff_format_email__multiple(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_oid oid;
git_commit *commit = NULL;
git_diff *diff = NULL;
git_diff_free(diff);
git_commit_free(commit);
git_buf_dispose(&buf);
+#endif
}
void test_diff_format_email__exclude_marker(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
assert_email_match(
email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+#endif
}
void test_diff_format_email__invalid_no(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_oid oid;
git_commit *commit = NULL;
git_diff *diff = NULL;
cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
cl_git_fail(git_diff_format_email(&buf, diff, &opts));
cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 2, 1, 0, NULL));
- cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 0, 0, 0, NULL));
git_diff_free(diff);
git_commit_free(commit);
git_buf_dispose(&buf);
+#endif
}
void test_diff_format_email__mode_change(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \
assert_email_match(
email, "7ade76dd34bba4733cf9878079f9fd4a456a9189", &opts);
+#endif
}
void test_diff_format_email__rename_add_remove(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
assert_email_match(
email, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts);
+#endif
}
void test_diff_format_email__multiline_summary(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
assert_email_match(
email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+#endif
}
void test_diff_format_email__binary(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
const char *email =
"From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \
assert_email_match(
email, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts);
+#endif
}
#include "diff_helpers.h"
#include "diff.h"
#include "repository.h"
-#include "buf_text.h"
static git_repository *g_repo = NULL;
#include "clar_libgit2.h"
#include "diff_helpers.h"
-#include "buf_text.h"
static git_repository *g_repo = NULL;
#define RENAME_MODIFICATION_COMMIT "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"
#define REWRITE_DELETE_COMMIT "84d8efa38af7ace2b302de0adbda16b1f1cd2e1b"
#define DELETE_RENAME_COMMIT "be053a189b0bbde545e0a3f59ce00b46ad29ce0d"
+#define BREAK_REWRITE_BASE_COMMIT "db98035f715427eef1f5e17f03e1801c05301e9e"
+#define BREAK_REWRITE_COMMIT "7e7bfb88ba9bc65fd700fee1819cf1c317aafa56"
/*
* Renames repo has:
cl_git_pass(
git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
cl_git_pass(
- git_buf_text_lf_to_crlf(&content, &old_content));
+ git_buf_lf_to_crlf(&content, &old_content));
cl_git_pass(
git_futils_writebuffer(&content, "renames/songof7cities.txt", 0, 0));
git_tree_free(old_tree);
git_tree_free(new_tree);
}
+
+/*
+ * The break_rewrite branch contains a testcase reduced from
+ * a real-world scenario, rather than being "constructed" like
+ * the above tests seem to be. There are two commits layered
+ * on top of the repo's initial commit; the base commit which
+ * clears out the files from the initial commit and installs
+ * four files. And then there's the modification commit which
+ * mutates the files in such a way as to trigger the bug in
+ * libgit2.
+ * commit db98035f715427eef1f5e17f03e1801c05301e9e
+ * serving.txt (deleted)
+ * sevencities.txt (deleted)
+ * AAA (313 lines)
+ * BBB (314 lines)
+ * CCC (704 lines)
+ * DDD (314 lines, identical to BBB)
+ * commit 7e7bfb88ba9bc65fd700fee1819cf1c317aafa56
+ * This deletes CCC and makes slight modifications
+ * to AAA, BBB, and DDD. The find_best_matches loop
+ * for git_diff_find_similar computes the following:
+ * CCC moved to AAA (similarity 91)
+ * CCC copied to AAA (similarity 91)
+ * DDD moved to BBB (similarity 52)
+ * CCC copied to BBB (similarity 90)
+ * BBB moved to DDD (similarity 52)
+ * CCC copied to DDD (similarity 90)
+ * The code to rewrite the diffs by resolving these
+ * copies/renames would resolve the BBB <-> DDD moves
+ * but then still leave BBB as a rename target for
+ * the deleted file CCC. Since the split flag on BBB
+ * was cleared, this would trigger an error.
+ */
+void test_diff_rename__break_rewrite(void)
+{
+ const char *old_sha = BREAK_REWRITE_BASE_COMMIT;
+ const char *new_sha = BREAK_REWRITE_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ find_opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES | GIT_DIFF_BREAK_REWRITES | GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, old_tree, new_tree, NULL));
+ cl_git_pass(git_diff_find_similar(diff, &find_opts));
+
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
git_diff_free(diff);
git_tree_free(tree);
}
+
+void test_diff_workdir__ignore_blank_lines(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+
+ g_repo = cl_git_sandbox_init("rebase");
+ cl_git_rewritefile("rebase/gravy.txt", "GRAVY SOUP.\n\n\nGet eight pounds of coarse lean beef--wash it clean and lay it in your\n\npot, put in the same ingredients as for the shin soup, with the same\nquantity of water, and follow the process directed for that. Strain the\nsoup through a sieve, and serve it up clear, with nothing more than\ntoasted bread in it; two table-spoonsful of mushroom catsup will add a\nfine flavour to the soup!\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/gravy.txt b/gravy.txt\nindex c4e6cca..3c617e6 100644\n--- a/gravy.txt\n+++ b/gravy.txt\n@@ -1,8 +1,10 @@\n GRAVY SOUP.\n \n+\n Get eight pounds of coarse lean beef--wash it clean and lay it in your\n+\n pot, put in the same ingredients as for the shin soup, with the same\n quantity of water, and follow the process directed for that. Strain the\n soup through a sieve, and serve it up clear, with nothing more than\n toasted bread in it; two table-spoonsful of mushroom catsup will add a\n-fine flavour to the soup.\n+fine flavour to the soup!\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("diff --git a/gravy.txt b/gravy.txt\nindex c4e6cca..3c617e6 100644\n--- a/gravy.txt\n+++ b/gravy.txt\n@@ -5,4 +7,4 @@ pot, put in the same ingredients as for the shin soup, with the same\n quantity of water, and follow the process directed for that. Strain the\n soup through a sieve, and serve it up clear, with nothing more than\n toasted bread in it; two table-spoonsful of mushroom catsup will add a\n-fine flavour to the soup.\n+fine flavour to the soup!\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
--- /dev/null
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "buffer.h"
+#include "diff_generate.h"
+
+static git_repository *repo;
+
+void test_email_create__initialize(void)
+{
+ repo = cl_git_sandbox_init("diff_format_email");
+}
+
+void test_email_create__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void email_for_commit(
+ git_buf *out,
+ const char *commit_id,
+ git_email_create_options *opts)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+
+ git_oid_fromstr(&oid, commit_id);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ cl_git_pass(git_email_create_from_commit(out, commit, opts));
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+}
+
+static void assert_email_match(
+ const char *expected,
+ const char *commit_id,
+ git_email_create_options *opts)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ email_for_commit(&buf, commit_id, opts);
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+
+ git_buf_dispose(&buf);
+}
+
+static void assert_subject_match(
+ const char *expected,
+ const char *commit_id,
+ git_email_create_options *opts)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *loc;
+
+ email_for_commit(&buf, commit_id, opts);
+
+ cl_assert((loc = strstr(buf.ptr, "\nSubject: ")) != NULL);
+ git_buf_consume(&buf, (loc + 10));
+ git_buf_truncate_at_char(&buf, '\n');
+
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+
+ git_buf_dispose(&buf);
+}
+
+void test_email_create__commit(void)
+{
+ const char *expected =
+ "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
+ "Subject: [PATCH] Modify some content\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "index 94aaae8..af8f41d 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt\n" \
+ "@@ -1,15 +1,17 @@\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+\n" \
+ "+\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(
+ expected, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", NULL);
+}
+
+void test_email_create__rename(void)
+{
+ const char *expected =
+ "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
+ "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt => file1.txt.renamed | 4 ++--\n" \
+ " 1 file changed, 2 insertions(+), 2 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt.renamed\n" \
+ "similarity index 86%\n" \
+ "rename from file1.txt\n" \
+ "rename to file1.txt.renamed\n" \
+ "index af8f41d..a97157a 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt.renamed\n" \
+ "@@ -3,13 +3,13 @@ file1.txt\n" \
+ " _file1.txt_\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ " file1.txt\n" \
+ " \n" \
+ " \n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " _file1.txt_\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(expected, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", NULL);
+}
+
+void test_email_create__rename_as_add_delete(void)
+{
+ const char *expected =
+ "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
+ "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 17 -----------------\n" \
+ " file1.txt.renamed | 17 +++++++++++++++++\n" \
+ " 2 files changed, 17 insertions(+), 17 deletions(-)\n" \
+ " delete mode 100644 file1.txt\n" \
+ " create mode 100644 file1.txt.renamed\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "deleted file mode 100644\n" \
+ "index af8f41d..0000000\n" \
+ "--- a/file1.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,17 +0,0 @@\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-_file1.txt_\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-\n" \
+ "-\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-_file1.txt_\n" \
+ "-_file1.txt_\n" \
+ "-file1.txt\n" \
+ "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
+ "new file mode 100644\n" \
+ "index 0000000..a97157a\n" \
+ "--- /dev/null\n" \
+ "+++ b/file1.txt.renamed\n" \
+ "@@ -0,0 +1,17 @@\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ "+file1.txt\n" \
+ "+\n" \
+ "+\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ "+file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ opts.flags |= GIT_EMAIL_CREATE_NO_RENAMES;
+
+ assert_email_match(expected, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts);
+}
+
+void test_email_create__binary(void)
+{
+ const char *expected =
+ "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \
+ "Subject: [PATCH] Modified binary file\n" \
+ "\n" \
+ "---\n" \
+ " binary.bin | Bin 3 -> 5 bytes\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ "\n" \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2519cc15eab801ff851cc7d50f0dee49a1..9ac35ff15cd8864aeafd889e4826a3150f0b06c4 100644\n" \
+ "GIT binary patch\n" \
+ "literal 5\n" \
+ "Mc${NkU}WL~000&M4gdfE\n" \
+ "\n" \
+ "literal 3\n" \
+ "Kc${Nk-~s>u4FC%O\n" \
+ "\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(expected, "8d7523f6fcb2404257889abe0d96f093d9f524f9", NULL);
+}
+
+void test_email_create__binary_not_included(void)
+{
+ const char *expected =
+ "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \
+ "Subject: [PATCH] Modified binary file\n" \
+ "\n" \
+ "---\n" \
+ " binary.bin | Bin 3 -> 5 bytes\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ "\n" \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2..9ac35ff 100644\n" \
+ "Binary files a/binary.bin and b/binary.bin differ\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ opts.diff_opts.flags &= ~GIT_DIFF_SHOW_BINARY;
+
+ assert_email_match(expected, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts);
+}
+
+void test_email_create__custom_summary_and_body(void)
+{
+ const char *expected = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \
+ "From: Patrick Steinhardt <ps@pks.im>\n" \
+ "Date: Tue, 24 Nov 2015 13:34:39 +0100\n" \
+ "Subject: [PPPPPATCH 2/4] This is a subject\n" \
+ "\n" \
+ "Modify content of file3.txt by appending a new line. Make this\n" \
+ "commit message somewhat longer to test behavior with newlines\n" \
+ "embedded in the message body.\n" \
+ "\n" \
+ "Also test if new paragraphs are included correctly.\n" \
+ "---\n" \
+ " file3.txt | 1 +\n" \
+ " 1 file changed, 1 insertion(+)\n" \
+ "\n" \
+ "diff --git a/file3.txt b/file3.txt\n" \
+ "index 9a2d780..7309653 100644\n" \
+ "--- a/file3.txt\n" \
+ "+++ b/file3.txt\n" \
+ "@@ -3,3 +3,4 @@ file3!\n" \
+ " file3\n" \
+ " file3\n" \
+ " file3\n" \
+ "+file3\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ const char *summary = "This is a subject\nwith\nnewlines";
+ const char *body = "Modify content of file3.txt by appending a new line. Make this\n" \
+ "commit message somewhat longer to test behavior with newlines\n" \
+ "embedded in the message body.\n" \
+ "\n" \
+ "Also test if new paragraphs are included correctly.";
+
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_buf buf = GIT_BUF_INIT;
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+
+ opts.subject_prefix = "PPPPPATCH";
+
+ git_oid_fromstr(&oid, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270");
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_email_create_from_diff(&buf, diff, 2, 4, &oid, summary, body, git_commit_author(commit), &opts));
+
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_dispose(&buf);
+}
+
+void test_email_create__commit_subjects(void)
+{
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+
+ assert_subject_match("[PATCH] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.reroll_number = 42;
+ assert_subject_match("[PATCH v42] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.flags |= GIT_EMAIL_CREATE_ALWAYS_NUMBER;
+ assert_subject_match("[PATCH v42 1/1] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.start_number = 9;
+ assert_subject_match("[PATCH v42 9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.subject_prefix = "";
+ assert_subject_match("[v42 9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.reroll_number = 0;
+ assert_subject_match("[9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.start_number = 0;
+ assert_subject_match("[1/1] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.flags = GIT_EMAIL_CREATE_OMIT_NUMBERS;
+ assert_subject_match("Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+}
int equals = 0;
size_t i;
- git_vector_init(&fetchhead_vector, 6, NULL);
+ cl_git_pass(git_vector_init(&fetchhead_vector, 6, NULL));
cl_set_cleanup(&cleanup_repository, "./test1");
cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
return 0;
}
+static int assert_none_for_merge(const char *ref, const char *url, const git_oid *id, unsigned int is_merge, void *data)
+{
+ GIT_UNUSED(ref);
+ GIT_UNUSED(url);
+ GIT_UNUSED(id);
+ GIT_UNUSED(data);
+
+ return is_merge ? -1 : 0;
+}
+
void test_fetchhead_nonetwork__unborn_with_upstream(void)
{
git_repository *repo;
cl_git_sandbox_cleanup();
}
+void test_fetchhead_nonetwork__fetch_into_repo_with_invalid_head(void)
+{
+ git_remote *remote;
+ char *strings[] = { "refs/heads/*:refs/remotes/origin/*" };
+ git_strarray refspecs = { strings, 1 };
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ /* HEAD pointing to nonexistent branch */
+ cl_git_rewritefile("./test1/.git/HEAD", "ref: refs/heads/\n");
+
+ cl_git_pass(git_remote_create_anonymous(&remote, g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, assert_none_for_merge, NULL));
+
+ git_remote_free(remote);
+}
+
void test_fetchhead_nonetwork__quote_in_branch_name(void)
{
cl_set_cleanup(&cleanup_repository, "./test1");
cl_git_pass(git_repository_open(&g_repo, "crlf.git"));
filter_opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
- filter_opts.flags |= GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD;
+ filter_opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD;
}
void test_filter_bare__cleanup(void)
git_blob_free(blob);
}
+void test_filter_bare__from_specific_commit_one(void)
+{
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
+ opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT;
+ cl_git_pass(git_oid_fromstr(&opts.attr_commit_id, "b8986fec0f7bde90f78ac72706e782d82f24f2f0"));
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "055c872")); /* ident */
+
+ cl_assert_equal_s("$Id$\n", git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "ident.bin", &opts));
+ cl_assert_equal_s("$Id$\n", buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "ident.identlf", &opts));
+ cl_assert_equal_s("$Id: 055c8729cdcc372500a08db659c045e16c4409fb $\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_bare__from_specific_commit_with_no_attributes_file(void)
+{
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
+ opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT;
+ cl_git_pass(git_oid_fromstr(&opts.attr_commit_id, "5afb6a14a864e30787857dd92af837e8cdd2cb1b"));
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "799770d")); /* all-lf */
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
{
git_filter_list *fl;
git_filter *crlf;
- git_buf in = { 0 }, out = { 0 };
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_WORKTREE, 0));
cl_git_pass(git_filter_list_push(fl, crlf, NULL));
- in.ptr = "Some text\nRight here\n";
- in.size = strlen(in.ptr);
+ in = "Some text\nRight here\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Some text\r\nRight here\r\n", out.ptr);
{
git_filter_list *fl;
git_filter *crlf;
- git_buf in = { 0 }, out = { 0 };
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_ODB, 0));
cl_git_pass(git_filter_list_push(fl, crlf, NULL));
- in.ptr = "Some text\r\nRight here\r\n";
- in.size = strlen(in.ptr);
+ in = "Some text\r\nRight here\r\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Some text\nRight here\n", out.ptr);
{
git_filter_list *fl;
git_filter *crlf;
- git_buf in = {0}, out = GIT_BUF_INIT;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
cl_repo_set_bool(g_repo, "core.safecrlf", true);
cl_git_pass(git_filter_list_push(fl, crlf, NULL));
/* Normalized \r\n succeeds with safecrlf */
- in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n";
- in.size = strlen(in.ptr);
+ in = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
/* Mix of line endings fails with safecrlf */
- in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
- in.size = strlen(in.ptr);
+ in = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in_len = strlen(in);
- cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_fail(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_i(git_error_last()->klass, GIT_ERROR_FILTER);
/* Normalized \n fails for autocrlf=true when safecrlf=true */
- in.ptr = "Normal\nLF\nonly\nline-endings.\n";
- in.size = strlen(in.ptr);
+ in = "Normal\nLF\nonly\nline-endings.\n";
+ in_len = strlen(in);
- cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_fail(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_i(git_error_last()->klass, GIT_ERROR_FILTER);
/* String with \r but without \r\n does not fail with safecrlf */
- in.ptr = "Normal\nCR only\rand some more\nline-endings.\n";
- in.size = strlen(in.ptr);
+ in = "Normal\nCR only\rand some more\nline-endings.\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Normal\nCR only\rand some more\nline-endings.\n", out.ptr);
git_filter_list_free(fl);
{
git_filter_list *fl;
git_filter *crlf;
- git_buf in = {0}, out = GIT_BUF_INIT;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
cl_repo_set_bool(g_repo, "core.safecrlf", true);
cl_git_pass(git_filter_list_push(fl, crlf, NULL));
/* Normalized \r\n succeeds with safecrlf */
- in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n";
- in.size = strlen(in.ptr);
+ in = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
/* Mix of line endings fails with safecrlf, but allowed to pass */
- in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
- in.size = strlen(in.ptr);
+ in = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
/* TODO: check for warning */
cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
/* Normalized \n fails with safecrlf, but allowed to pass */
- in.ptr = "Normal\nLF\nonly\nline-endings.\n";
- in.size = strlen(in.ptr);
+ in = "Normal\nLF\nonly\nline-endings.\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
/* TODO: check for warning */
cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr);
{
git_filter_list *fl;
git_filter *crlf;
- git_buf in = {0}, out = GIT_BUF_INIT;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_ODB, 0));
cl_git_pass(git_filter_list_push(fl, crlf, NULL));
/* Normalized \r\n succeeds with safecrlf */
- in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n";
- in.size = strlen(in.ptr);
+ in = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
/* Mix of line endings fails with safecrlf */
- in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
- in.size = strlen(in.ptr);
+ in = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
/* Normalized \n fails with safecrlf */
- in.ptr = "Normal\nLF\nonly\nline-endings.\n";
- in.size = strlen(in.ptr);
+ in = "Normal\nLF\nonly\nline-endings.\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr);
git_filter_list_free(fl);
{
git_filter_list *fl;
git_filter *crlf;
- git_buf in = {0}, out = GIT_BUF_INIT;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
cl_repo_set_string(g_repo, "core.safecrlf", "warn");
cl_git_pass(git_filter_list_push(fl, crlf, NULL));
/* Normalized \r\n succeeds with safecrlf=warn */
- in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n";
- in.size = strlen(in.ptr);
+ in = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
/* Mix of line endings succeeds with safecrlf=warn */
- in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
- in.size = strlen(in.ptr);
+ in = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
/* TODO: check for warning */
cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
/* Normalized \n is reversible, so does not fail with safecrlf=warn */
- in.ptr = "Normal\nLF\nonly\nline-endings.\n";
- in.size = strlen(in.ptr);
+ in = "Normal\nLF\nonly\nline-endings.\n";
+ in_len = strlen(in);
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
- cl_assert_equal_s(in.ptr, out.ptr);
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s(in, out.ptr);
git_filter_list_free(fl);
git_buf_dispose(&out);
#include "posix.h"
#include "blob.h"
#include "filter.h"
-#include "buf_text.h"
#include "git2/sys/filter.h"
#include "git2/sys/repository.h"
#include "custom_helpers.h"
void test_filter_custom__to_odb(void)
{
git_filter_list *fl;
- git_buf out = { 0 };
- git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data));
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB, 0));
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ in = workdir_data;
+ in_len = strlen(workdir_data);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_i(BITFLIPPED_AND_REVERSED_DATA_LEN, out.size);
void test_filter_custom__to_workdir(void)
{
git_filter_list *fl;
- git_buf out = { 0 };
- git_buf in = GIT_BUF_INIT_CONST(
- bitflipped_and_reversed_data, BITFLIPPED_AND_REVERSED_DATA_LEN);
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0));
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ in = (char *)bitflipped_and_reversed_data;
+ in_len = BITFLIPPED_AND_REVERSED_DATA_LEN;
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
cl_assert_equal_i(strlen(workdir_data), out.size);
{
git_filter_list *filters;
git_buf out = GIT_BUF_INIT;
- git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data));
+ const char *in;
+ size_t in_len;
cl_git_pass(git_filter_list_load(
&filters, g_repo, NULL, "villain", GIT_FILTER_TO_WORKTREE, 0));
- cl_git_fail(git_filter_list_apply_to_data(&out, filters, &in));
+ in = workdir_data;
+ in_len = strlen(workdir_data);
+
+ cl_git_fail(git_filter_list_apply_to_buffer(&out, filters, in, in_len));
git_filter_list_free(filters);
git_buf_dispose(&out);
#include "clar_libgit2.h"
#include "posix.h"
#include "filter.h"
-#include "buf_text.h"
#include "git2/sys/filter.h"
#define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff)
return 0;
}
+static int bitflip_filter_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, bitflip_filter_apply, NULL, payload, src, next);
+}
+
static void bitflip_filter_free(git_filter *f)
{
git__free(f);
filter->version = GIT_FILTER_VERSION;
filter->attributes = "+bitflip";
filter->shutdown = bitflip_filter_free;
- filter->apply = bitflip_filter_apply;
+ filter->stream = bitflip_filter_stream;
return filter;
}
return 0;
}
+static int reverse_filter_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, reverse_filter_apply, NULL, payload, src, next);
+}
+
static void reverse_filter_free(git_filter *f)
{
git__free(f);
filter->version = GIT_FILTER_VERSION;
filter->attributes = attrs;
filter->shutdown = reverse_filter_free;
- filter->apply = reverse_filter_apply;
+ filter->stream = reverse_filter_stream;
return filter;
}
#include "posix.h"
#include "blob.h"
#include "filter.h"
-#include "buf_text.h"
#include "git2/sys/filter.h"
#include "git2/sys/repository.h"
#include "posix.h"
#include "blob.h"
#include "filter.h"
-#include "buf_text.h"
#include "git2/sys/filter.h"
#include "git2/sys/repository.h"
#include "custom_helpers.h"
return GIT_PASSTHROUGH;
}
+static int wildcard_filter_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, wildcard_filter_apply, NULL, payload, src, next);
+}
+
static void wildcard_filter_cleanup(git_filter *self, void *payload)
{
GIT_UNUSED(self);
filter->version = GIT_FILTER_VERSION;
filter->attributes = "filter=*";
filter->check = wildcard_filter_check;
- filter->apply = wildcard_filter_apply;
+ filter->stream = wildcard_filter_stream;
filter->cleanup = wildcard_filter_cleanup;
filter->shutdown = wildcard_filter_free;
void test_filter_wildcard__reverse(void)
{
git_filter_list *fl;
- git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT;
+ git_buf out = GIT_BUF_INIT;
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "hero-reverse-foo", GIT_FILTER_TO_ODB, 0));
- cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN));
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, (char *)input, DATA_LEN));
cl_assert_equal_i(DATA_LEN, out.size);
git_filter_list_free(fl);
git_buf_dispose(&out);
- git_buf_dispose(&in);
}
void test_filter_wildcard__flip(void)
{
git_filter_list *fl;
- git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT;
+ git_buf out = GIT_BUF_INIT;
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "hero-flip-foo", GIT_FILTER_TO_ODB, 0));
- cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN));
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, (char *)input, DATA_LEN));
cl_assert_equal_i(DATA_LEN, out.size);
git_filter_list_free(fl);
git_buf_dispose(&out);
- git_buf_dispose(&in);
}
void test_filter_wildcard__none(void)
{
git_filter_list *fl;
- git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT;
+ git_buf out = GIT_BUF_INIT;
cl_git_pass(git_filter_list_load(
&fl, g_repo, NULL, "none-foo", GIT_FILTER_TO_ODB, 0));
- cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN));
- cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, (char *)input, DATA_LEN));
cl_assert_equal_i(DATA_LEN, out.size);
git_filter_list_free(fl);
git_buf_dispose(&out);
- git_buf_dispose(&in);
}
--- /dev/null
+#include "clar_libgit2.h"
+
+#include <git2.h>
+#include <git2/sys/commit_graph.h>
+
+#include "commit_graph.h"
+#include "futils.h"
+
+void test_graph_commit_graph__parse(void)
+{
+ git_repository *repo;
+ struct git_commit_graph_file *file;
+ struct git_commit_graph_entry e, parent;
+ git_oid id;
+ git_buf commit_graph_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_buf_joinpath(&commit_graph_path, git_repository_path(repo), "objects/info/commit-graph"));
+ cl_git_pass(git_commit_graph_file_open(&file, git_buf_cstr(&commit_graph_path)));
+ cl_assert_equal_i(git_commit_graph_file_needs_refresh(file, git_buf_cstr(&commit_graph_path)), 0);
+
+ cl_git_pass(git_oid_fromstr(&id, "5001298e0c09ad9c34e4249bc5801c75e9754fa5"));
+ cl_git_pass(git_commit_graph_entry_find(&e, file, &id, GIT_OID_HEXSZ));
+ cl_assert_equal_oid(&e.sha1, &id);
+ cl_git_pass(git_oid_fromstr(&id, "418382dff1ffb8bdfba833f4d8bbcde58b1e7f47"));
+ cl_assert_equal_oid(&e.tree_oid, &id);
+ cl_assert_equal_i(e.generation, 1);
+ cl_assert_equal_i(e.commit_time, UINT64_C(1273610423));
+ cl_assert_equal_i(e.parent_count, 0);
+
+ cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_commit_graph_entry_find(&e, file, &id, GIT_OID_HEXSZ));
+ cl_assert_equal_oid(&e.sha1, &id);
+ cl_assert_equal_i(e.generation, 5);
+ cl_assert_equal_i(e.commit_time, UINT64_C(1274813907));
+ cl_assert_equal_i(e.parent_count, 2);
+
+ cl_git_pass(git_oid_fromstr(&id, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 0));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 4);
+
+ cl_git_pass(git_oid_fromstr(&id, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 1));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 3);
+
+ git_commit_graph_file_free(file);
+ git_repository_free(repo);
+ git_buf_dispose(&commit_graph_path);
+}
+
+void test_graph_commit_graph__parse_octopus_merge(void)
+{
+ git_repository *repo;
+ struct git_commit_graph_file *file;
+ struct git_commit_graph_entry e, parent;
+ git_oid id;
+ git_buf commit_graph_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("merge-recursive/.gitted")));
+ cl_git_pass(git_buf_joinpath(&commit_graph_path, git_repository_path(repo), "objects/info/commit-graph"));
+ cl_git_pass(git_commit_graph_file_open(&file, git_buf_cstr(&commit_graph_path)));
+
+ cl_git_pass(git_oid_fromstr(&id, "d71c24b3b113fd1d1909998c5bfe33b86a65ee03"));
+ cl_git_pass(git_commit_graph_entry_find(&e, file, &id, GIT_OID_HEXSZ));
+ cl_assert_equal_oid(&e.sha1, &id);
+ cl_git_pass(git_oid_fromstr(&id, "348f16ffaeb73f319a75cec5b16a0a47d2d5e27c"));
+ cl_assert_equal_oid(&e.tree_oid, &id);
+ cl_assert_equal_i(e.generation, 7);
+ cl_assert_equal_i(e.commit_time, UINT64_C(1447083009));
+ cl_assert_equal_i(e.parent_count, 3);
+
+ cl_git_pass(git_oid_fromstr(&id, "ad2ace9e15f66b3d1138922e6ffdc3ea3f967fa6"));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 0));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 6);
+
+ cl_git_pass(git_oid_fromstr(&id, "483065df53c0f4a02cdc6b2910b05d388fc17ffb"));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 1));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 2);
+
+ cl_git_pass(git_oid_fromstr(&id, "815b5a1c80ca749d705c7aa0cb294a00cbedd340"));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 2));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 6);
+
+ git_commit_graph_file_free(file);
+ git_repository_free(repo);
+ git_buf_dispose(&commit_graph_path);
+}
+
+void test_graph_commit_graph__writer(void)
+{
+ git_repository *repo;
+ git_commit_graph_writer *w = NULL;
+ git_revwalk *walk;
+ git_commit_graph_writer_options opts = GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT;
+ git_buf cgraph = GIT_BUF_INIT, expected_cgraph = GIT_BUF_INIT, path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/info"));
+ cl_git_pass(git_commit_graph_writer_new(&w, git_buf_cstr(&path)));
+
+ /* This is equivalent to `git commit-graph write --reachable`. */
+ cl_git_pass(git_revwalk_new(&walk, repo));
+ cl_git_pass(git_revwalk_push_glob(walk, "refs/*"));
+ cl_git_pass(git_commit_graph_writer_add_revwalk(w, walk));
+ git_revwalk_free(walk);
+
+ cl_git_pass(git_commit_graph_writer_dump(&cgraph, w, &opts));
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/info/commit-graph"));
+ cl_git_pass(git_futils_readbuffer(&expected_cgraph, git_buf_cstr(&path)));
+
+ cl_assert_equal_i(git_buf_len(&cgraph), git_buf_len(&expected_cgraph));
+ cl_assert_equal_i(memcmp(git_buf_cstr(&cgraph), git_buf_cstr(&expected_cgraph), git_buf_len(&cgraph)), 0);
+
+ git_buf_dispose(&cgraph);
+ git_buf_dispose(&expected_cgraph);
+ git_buf_dispose(&path);
+ git_commit_graph_writer_free(w);
+ git_repository_free(repo);
+}
--- /dev/null
+#include "clar_libgit2.h"
+
+#include <git2.h>
+
+#include "commit_graph.h"
+#include "bitvec.h"
+#include "vector.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_graph_reachable_from_any__initialize(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+
+ git_oid_fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707");
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ git_commit_free(commit);
+}
+
+void test_graph_reachable_from_any__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_graph_reachable_from_any__returns_correct_result(void)
+{
+ git_object *branchA1, *branchA2, *branchB1, *branchB2, *branchC1, *branchC2, *branchH1,
+ *branchH2;
+ git_oid descendants[7];
+
+ cl_git_pass(git_revparse_single(&branchA1, repo, "branchA-1"));
+ cl_git_pass(git_revparse_single(&branchA2, repo, "branchA-2"));
+ cl_git_pass(git_revparse_single(&branchB1, repo, "branchB-1"));
+ cl_git_pass(git_revparse_single(&branchB2, repo, "branchB-2"));
+ cl_git_pass(git_revparse_single(&branchC1, repo, "branchC-1"));
+ cl_git_pass(git_revparse_single(&branchC2, repo, "branchC-2"));
+ cl_git_pass(git_revparse_single(&branchH1, repo, "branchH-1"));
+ cl_git_pass(git_revparse_single(&branchH2, repo, "branchH-2"));
+
+ cl_assert_equal_i(
+ git_graph_reachable_from_any(
+ repo, git_object_id(branchH1), git_object_id(branchA1), 1),
+ 0);
+ cl_assert_equal_i(
+ git_graph_reachable_from_any(
+ repo, git_object_id(branchH1), git_object_id(branchA2), 1),
+ 0);
+
+ cl_git_pass(git_oid_cpy(&descendants[0], git_object_id(branchA1)));
+ cl_git_pass(git_oid_cpy(&descendants[1], git_object_id(branchA2)));
+ cl_git_pass(git_oid_cpy(&descendants[2], git_object_id(branchB1)));
+ cl_git_pass(git_oid_cpy(&descendants[3], git_object_id(branchB2)));
+ cl_git_pass(git_oid_cpy(&descendants[4], git_object_id(branchC1)));
+ cl_git_pass(git_oid_cpy(&descendants[5], git_object_id(branchC2)));
+ cl_git_pass(git_oid_cpy(&descendants[6], git_object_id(branchH2)));
+ cl_assert_equal_i(
+ git_graph_reachable_from_any(repo, git_object_id(branchH2), descendants, 6),
+ 0);
+ cl_assert_equal_i(
+ git_graph_reachable_from_any(repo, git_object_id(branchH2), descendants, 7),
+ 1);
+
+ git_object_free(branchA1);
+ git_object_free(branchA2);
+ git_object_free(branchB1);
+ git_object_free(branchB2);
+ git_object_free(branchC1);
+ git_object_free(branchC2);
+ git_object_free(branchH1);
+ git_object_free(branchH2);
+}
+
+struct exhaustive_state {
+ git_odb *db;
+ git_vector commits;
+};
+
+/** Get all commits from the repository. */
+static int exhaustive_commits(const git_oid *id, void *payload)
+{
+ struct exhaustive_state *mc = (struct exhaustive_state *)payload;
+ size_t header_len;
+ git_object_t header_type;
+ int error = 0;
+
+ error = git_odb_read_header(&header_len, &header_type, mc->db, id);
+ if (error < 0)
+ return error;
+
+ if (header_type == GIT_OBJECT_COMMIT) {
+ git_commit *commit = NULL;
+
+ cl_git_pass(git_commit_lookup(&commit, repo, id));
+ cl_git_pass(git_vector_insert(&mc->commits, commit));
+ }
+
+ return 0;
+}
+
+/** Compare the `git_oid`s of two `git_commit` objects. */
+static int commit_id_cmp(const void *a, const void *b)
+{
+ return git_oid_cmp(
+ git_commit_id((const git_commit *)a), git_commit_id((const git_commit *)b));
+}
+
+/** Find a `git_commit` whose ID matches the provided `git_oid` key. */
+static int id_commit_id_cmp(const void *key, const void *commit)
+{
+ return git_oid_cmp((const git_oid *)key, git_commit_id((const git_commit *)commit));
+}
+
+void test_graph_reachable_from_any__exhaustive(void)
+{
+ struct exhaustive_state mc = {
+ .db = NULL,
+ .commits = GIT_VECTOR_INIT,
+ };
+ size_t child_idx, commit_count;
+ size_t n_descendants;
+ git_commit *child_commit;
+ git_bitvec reachable;
+
+ cl_git_pass(git_repository_odb(&mc.db, repo));
+ cl_git_pass(git_odb_foreach(mc.db, &exhaustive_commits, &mc));
+ git_vector_set_cmp(&mc.commits, commit_id_cmp);
+ git_vector_sort(&mc.commits);
+ cl_git_pass(git_bitvec_init(
+ &reachable,
+ git_vector_length(&mc.commits) * git_vector_length(&mc.commits)));
+
+ commit_count = git_vector_length(&mc.commits);
+ git_vector_foreach (&mc.commits, child_idx, child_commit) {
+ unsigned int parent_i;
+
+ /* We treat each commit as being able to reach itself. */
+ git_bitvec_set(&reachable, child_idx * commit_count + child_idx, true);
+
+ for (parent_i = 0; parent_i < git_commit_parentcount(child_commit); ++parent_i) {
+ size_t parent_idx = -1;
+ cl_git_pass(git_vector_bsearch2(
+ &parent_idx,
+ &mc.commits,
+ id_commit_id_cmp,
+ git_commit_parent_id(child_commit, parent_i)));
+
+ /* We have established that parent_idx is reachable from child_idx */
+ git_bitvec_set(&reachable, parent_idx * commit_count + child_idx, true);
+ }
+ }
+
+ /* Floyd-Warshall */
+ {
+ size_t i, j, k;
+ for (k = 0; k < commit_count; ++k) {
+ for (i = 0; i < commit_count; ++i) {
+ if (!git_bitvec_get(&reachable, i * commit_count + k))
+ continue;
+ for (j = 0; j < commit_count; ++j) {
+ if (!git_bitvec_get(&reachable, k * commit_count + j))
+ continue;
+ git_bitvec_set(&reachable, i * commit_count + j, true);
+ }
+ }
+ }
+ }
+
+ /* Try 1000 subsets of 1 through 10 entries each. */
+ srand(0x223ddc4b);
+ for (n_descendants = 1; n_descendants < 10; ++n_descendants) {
+ size_t test_iteration;
+ git_oid descendants[10];
+
+ for (test_iteration = 0; test_iteration < 1000; ++test_iteration) {
+ size_t descendant_i;
+ size_t child_idx, parent_idx;
+ int expected_reachable = false, actual_reachable;
+ git_commit *child_commit, *parent_commit;
+
+ parent_idx = rand() % commit_count;
+ parent_commit = (git_commit *)git_vector_get(&mc.commits, parent_idx);
+ for (descendant_i = 0; descendant_i < n_descendants; ++descendant_i) {
+ child_idx = rand() % commit_count;
+ child_commit = (git_commit *)git_vector_get(&mc.commits, child_idx);
+ expected_reachable |= git_bitvec_get(
+ &reachable, parent_idx * commit_count + child_idx);
+ git_oid_cpy(&descendants[descendant_i],
+ git_commit_id(child_commit));
+ }
+
+ actual_reachable = git_graph_reachable_from_any(
+ repo,
+ git_commit_id(parent_commit),
+ descendants,
+ n_descendants);
+ if (actual_reachable != expected_reachable) {
+ git_buf error_message_buf = GIT_BUF_INIT;
+ char parent_oidbuf[9] = {0}, child_oidbuf[9] = {0};
+
+ cl_git_pass(git_oid_nfmt(
+ parent_oidbuf, 8, git_commit_id(parent_commit)));
+ git_buf_printf(&error_message_buf,
+ "git_graph_reachable_from_any(\"%s\", %zu, "
+ "{",
+ parent_oidbuf,
+ n_descendants);
+ for (descendant_i = 0; descendant_i < n_descendants;
+ ++descendant_i) {
+ cl_git_pass(
+ git_oid_nfmt(child_oidbuf,
+ 8,
+ &descendants[descendant_i]));
+ git_buf_printf(&error_message_buf, " \"%s\"", child_oidbuf);
+ }
+ git_buf_printf(&error_message_buf,
+ " }) = %d, expected = %d",
+ actual_reachable,
+ expected_reachable);
+ cl_check_(actual_reachable == expected_reachable,
+ git_buf_cstr(&error_message_buf));
+ }
+ }
+ }
+
+ git_vector_foreach (&mc.commits, child_idx, child_commit)
+ git_commit_free(child_commit);
+ git_bitvec_free(&reachable);
+ git_vector_free(&mc.commits);
+ git_odb_free(mc.db);
+}
assert_is_ignored(true, "ff");
assert_is_ignored(false, "f");
}
+
+void test_ignore_path__negative_more_specific(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "*.txt\n!/dir/test.txt\n");
+ assert_is_ignored(true, "test.txt");
+ assert_is_ignored(false, "dir/test.txt");
+ assert_is_ignored(true, "outer/dir/test.txt");
+}
/* confirm that ignore files were cached */
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/exclude"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/exclude"));
cl_assert(git_attr_cache__is_cached(
- g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitignore"));
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitignore"));
}
void test_index_addall__hidden_files(void)
{
+#ifdef GIT_WIN32
git_index *index;
- GIT_UNUSED(index);
-
-#ifdef GIT_WIN32
addall_create_test_repo(true);
cl_git_pass(git_repository_index(&index, g_repo));
void test_index_bypath__add_hidden(void)
{
+#ifdef GIT_WIN32
const git_index_entry *entry;
bool hidden;
- GIT_UNUSED(entry);
- GIT_UNUSED(hidden);
-
-#ifdef GIT_WIN32
cl_git_mkfile("submod2/hidden_file", "you can't see me");
cl_git_pass(git_win32__hidden(&hidden, "submod2/hidden_file"));
int i;
cl_git_pass(git_buf_puts(&fullpath, root));
- cl_git_pass(git_buf_putc(&fullpath, '/'));
+ cl_git_pass(git_path_to_dir(&fullpath));
root_len = fullpath.size;
#include "repository.h"
#include "git2/sys/repository.h"
#include "mailmap_testdata.h"
-#include "buf_text.h"
static git_repository *g_repo;
static git_mailmap *g_mailmap;
/* Parse with windows-style line endings */
git_buf_attach_notowned(&unixbuf, string_mailmap, strlen(string_mailmap));
- cl_git_pass(git_buf_text_lf_to_crlf(&winbuf, &unixbuf));
+ cl_git_pass(git_buf_lf_to_crlf(&winbuf, &unixbuf));
cl_git_pass(git_mailmap_from_buffer(&g_mailmap, winbuf.ptr, winbuf.size));
git_buf_dispose(&winbuf);
#include "clar_libgit2.h"
#include "clar_libgit2_trace.h"
+#ifdef GIT_WIN32_LEAKCHECK
+# include "win32/w32_leakcheck.h"
+#endif
+
#ifdef _WIN32
int __cdecl main(int argc, char *argv[])
#else
res = git_libgit2_init();
if (res < 0) {
- fprintf(stderr, "failed to init libgit2");
+ const git_error *err = git_error_last();
+ const char *msg = err ? err->message : "unknown failure";
+ fprintf(stderr, "failed to init libgit2: %s\n", msg);
return res;
}
cl_global_trace_disable();
git_libgit2_shutdown();
+#ifdef GIT_WIN32_LEAKCHECK
+ if (git_win32_leakcheck_has_leaks())
+ res = res || 1;
+#endif
+
at_exit_cmd = getenv("CLAR_AT_EXIT");
if (at_exit_cmd != NULL) {
int at_exit = system(at_exit_cmd);
git_buf file_path_buf = GIT_BUF_INIT, file_buf = GIT_BUF_INIT;
bool equals;
- git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename);
+ git_buf_joinpath(&file_path_buf, git_repository_path(repo), filename);
cl_git_pass(git_futils_readbuffer(&file_buf, file_path_buf.ptr));
equals = (strcmp(file_buf.ptr, expected) == 0);
{
git_buf file_path_buf = GIT_BUF_INIT;
- git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo),
+ git_buf_joinpath(&file_path_buf, git_repository_path(repo),
filename);
cl_git_rewritefile(file_path_buf.ptr, output);
+++ /dev/null
-#include "clar_libgit2.h"
-#include "net.h"
-#include "netops.h"
-
-static git_net_url source, target;
-
-void test_network_joinpath__initialize(void)
-{
- memset(&source, 0, sizeof(source));
- memset(&target, 0, sizeof(target));
-}
-
-void test_network_joinpath__cleanup(void)
-{
- git_net_url_dispose(&source);
- git_net_url_dispose(&target);
-}
-
-void test_network_joinpath__target_paths_and_queries(void)
-{
- cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b"));
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d"));
- cl_assert_equal_s(target.path, "/a/b/c/d");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d?foo"));
- cl_assert_equal_s(target.path, "/a/b/c/d");
- cl_assert_equal_s(target.query, "foo");
- git_net_url_dispose(&target);
-}
-
-void test_network_joinpath__source_query_removed(void)
-{
- cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b?query&one&two"));
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d"));
- cl_assert_equal_s(target.path, "/a/b/c/d");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d?foo"));
- cl_assert_equal_s(target.path, "/a/b/c/d");
- cl_assert_equal_s(target.query, "foo");
- git_net_url_dispose(&target);
-}
-
-void test_network_joinpath__source_lacks_path(void)
-{
- cl_git_pass(git_net_url_parse(&source, "http://example.com"));
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
- cl_assert_equal_s(target.path, "/");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, ""));
- cl_assert_equal_s(target.path, "/");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
- cl_assert_equal_s(target.path, "/foo/bar");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
- cl_assert_equal_s(target.path, "/foo/bar");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-}
-
-void test_network_joinpath__source_is_slash(void)
-{
- cl_git_pass(git_net_url_parse(&source, "http://example.com/"));
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
- cl_assert_equal_s(target.path, "/");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, ""));
- cl_assert_equal_s(target.path, "/");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
- cl_assert_equal_s(target.path, "/foo/bar");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
- cl_assert_equal_s(target.path, "/foo/bar");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-}
-
-
-void test_network_joinpath__source_has_query(void)
-{
- cl_git_pass(git_net_url_parse(&source, "http://example.com?query"));
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
- cl_assert_equal_s(target.path, "/");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, ""));
- cl_assert_equal_s(target.path, "/");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
- cl_assert_equal_s(target.path, "/foo/bar");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
- cl_assert_equal_s(target.path, "/asdf");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
- cl_assert_equal_s(target.path, "/foo/bar");
- cl_assert_equal_s(target.query, "hello");
- git_net_url_dispose(&target);
-}
-
-
-void test_network_joinpath__empty_query_ignored(void)
-{
- cl_git_pass(git_net_url_parse(&source, "http://example.com/foo"));
-
- cl_git_pass(git_net_url_joinpath(&target, &source, "/bar/baz?"));
- cl_assert_equal_s(target.path, "/foo/bar/baz");
- cl_assert_equal_p(target.query, NULL);
- git_net_url_dispose(&target);
-}
+++ /dev/null
-#include "clar_libgit2.h"
-#include "net.h"
-#include "netops.h"
-
-static git_net_url conndata;
-
-void test_network_redirect__initialize(void)
-{
- memset(&conndata, 0, sizeof(conndata));
-}
-
-void test_network_redirect__cleanup(void)
-{
- git_net_url_dispose(&conndata);
-}
-
-void test_network_redirect__redirect_http(void)
-{
- cl_git_pass(git_net_url_parse(&conndata,
- "http://example.com/foo/bar/baz"));
- cl_git_pass(git_net_url_apply_redirect(&conndata,
- "http://example.com/foo/bar/baz", "bar/baz"));
- cl_assert_equal_s(conndata.scheme, "http");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/foo/");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
-}
-
-void test_network_redirect__redirect_ssl(void)
-{
- cl_git_pass(git_net_url_parse(&conndata,
- "https://example.com/foo/bar/baz"));
- cl_git_pass(git_net_url_apply_redirect(&conndata,
- "https://example.com/foo/bar/baz", "bar/baz"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/foo/");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
-}
-
-void test_network_redirect__redirect_leaves_root_path(void)
-{
- cl_git_pass(git_net_url_parse(&conndata,
- "https://example.com/foo/bar/baz"));
- cl_git_pass(git_net_url_apply_redirect(&conndata,
- "https://example.com/foo/bar/baz", "/foo/bar/baz"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
-}
-
-void test_network_redirect__redirect_encoded_username_password(void)
-{
- cl_git_pass(git_net_url_parse(&conndata,
- "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz"));
- cl_git_pass(git_net_url_apply_redirect(&conndata,
- "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/foo/");
- cl_assert_equal_s(conndata.username, "user/name");
- cl_assert_equal_s(conndata.password, "pass@word%zyx%v");
-}
-
-void test_network_redirect__redirect_cross_host_denied(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "https://bar.com/bar/baz"));
- cl_git_fail_with(git_net_url_apply_redirect(&conndata,
- "https://foo.com/bar/baz", NULL),
- -1);
-}
-
-void test_network_redirect__redirect_http_downgrade_denied(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz"));
- cl_git_fail_with(git_net_url_apply_redirect(&conndata,
- "http://foo.com/bar/baz", NULL),
- -1);
-}
-
-void test_network_redirect__redirect_relative(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "http://foo.com/bar/baz/biff"));
- cl_git_pass(git_net_url_apply_redirect(&conndata,
- "/zap/baz/biff?bam", NULL));
- cl_assert_equal_s(conndata.scheme, "http");
- cl_assert_equal_s(conndata.host, "foo.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
-}
-
-void test_network_redirect__redirect_relative_ssl(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz/biff"));
- cl_git_pass(git_net_url_apply_redirect(&conndata,
- "/zap/baz/biff?bam", NULL));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "foo.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
-}
-
-void test_network_redirect__service_query_no_query_params_in_location(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack"));
- cl_git_pass(git_net_url_apply_redirect(&conndata,
- "/baz/info/refs", "/info/refs?service=git-upload-pack"));
- cl_assert_equal_s(conndata.path, "/baz");
-}
-
-void test_network_redirect__service_query_with_query_params_in_location(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack"));
- cl_git_pass(git_net_url_apply_redirect(&conndata,
- "/baz/info/refs?service=git-upload-pack", "/info/refs?service=git-upload-pack"));
- cl_assert_equal_s(conndata.path, "/baz");
-}
if (is_expected_to_be_valid)
cl_assert_equal_i(0, error);
else
- cl_assert_equal_i(GIT_ERROR, error);
+ cl_assert_equal_i(GIT_EINVALIDSPEC, error);
}
void test_network_refspecs__parsing(void)
#include "clar_libgit2.h"
+static int is_valid_name(const char *name)
+{
+ int valid = 0;
+ cl_git_pass(git_remote_name_is_valid(&valid, name));
+ return valid;
+}
+
void test_network_remote_isvalidname__can_detect_invalid_formats(void)
{
- cl_assert_equal_i(false, git_remote_is_valid_name("/"));
- cl_assert_equal_i(false, git_remote_is_valid_name("//"));
- cl_assert_equal_i(false, git_remote_is_valid_name(".lock"));
- cl_assert_equal_i(false, git_remote_is_valid_name("a.lock"));
- cl_assert_equal_i(false, git_remote_is_valid_name("/no/leading/slash"));
- cl_assert_equal_i(false, git_remote_is_valid_name("no/trailing/slash/"));
+ cl_assert_equal_i(false, is_valid_name("/"));
+ cl_assert_equal_i(false, is_valid_name("//"));
+ cl_assert_equal_i(false, is_valid_name(".lock"));
+ cl_assert_equal_i(false, is_valid_name("a.lock"));
+ cl_assert_equal_i(false, is_valid_name("/no/leading/slash"));
+ cl_assert_equal_i(false, is_valid_name("no/trailing/slash/"));
}
void test_network_remote_isvalidname__wont_hopefully_choke_on_valid_formats(void)
{
- cl_assert_equal_i(true, git_remote_is_valid_name("webmatrix"));
- cl_assert_equal_i(true, git_remote_is_valid_name("yishaigalatzer/rules"));
+ cl_assert_equal_i(true, is_valid_name("webmatrix"));
+ cl_assert_equal_i(true, is_valid_name("yishaigalatzer/rules"));
}
git_buf_dispose(&url);
}
+static int remote_ready_callback(git_remote *remote, int direction, void *payload)
+{
+ if (direction == GIT_DIRECTION_PUSH) {
+ const char *url = git_remote_pushurl(remote);
+
+ cl_assert_equal_p(url, NULL);;
+ cl_assert_equal_s(payload, "payload");
+ return git_remote_set_instance_pushurl(remote, "push_url");
+ }
+
+ if (direction == GIT_DIRECTION_FETCH) {
+ const char *url = git_remote_url(remote);
+
+ cl_assert_equal_s(url, "git://github.com/libgit2/libgit2");
+ cl_assert_equal_s(payload, "payload");
+ return git_remote_set_instance_url(remote, "fetch_url");
+ }
+
+ return -1;
+}
+
+void test_network_remote_remotes__remote_ready(void)
+{
+ git_buf url = GIT_BUF_INIT;
+
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ callbacks.remote_ready = remote_ready_callback;
+ callbacks.payload = "payload";
+
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+ cl_assert(git_remote_pushurl(_remote) == NULL);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, &callbacks));
+ cl_assert_equal_s(url.ptr, "fetch_url");
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, &callbacks));
+ cl_assert_equal_s(url.ptr, "push_url");
+
+ git_buf_dispose(&url);
+}
+
+#ifndef GIT_DEPRECATE_HARD
static int urlresolve_callback(git_buf *url_resolved, const char *url, int direction, void *payload)
{
cl_assert(strcmp(url, "git://github.com/libgit2/libgit2") == 0);
return GIT_OK;
}
+#endif
void test_network_remote_remotes__urlresolve(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_buf url = GIT_BUF_INIT;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
cl_assert_equal_s(url.ptr, "pushresolve");
git_buf_dispose(&url);
+#endif
}
+#ifndef GIT_DEPRECATE_HARD
static int urlresolve_passthrough_callback(git_buf *url_resolved, const char *url, int direction, void *payload)
{
GIT_UNUSED(url_resolved);
GIT_UNUSED(payload);
return GIT_PASSTHROUGH;
}
+#endif
void test_network_remote_remotes__urlresolve_passthrough(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_buf url = GIT_BUF_INIT;
const char *orig_url = "git://github.com/libgit2/libgit2";
cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, &callbacks));
cl_assert_equal_s(url.ptr, orig_url);
+ git_buf_dispose(&url);
+#endif
+}
+
+void test_network_remote_remotes__instance_url(void)
+{
+ git_buf url = GIT_BUF_INIT;
+ const char *orig_url = "git://github.com/libgit2/libgit2";
+
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), orig_url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, NULL));
+ cl_assert_equal_s(url.ptr, orig_url);
+ git_buf_clear(&url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, NULL));
+ cl_assert_equal_s(url.ptr, orig_url);
+ git_buf_clear(&url);
+
+ /* Setting the instance url updates the fetch and push URLs */
+ git_remote_set_instance_url(_remote, "https://github.com/new/remote/url");
+ cl_assert_equal_s(git_remote_url(_remote), "https://github.com/new/remote/url");
+ cl_assert_equal_p(git_remote_pushurl(_remote), NULL);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, NULL));
+ cl_assert_equal_s(url.ptr, "https://github.com/new/remote/url");
+ git_buf_clear(&url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, NULL));
+ cl_assert_equal_s(url.ptr, "https://github.com/new/remote/url");
+ git_buf_clear(&url);
+
+ /* Setting the instance push url updates only the push URL */
+ git_remote_set_instance_pushurl(_remote, "https://github.com/new/push/url");
+ cl_assert_equal_s(git_remote_url(_remote), "https://github.com/new/remote/url");
+ cl_assert_equal_s(git_remote_pushurl(_remote), "https://github.com/new/push/url");
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, NULL));
+ cl_assert_equal_s(url.ptr, "https://github.com/new/remote/url");
+ git_buf_clear(&url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, NULL));
+ cl_assert_equal_s(url.ptr, "https://github.com/new/push/url");
+ git_buf_clear(&url);
+
git_buf_dispose(&url);
}
--- /dev/null
+#include "clar_libgit2.h"
+#include "net.h"
+#include "netops.h"
+
+static git_net_url source, target;
+
+void test_network_url_joinpath__initialize(void)
+{
+ memset(&source, 0, sizeof(source));
+ memset(&target, 0, sizeof(target));
+}
+
+void test_network_url_joinpath__cleanup(void)
+{
+ git_net_url_dispose(&source);
+ git_net_url_dispose(&target);
+}
+
+void test_network_url_joinpath__target_paths_and_queries(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d"));
+ cl_assert_equal_s(target.path, "/a/b/c/d");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d?foo"));
+ cl_assert_equal_s(target.path, "/a/b/c/d");
+ cl_assert_equal_s(target.query, "foo");
+ git_net_url_dispose(&target);
+}
+
+void test_network_url_joinpath__source_query_removed(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b?query&one&two"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d"));
+ cl_assert_equal_s(target.path, "/a/b/c/d");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d?foo"));
+ cl_assert_equal_s(target.path, "/a/b/c/d");
+ cl_assert_equal_s(target.query, "foo");
+ git_net_url_dispose(&target);
+}
+
+void test_network_url_joinpath__source_lacks_path(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, ""));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+}
+
+void test_network_url_joinpath__source_is_slash(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com/"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, ""));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+}
+
+
+void test_network_url_joinpath__source_has_query(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com?query"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, ""));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+}
+
+
+void test_network_url_joinpath__empty_query_ignored(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com/foo"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/bar/baz?"));
+ cl_assert_equal_s(target.path, "/foo/bar/baz");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "net.h"
+
+static git_net_url conndata;
+
+void test_network_url_parse__initialize(void)
+{
+ memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_network_url_parse__cleanup(void)
+{
+ git_net_url_dispose(&conndata);
+}
+
+/* Hostname */
+
+void test_network_url_parse__hostname_trivial(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__hostname_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__hostname_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__hostname_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__hostname_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__hostname_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@hostname.com:1234/"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "hostname.com");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__hostname_user(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__hostname_user_pass(void)
+{
+ /* user:pass@hostname.tld/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__hostname_port(void)
+{
+ /* hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__hostname_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__hostname_user_port(void)
+{
+ /* user@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__hostname_user_pass_port(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+/* IPv4 addresses */
+
+void test_network_url_parse__ipv4_trivial(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv4_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv4_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv4_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv4_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv4_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@192.168.1.1:1234/"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv4_user(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv4_user_pass(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv4_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv4_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv4_user_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv4_user_pass_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+/* IPv6 addresses */
+
+void test_network_url_parse__ipv6_trivial(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv6_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv6_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv6_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv6_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv6_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001]:1234/"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv6_user(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv6_user_pass(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv6_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv6_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_network_url_parse__ipv6_user_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv6_user_pass_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_network_url_parse__ipv6_invalid_addresses(void)
+{
+ /* Opening bracket missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@fe80::dcad:beff:fe00:0001]:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@fe80::dcad:beff:fe00:0001]:9191/resource"));
+
+ /* Closing bracket missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://[fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@[fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@[fe80::dcad:beff:fe00:0001:9191/resource"));
+ /* Both brackets missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@fe80::dcad:beff:fe00:0001:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@fe80::dcad:beff:fe00:0001:9191/resource"));
+
+ /* Invalid chracter inside address */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata, "http://[fe8o::dcad:beff:fe00:0001]/resource"));
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "net.h"
+
+struct url_pattern {
+ const char *url;
+ const char *pattern;
+ bool matches;
+};
+
+void test_network_url_pattern__single(void)
+{
+ git_net_url url;
+ size_t i;
+
+ struct url_pattern url_patterns[] = {
+ /* Wildcard matches */
+ { "https://example.com/", "", false },
+ { "https://example.com/", "*", true },
+
+ /* Literal and wildcard matches */
+ { "https://example.com/", "example.com", true },
+ { "https://example.com/", ".example.com", true },
+ { "https://example.com/", "*.example.com", true },
+ { "https://www.example.com/", "www.example.com", true },
+ { "https://www.example.com/", ".example.com", true },
+ { "https://www.example.com/", "*.example.com", true },
+
+ /* Literal and wildcard failures */
+ { "https://example.com/", "example.org", false },
+ { "https://example.com/", ".example.org", false },
+ { "https://example.com/", "*.example.org", false },
+ { "https://foo.example.com/", "www.example.com", false },
+
+ /*
+ * A port in the pattern is optional; if no port is
+ * present, it matches *all* ports.
+ */
+ { "https://example.com/", "example.com:443", true },
+ { "https://example.com/", "example.com:80", false },
+ { "https://example.com:1443/", "example.com", true },
+
+ /* Failures with similar prefix/suffix */
+ { "https://texample.com/", "example.com", false },
+ { "https://example.com/", "mexample.com", false },
+ { "https://example.com:44/", "example.com:443", false },
+ { "https://example.com:443/", "example.com:44", false },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(url_patterns); i++) {
+ cl_git_pass(git_net_url_parse(&url, url_patterns[i].url));
+ cl_assert_(git_net_url_matches_pattern(&url, url_patterns[i].pattern) == url_patterns[i].matches, url_patterns[i].pattern);
+ git_net_url_dispose(&url);
+ }
+}
+
+void test_network_url_pattern__list(void)
+{
+ git_net_url url;
+ size_t i;
+
+ struct url_pattern url_patterns[] = {
+ /* Wildcard matches */
+ { "https://example.com/", "", false },
+ { "https://example.com/", "*", true },
+ { "https://example.com/", ",example.com,", true },
+ { "https://example.com/", "foo,,example.com,,bar", true },
+ { "https://example.com/", "foo,,zzz,,*,,bar", true },
+
+ /* Literals */
+ { "https://example.com/", "example.com", true },
+ { "https://example.com/", "foo.bar,example.com", true },
+ { "https://example.com/", "foo.bar", false },
+ { "https://example.com/", "foo.bar,example.org", false },
+ { "https://www.example.com/", "foo.example.com,www.example.com,bar.example.com", true },
+ { "https://www.example.com/", "foo.example.com,baz.example.com,bar.example.com", false },
+ { "https://foo.example.com/", "www.example.com", false },
+ { "https://foo.example.com/", "bar.example.com,www.example.com,", false },
+
+ /* Wildcards */
+ { "https://example.com/", ".example.com", true },
+ { "https://example.com/", "*.example.com", true },
+ { "https://example.com/", "foo.com,bar.com,.example.com", true },
+ { "https://example.com/", ".foo.com,.bar.com,.example.com", true },
+ { "https://example.com/", ".foo.com,.bar.com,asdf.com", false },
+ { "https://example.com/", "*.foo,*.bar,*.example.com,*.asdf", true },
+ { "https://example.com/", "*.foo,*.bar,*.asdf", false },
+
+
+ /* Ports! */
+ { "https://example.com/", "example.com:443", true },
+ { "https://example.com/", "example.com:42,example.com:443,example.com:99", true },
+ { "https://example.com/", "example.com:42,example.com:80,example.org:443", false },
+ { "https://example.com:1443/", "example.com", true },
+ { "https://example.com:44/", "example.com:443", false },
+ { "https://example.com:443/", "example.com:44", false },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(url_patterns); i++) {
+ cl_git_pass(git_net_url_parse(&url, url_patterns[i].url));
+ cl_assert_(git_net_url_matches_pattern_list(&url, url_patterns[i].pattern) == url_patterns[i].matches, url_patterns[i].pattern);
+ git_net_url_dispose(&url);
+ }
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "net.h"
+#include "netops.h"
+
+static git_net_url conndata;
+
+void test_network_url_redirect__initialize(void)
+{
+ memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_network_url_redirect__cleanup(void)
+{
+ git_net_url_dispose(&conndata);
+}
+
+void test_network_url_redirect__redirect_http(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "http://example.com/foo/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "http://example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_url_redirect__redirect_ssl(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com/foo/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "https://example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_url_redirect__redirect_leaves_root_path(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com/foo/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "https://example.com/foo/bar/baz", "/foo/bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_url_redirect__redirect_encoded_username_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_s(conndata.username, "user/name");
+ cl_assert_equal_s(conndata.password, "pass@word%zyx%v");
+}
+
+void test_network_url_redirect__redirect_cross_host_denied(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://bar.com/bar/baz"));
+ cl_git_fail_with(git_net_url_apply_redirect(&conndata,
+ "https://foo.com/bar/baz", NULL),
+ -1);
+}
+
+void test_network_url_redirect__redirect_http_downgrade_denied(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz"));
+ cl_git_fail_with(git_net_url_apply_redirect(&conndata,
+ "http://foo.com/bar/baz", NULL),
+ -1);
+}
+
+void test_network_url_redirect__redirect_relative(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://foo.com/bar/baz/biff"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "/zap/baz/biff?bam", NULL));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_url_redirect__redirect_relative_ssl(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz/biff"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "/zap/baz/biff?bam", NULL));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_network_url_redirect__service_query_no_query_params_in_location(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "/baz/info/refs", "/info/refs?service=git-upload-pack"));
+ cl_assert_equal_s(conndata.path, "/baz");
+}
+
+void test_network_url_redirect__service_query_with_query_params_in_location(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "/baz/info/refs?service=git-upload-pack", "/info/refs?service=git-upload-pack"));
+ cl_assert_equal_s(conndata.path, "/baz");
+}
+++ /dev/null
-#include "clar_libgit2.h"
-#include "net.h"
-
-static git_net_url conndata;
-
-void test_network_urlparse__initialize(void)
-{
- memset(&conndata, 0, sizeof(conndata));
-}
-
-void test_network_urlparse__cleanup(void)
-{
- git_net_url_dispose(&conndata);
-}
-
-void test_network_urlparse__trivial(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "http://example.com/resource"));
- cl_assert_equal_s(conndata.scheme, "http");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/resource");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
-}
-
-void test_network_urlparse__root(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "http://example.com/"));
- cl_assert_equal_s(conndata.scheme, "http");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
-}
-
-void test_network_urlparse__implied_root(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "http://example.com"));
- cl_assert_equal_s(conndata.scheme, "http");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
-}
-
-void test_network_urlparse__implied_root_custom_port(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "http://example.com:42"));
- cl_assert_equal_s(conndata.scheme, "http");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "42");
- cl_assert_equal_s(conndata.path, "/");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
-}
-
-void test_network_urlparse__implied_root_empty_port(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "http://example.com:"));
- cl_assert_equal_s(conndata.scheme, "http");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
-}
-
-void test_network_urlparse__encoded_password(void)
-{
- cl_git_pass(git_net_url_parse(&conndata,
- "https://user:pass%2fis%40bad@hostname.com:1234/"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "hostname.com");
- cl_assert_equal_s(conndata.port, "1234");
- cl_assert_equal_s(conndata.path, "/");
- cl_assert_equal_s(conndata.username, "user");
- cl_assert_equal_s(conndata.password, "pass/is@bad");
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
-}
-
-void test_network_urlparse__user(void)
-{
- cl_git_pass(git_net_url_parse(&conndata,
- "https://user@example.com/resource"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/resource");
- cl_assert_equal_s(conndata.username, "user");
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
-}
-
-void test_network_urlparse__user_pass(void)
-{
- /* user:pass@hostname.tld/resource */
- cl_git_pass(git_net_url_parse(&conndata,
- "https://user:pass@example.com/resource"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "443");
- cl_assert_equal_s(conndata.path, "/resource");
- cl_assert_equal_s(conndata.username, "user");
- cl_assert_equal_s(conndata.password, "pass");
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
-}
-
-void test_network_urlparse__port(void)
-{
- /* hostname.tld:port/resource */
- cl_git_pass(git_net_url_parse(&conndata,
- "https://example.com:9191/resource"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "9191");
- cl_assert_equal_s(conndata.path, "/resource");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
-}
-
-void test_network_urlparse__empty_port(void)
-{
- cl_git_pass(git_net_url_parse(&conndata, "http://example.com:/resource"));
- cl_assert_equal_s(conndata.scheme, "http");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "80");
- cl_assert_equal_s(conndata.path, "/resource");
- cl_assert_equal_p(conndata.username, NULL);
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
-}
-
-void test_network_urlparse__user_port(void)
-{
- /* user@hostname.tld:port/resource */
- cl_git_pass(git_net_url_parse(&conndata,
- "https://user@example.com:9191/resource"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "9191");
- cl_assert_equal_s(conndata.path, "/resource");
- cl_assert_equal_s(conndata.username, "user");
- cl_assert_equal_p(conndata.password, NULL);
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
-}
-
-void test_network_urlparse__user_pass_port(void)
-{
- /* user:pass@hostname.tld:port/resource */
- cl_git_pass(git_net_url_parse(&conndata,
- "https://user:pass@example.com:9191/resource"));
- cl_assert_equal_s(conndata.scheme, "https");
- cl_assert_equal_s(conndata.host, "example.com");
- cl_assert_equal_s(conndata.port, "9191");
- cl_assert_equal_s(conndata.path, "/resource");
- cl_assert_equal_s(conndata.username, "user");
- cl_assert_equal_s(conndata.password, "pass");
- cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
-}
#include "clar_libgit2.h"
#include "posix.h"
#include "blob.h"
-#include "buf_text.h"
static git_repository *g_repo = NULL;
{ 0, 0, 2, 2, 2, 6, 0 },
{ 0, 0, 4, 4, 1, 31, 0 },
{ 0, 1, 1, 2, 1, 9, 5 },
- { GIT_BOM_UTF8, 0, 0, 1, 0, 16, 0 },
- { GIT_BOM_UTF8, 0, 2, 2, 2, 27, 0 },
- { GIT_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 },
+ { GIT_BUF_BOM_UTF8, 0, 0, 1, 0, 16, 0 },
+ { GIT_BUF_BOM_UTF8, 0, 2, 2, 2, 27, 0 },
+ { GIT_BUF_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 },
};
void test_object_blob_filter__initialize(void)
for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
cl_git_pass(git_blob__getbuf(&buf, blob));
- git_buf_text_gather_stats(&stats, &buf, false);
+ git_buf_gather_text_stats(&stats, &buf, false);
cl_assert_equal_i(
0, memcmp(&g_crlf_filtered_stats[i], &stats, sizeof(stats)));
git_blob_free(blob);
int already_found[MAX_USED_TAGS] = { 0 };
git_strarray tag_list;
int error = 0;
- size_t sucessfully_found = 0;
+ size_t successfully_found = 0;
size_t i, j;
cl_assert(data->expected_matches <= MAX_USED_TAGS);
if (!already_found[j] && !strcmp(data->expected_results[j], tag_list.strings[i]))
{
already_found[j] = 1;
- sucessfully_found++;
+ successfully_found++;
break;
}
}
}
- cl_assert_equal_i((int)sucessfully_found, (int)data->expected_matches);
+ cl_assert_equal_i((int)successfully_found, (int)data->expected_matches);
exit:
git_strarray_dispose(&tag_list);
cl_fixture_sandbox("testrepo.git");
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
- cl_git_pass(git_buf_printf(&buf, "%s/objects/somefile", git_repository_path(repo)));
+ cl_git_pass(git_buf_joinpath(&buf, git_repository_path(repo), "objects/somefile"));
cl_git_mkfile(buf.ptr, "");
git_buf_dispose(&buf);
#include "clar_libgit2.h"
#include "git2/sys/odb_backend.h"
+#include "odb.h"
typedef struct {
git_odb_backend base;
{
git_odb_free(_odb);
_odb = NULL;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY,
+ GIT_ODB_DEFAULT_LOOSE_PRIORITY));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_PACKED_PRIORITY,
+ GIT_ODB_DEFAULT_PACKED_PRIORITY));
}
void test_odb_sorting__basic_backends_sorting(void)
check_backend_sorting(_odb);
}
+
+void test_odb_sorting__override_default_backend_priority(void)
+{
+ git_odb *new_odb;
+ git_odb_backend *loose, *packed, *backend;
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, 5));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, 3));
+ git_odb_backend_pack(&packed, "./testrepo.git/objects");
+ git_odb_backend_loose(&loose, "./testrepo.git/objects", -1, 0, 0, 0);
+
+ cl_git_pass(git_odb_open(&new_odb, cl_fixture("testrepo.git/objects")));
+ cl_assert_equal_sz(2, git_odb_num_backends(new_odb));
+
+ cl_git_pass(git_odb_get_backend(&backend, new_odb, 0));
+ cl_assert_equal_p(loose->read, backend->read);
+
+ cl_git_pass(git_odb_get_backend(&backend, new_odb, 1));
+ cl_assert_equal_p(packed->read, backend->read);
+
+ git_odb_free(new_odb);
+ loose->free(loose);
+ packed->free(packed);
+}
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
- /* FIXME: we don't actually reject RC4 anywhere, figure out what to tweak */
- cl_skip();
-
if (!g_has_ssl)
cl_skip();
- cl_git_fail_with(GIT_ECERTIFICATE,
- git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", NULL));
- cl_git_fail_with(GIT_ECERTIFICATE,
- git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", &opts));
+ cl_git_fail(git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", NULL));
+ cl_git_fail(git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", &opts));
}
static int _orig_proxies_need_reset = 0;
static char *_orig_http_proxy = NULL;
static char *_orig_https_proxy = NULL;
+static char *_orig_no_proxy = NULL;
static int ssl_cert(git_cert *cert, int valid, const char *host, void *payload)
{
if (_orig_proxies_need_reset) {
cl_setenv("HTTP_PROXY", _orig_http_proxy);
cl_setenv("HTTPS_PROXY", _orig_https_proxy);
+ cl_setenv("NO_PROXY", _orig_no_proxy);
git__free(_orig_http_proxy);
git__free(_orig_https_proxy);
+ git__free(_orig_no_proxy);
}
+
+ git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, NULL, NULL);
}
void test_online_clone__network_full(void)
_orig_http_proxy = cl_getenv("HTTP_PROXY");
_orig_https_proxy = cl_getenv("HTTPS_PROXY");
+ _orig_no_proxy = cl_getenv("NO_PROXY");
_orig_proxies_need_reset = 1;
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
cl_setenv("HTTP_PROXY", url.ptr);
cl_setenv("HTTPS_PROXY", url.ptr);
+ cl_setenv("NO_PROXY", NULL);
cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
git_buf_dispose(&url);
}
+void test_online_clone__proxy_credentials_in_url_https(void)
+{
+ git_buf url = GIT_BUF_INIT;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ cl_git_pass(git_buf_printf(&url, "%s://%s:%s@%s/",
+ _remote_proxy_scheme ? _remote_proxy_scheme : "http",
+ _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+ g_options.fetch_opts.callbacks.certificate_check = ssl_cert;
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 0);
+
+ git_buf_dispose(&url);
+}
+
void test_online_clone__proxy_auto_not_detected(void)
{
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
--- /dev/null
+#include "clar_libgit2.h"
+
+#include "path.h"
+#include "git2/clone.h"
+#include "git2/cred_helpers.h"
+#include "remote.h"
+#include "futils.h"
+#include "refs.h"
+
+/*
+ * Certificate one is in the `certs` folder; certificate two is in the
+ * `self-signed.pem` file.
+ */
+#define CUSTOM_CERT_ONE_URL "https://test.libgit2.org:1443/anonymous/test.git"
+#define CUSTOM_CERT_ONE_PATH "certs"
+
+#define CUSTOM_CERT_TWO_URL "https://test.libgit2.org:2443/anonymous/test.git"
+#define CUSTOM_CERT_TWO_FILE "self-signed.pem"
+
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+static git_repository *g_repo;
+static int initialized = false;
+#endif
+
+void test_online_customcert__initialize(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ g_repo = NULL;
+
+ if (!initialized) {
+ git_buf path = GIT_BUF_INIT, file = GIT_BUF_INIT;
+ char cwd[GIT_PATH_MAX];
+
+ cl_fixture_sandbox(CUSTOM_CERT_ONE_PATH);
+ cl_fixture_sandbox(CUSTOM_CERT_TWO_FILE);
+
+ cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
+ cl_git_pass(git_buf_joinpath(&path, cwd, CUSTOM_CERT_ONE_PATH));
+ cl_git_pass(git_buf_joinpath(&file, cwd, CUSTOM_CERT_TWO_FILE));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS,
+ file.ptr, path.ptr));
+ initialized = true;
+
+ git_buf_dispose(&file);
+ git_buf_dispose(&path);
+ }
+#endif
+}
+
+void test_online_customcert__cleanup(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ cl_fixture_cleanup("./cloned");
+ cl_fixture_cleanup(CUSTOM_CERT_ONE_PATH);
+ cl_fixture_cleanup(CUSTOM_CERT_TWO_FILE);
+#endif
+}
+
+void test_online_customcert__file(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ cl_git_pass(git_clone(&g_repo, CUSTOM_CERT_ONE_URL, "./cloned", NULL));
+ cl_assert(git_path_exists("./cloned/master.txt"));
+#endif
+}
+
+void test_online_customcert__path(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ cl_git_pass(git_clone(&g_repo, CUSTOM_CERT_TWO_URL, "./cloned", NULL));
+ cl_assert(git_path_exists("./cloned/master.txt"));
+#endif
+}
static git_repository *_repo;
static int counter;
+static char *_remote_proxy_scheme = NULL;
+static char *_remote_proxy_host = NULL;
+static char *_remote_proxy_user = NULL;
+static char *_remote_proxy_pass = NULL;
+
void test_online_fetch__initialize(void)
{
cl_git_pass(git_repository_init(&_repo, "./fetch", 0));
+
+ _remote_proxy_scheme = cl_getenv("GITTEST_REMOTE_PROXY_SCHEME");
+ _remote_proxy_host = cl_getenv("GITTEST_REMOTE_PROXY_HOST");
+ _remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER");
+ _remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
}
void test_online_fetch__cleanup(void)
_repo = NULL;
cl_fixture_cleanup("./fetch");
+
+ git__free(_remote_proxy_scheme);
+ git__free(_remote_proxy_host);
+ git__free(_remote_proxy_user);
+ git__free(_remote_proxy_pass);
}
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
git_remote_free(remote);
}
+
+void test_online_fetch__proxy(void)
+{
+ git_remote *remote;
+ git_buf url = GIT_BUF_INIT;
+ git_fetch_options fetch_opts;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ cl_git_pass(git_buf_printf(&url, "%s://%s:%s@%s/",
+ _remote_proxy_scheme ? _remote_proxy_scheme : "http",
+ _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
+
+ cl_git_pass(git_fetch_options_init(&fetch_opts, GIT_FETCH_OPTIONS_VERSION));
+ fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ fetch_opts.proxy_opts.url = url.ptr;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test", "https://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, &fetch_opts.proxy_opts, NULL));
+ cl_git_pass(git_remote_fetch(remote, NULL, &fetch_opts, NULL));
+
+ git_remote_free(remote);
+ git_buf_dispose(&url);
+}
int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len)
{
git_buf del_spec = GIT_BUF_INIT;
+ int valid;
size_t i;
for (i = 0; i < heads_len; i++) {
const git_remote_head *head = heads[i];
/* Ignore malformed ref names (which also saves us from tag^{} */
- if (!git_reference_is_valid_name(head->name))
+ cl_git_pass(git_reference_name_is_valid(&valid, head->name));
+ if (!valid)
return 0;
/* Create a refspec that deletes a branch in the remote */
* @param data pointer to a record_callbacks_data instance
*/
#define RECORD_CALLBACKS_INIT(data) \
- { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, NULL, NULL, data, NULL }
+ { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, NULL, NULL, NULL, data, NULL }
typedef struct {
char *name;
#include "clar_libgit2.h"
#include "mwindow.h"
-#include "global.h"
#include <git2.h>
#include "git2/sys/commit.h"
static size_t expected_open_mwindow_files = 0;
static size_t original_mwindow_file_limit = 0;
+extern git_mutex git__mwindow_mutex;
extern git_mwindow_ctl git_mwindow__mem_ctl;
void test_pack_filelimit__initialize_tiny(void)
#include "clar_libgit2.h"
#include <git2.h>
+#include <git2/sys/midx.h>
+#include "futils.h"
#include "midx.h"
void test_pack_midx__parse(void)
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_buf_joinpath(&midx_path, git_repository_path(repo), "objects/pack/multi-pack-index"));
cl_git_pass(git_midx_open(&idx, git_buf_cstr(&midx_path)));
+ cl_assert_equal_i(git_midx_needs_refresh(idx, git_buf_cstr(&midx_path)), 0);
cl_git_pass(git_oid_fromstr(&id, "5001298e0c09ad9c34e4249bc5801c75e9754fa5"));
cl_git_pass(git_midx_entry_find(&e, idx, &id, GIT_OID_HEXSZ));
git_repository_free(repo);
git_buf_dispose(&midx_path);
}
+
+void test_pack_midx__lookup(void)
+{
+ git_repository *repo;
+ git_commit *commit;
+ git_oid id;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_oid_fromstr(&id, "5001298e0c09ad9c34e4249bc5801c75e9754fa5"));
+ cl_git_pass(git_commit_lookup_prefix(&commit, repo, &id, GIT_OID_HEXSZ));
+ cl_assert_equal_s(git_commit_message(commit), "packed commit one\n");
+
+ git_commit_free(commit);
+ git_repository_free(repo);
+}
+
+void test_pack_midx__writer(void)
+{
+ git_repository *repo;
+ git_midx_writer *w = NULL;
+ git_buf midx = GIT_BUF_INIT, expected_midx = GIT_BUF_INIT, path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/pack"));
+ cl_git_pass(git_midx_writer_new(&w, git_buf_cstr(&path)));
+
+ cl_git_pass(git_midx_writer_add(w, "pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx"));
+ cl_git_pass(git_midx_writer_add(w, "pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx"));
+ cl_git_pass(git_midx_writer_add(w, "pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"));
+
+ cl_git_pass(git_midx_writer_dump(&midx, w));
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/pack/multi-pack-index"));
+ cl_git_pass(git_futils_readbuffer(&expected_midx, git_buf_cstr(&path)));
+
+ cl_assert_equal_i(git_buf_len(&midx), git_buf_len(&expected_midx));
+ cl_assert_equal_strn(git_buf_cstr(&midx), git_buf_cstr(&expected_midx), git_buf_len(&midx));
+
+ git_buf_dispose(&midx);
+ git_buf_dispose(&expected_midx);
+ git_buf_dispose(&path);
+ git_midx_writer_free(w);
+ git_repository_free(repo);
+}
+
+void test_pack_midx__odb_create(void)
+{
+ git_repository *repo;
+ git_odb *odb;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_buf midx = GIT_BUF_INIT, expected_midx = GIT_BUF_INIT, midx_path = GIT_BUF_INIT;
+ struct stat st;
+
+ opts.bare = true;
+ opts.local = GIT_CLONE_LOCAL;
+ cl_git_pass(git_clone(&repo, cl_fixture("testrepo/.gitted"), "./clone.git", &opts));
+ cl_git_pass(git_buf_joinpath(&midx_path, git_repository_path(repo), "objects/pack/multi-pack-index"));
+ cl_git_fail(p_stat(git_buf_cstr(&midx_path), &st));
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_git_pass(git_odb_write_multi_pack_index(odb));
+ git_odb_free(odb);
+
+ cl_git_pass(p_stat(git_buf_cstr(&midx_path), &st));
+
+ cl_git_pass(git_futils_readbuffer(&expected_midx, cl_fixture("testrepo.git/objects/pack/multi-pack-index")));
+ cl_git_pass(git_futils_readbuffer(&midx, git_buf_cstr(&midx_path)));
+ cl_assert_equal_i(git_buf_len(&midx), git_buf_len(&expected_midx));
+ cl_assert_equal_strn(git_buf_cstr(&midx), git_buf_cstr(&expected_midx), git_buf_len(&midx));
+
+ git_repository_free(repo);
+ git_buf_dispose(&midx);
+ git_buf_dispose(&midx_path);
+ git_buf_dispose(&expected_midx);
+
+ cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
+}
\ No newline at end of file
--- /dev/null
+#include "clar_libgit2.h"
+#include "pool.h"
+
+#include <git2.h>
+#include "git2/sys/commit.h"
+#include "git2/sys/mempack.h"
+
+static size_t original_mwindow_file_limit = 0;
+
+void test_pack_threadsafety__initialize(void)
+{
+ size_t open_mwindow_files = 1;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, &original_mwindow_file_limit));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, open_mwindow_files));
+}
+
+void test_pack_threadsafety__cleanup(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, original_mwindow_file_limit));
+}
+
+#ifdef GIT_THREADS
+static void *get_status(void *arg)
+{
+ const char *repo_path = (const char *)arg;
+ git_repository *repo;
+ git_status_list *status;
+
+ cl_git_pass(git_repository_open(&repo, repo_path));
+ cl_git_pass(git_status_list_new(&status, repo, NULL));
+ git_status_list_free(status);
+ git_repository_free(repo);
+
+ return NULL;
+}
+#endif
+
+void test_pack_threadsafety__open_repo_in_multiple_threads(void)
+{
+#ifdef GIT_THREADS
+ const char *repo_path = cl_fixture("../..");
+ git_repository *repo;
+ git_thread threads[8];
+ size_t i;
+
+ /* If we can't open the libgit2 repo or if it isn't a full repo
+ * with proper history, just skip this test */
+ if (git_repository_open(&repo, repo_path) < 0)
+ cl_skip();
+ if (git_repository_is_shallow(repo))
+ cl_skip();
+ git_repository_free(repo);
+
+ for (i = 0; i < ARRAY_SIZE(threads); i++)
+ git_thread_create(&threads[i], get_status, (void *)repo_path);
+ for (i = 0; i < ARRAY_SIZE(threads); i++)
+ git_thread_join(&threads[i], NULL);
+#else
+ cl_skip();
+#endif
+}
#include "clar_libgit2.h"
#include "path.h"
+void test_path_core__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
static void test_make_relative(
const char *expected_path,
const char *path,
test_make_relative("/path/to/foo.c", "/path/to/foo.c", "d:/path/to", GIT_ENOTFOUND);
test_make_relative("d:/path/to/foo.c", "d:/path/to/foo.c", "/path/to", GIT_ENOTFOUND);
-
+
test_make_relative("/path/to/foo.c", "/path/to/foo.c", "not-a-rooted-path", GIT_ENOTFOUND);
test_make_relative("not-a-rooted-path", "not-a-rooted-path", "/path/to", GIT_ENOTFOUND);
-
+
test_make_relative("/path", "/path", "pathtofoo", GIT_ENOTFOUND);
test_make_relative("path", "path", "pathtofoo", GIT_ENOTFOUND);
}
void test_path_core__isvalid_standard(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/file.txt", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.file", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar/file.txt", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar/.file", 0, 0));
}
void test_path_core__isvalid_empty_dir_component(void)
{
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo//bar", 0, 0));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo//bar", 0, 0));
/* leading slash */
- cl_assert_equal_b(false, git_path_isvalid(NULL, "/", 0, 0));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "/foo", 0, 0));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "/foo/bar", 0, 0));
+ cl_assert_equal_b(false, git_path_validate(NULL, "/", 0, 0));
+ cl_assert_equal_b(false, git_path_validate(NULL, "/foo", 0, 0));
+ cl_assert_equal_b(false, git_path_validate(NULL, "/foo/bar", 0, 0));
/* trailing slash */
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/", 0, 0));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/", 0, 0));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/", 0, 0));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/bar/", 0, 0));
}
void test_path_core__isvalid_dot_and_dotdot(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "./foo", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "./foo", 0, 0));
-
- cl_assert_equal_b(true, git_path_isvalid(NULL, "..", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "../foo", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/..", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "../foo", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".", 0, GIT_PATH_REJECT_TRAVERSAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "./foo", 0, GIT_PATH_REJECT_TRAVERSAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.", 0, GIT_PATH_REJECT_TRAVERSAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "./foo", 0, GIT_PATH_REJECT_TRAVERSAL));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "..", 0, GIT_PATH_REJECT_TRAVERSAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "../foo", 0, GIT_PATH_REJECT_TRAVERSAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/..", 0, GIT_PATH_REJECT_TRAVERSAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "../foo", 0, GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "./foo", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/.", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "./foo", 0, 0));
+
+ cl_assert_equal_b(true, git_path_validate(NULL, "..", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "../foo", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/..", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "../foo", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, ".", 0, GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "./foo", 0, GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/.", 0, GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "./foo", 0, GIT_PATH_REJECT_TRAVERSAL));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "..", 0, GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "../foo", 0, GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/..", 0, GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "../foo", 0, GIT_PATH_REJECT_TRAVERSAL));
}
void test_path_core__isvalid_dot_git(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".git", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".git/foo", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.git", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.git/bar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.GIT/bar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.Git", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
-
- cl_assert_equal_b(true, git_path_isvalid(NULL, "!git", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/!git", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "!git/bar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".tig", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.tig", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".tig/bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".git", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".git/foo", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/.git", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/.git/bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/.GIT/bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar/.Git", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, ".git", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".git/foo", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/.git", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/.git/bar", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/.GIT/bar", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/bar/.Git", 0, GIT_PATH_REJECT_DOT_GIT_LITERAL));
+
+ cl_assert_equal_b(true, git_path_validate(NULL, "!git", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/!git", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "!git/bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".tig", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/.tig", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".tig/bar", 0, 0));
}
void test_path_core__isvalid_backslash(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo\\file.txt", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar\\file.txt", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar\\", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo\\file.txt", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar\\file.txt", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar\\", 0, 0));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo\\file.txt", 0, GIT_PATH_REJECT_BACKSLASH));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar\\file.txt", 0, GIT_PATH_REJECT_BACKSLASH));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar\\", 0, GIT_PATH_REJECT_BACKSLASH));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo\\file.txt", 0, GIT_PATH_REJECT_BACKSLASH));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/bar\\file.txt", 0, GIT_PATH_REJECT_BACKSLASH));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/bar\\", 0, GIT_PATH_REJECT_BACKSLASH));
}
void test_path_core__isvalid_trailing_dot(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo.", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo...", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar.", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo./bar", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo.", 0, GIT_PATH_REJECT_TRAILING_DOT));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo...", 0, GIT_PATH_REJECT_TRAILING_DOT));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar.", 0, GIT_PATH_REJECT_TRAILING_DOT));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo./bar", 0, GIT_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo.", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo...", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar.", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo./bar", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo.", 0, GIT_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo...", 0, GIT_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/bar.", 0, GIT_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo./bar", 0, GIT_PATH_REJECT_TRAILING_DOT));
}
void test_path_core__isvalid_trailing_space(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo ", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo ", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar ", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, " ", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo /bar", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo ", 0, GIT_PATH_REJECT_TRAILING_SPACE));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo ", 0, GIT_PATH_REJECT_TRAILING_SPACE));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar ", 0, GIT_PATH_REJECT_TRAILING_SPACE));
- cl_assert_equal_b(false, git_path_isvalid(NULL, " ", 0, GIT_PATH_REJECT_TRAILING_SPACE));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo /bar", 0, GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo ", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo ", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar ", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, " ", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo /bar", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo ", 0, GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo ", 0, GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/bar ", 0, GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_path_validate(NULL, " ", 0, GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo /bar", 0, GIT_PATH_REJECT_TRAILING_SPACE));
}
void test_path_core__isvalid_trailing_colon(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo:", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar:", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ":", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "foo:/bar", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo:", 0, GIT_PATH_REJECT_TRAILING_COLON));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar:", 0, GIT_PATH_REJECT_TRAILING_COLON));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ":", 0, GIT_PATH_REJECT_TRAILING_COLON));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "foo:/bar", 0, GIT_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo:", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo/bar:", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, ":", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "foo:/bar", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo:", 0, GIT_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo/bar:", 0, GIT_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_path_validate(NULL, ":", 0, GIT_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_path_validate(NULL, "foo:/bar", 0, GIT_PATH_REJECT_TRAILING_COLON));
}
void test_path_core__isvalid_dotgit_ntfs(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".git", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".git ", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".git.", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".git.. .", 0, 0));
-
- cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1 ", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1.", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1.. .", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".git ", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".git.", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".git.. .", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1 ", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1.", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1.. .", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".git", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".git ", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".git.", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".git.. .", 0, 0));
+
+ cl_assert_equal_b(true, git_path_validate(NULL, "git~1", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "git~1 ", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "git~1.", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "git~1.. .", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, ".git", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".git ", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".git.", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".git.. .", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "git~1", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "git~1 ", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "git~1.", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "git~1.. .", 0, GIT_PATH_REJECT_DOT_GIT_NTFS));
}
void test_path_core__isvalid_dos_paths(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux:", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.asdf", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.asdf\\zippy", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux:asdf\\foobar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "con", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "prn", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "nul", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "aux", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "aux:", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.asdf", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.asdf\\zippy", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "aux:asdf\\foobar", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "con", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "prn", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "nul", 0, GIT_PATH_REJECT_DOS_PATHS));
-
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux1", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux1", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "auxn", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "aux\\foo", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux.", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux:", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux.asdf", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux.asdf\\zippy", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux:asdf\\foobar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "con", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "prn", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "nul", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "aux", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "aux.", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "aux:", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "aux.asdf", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "aux.asdf\\zippy", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "aux:asdf\\foobar", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "con", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "prn", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "nul", 0, GIT_PATH_REJECT_DOS_PATHS));
+
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux1", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux1", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "auxn", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "aux\\foo", 0, GIT_PATH_REJECT_DOS_PATHS));
}
void test_path_core__isvalid_dos_paths_withnum(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com1", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com1:", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.asdf", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.asdf\\zippy", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com1:asdf\\foobar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com1\\foo", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt1", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "com1", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "com1:", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.asdf", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.asdf\\zippy", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "com1:asdf\\foobar", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "com1/foo", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "lpt1", 0, GIT_PATH_REJECT_DOS_PATHS));
-
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com0", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com0", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com10", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com10", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "comn", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "com1\\foo", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt0", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt10", 0, GIT_PATH_REJECT_DOS_PATHS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "lptn", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com1", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com1.", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com1:", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com1.asdf", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com1.asdf\\zippy", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com1:asdf\\foobar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com1\\foo", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "lpt1", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "com1", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "com1.", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "com1:", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "com1.asdf", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "com1.asdf\\zippy", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "com1:asdf\\foobar", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "com1/foo", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "lpt1", 0, GIT_PATH_REJECT_DOS_PATHS));
+
+ cl_assert_equal_b(true, git_path_validate(NULL, "com0", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com0", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com10", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com10", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "comn", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "com1\\foo", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "lpt0", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "lpt10", 0, GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "lptn", 0, GIT_PATH_REJECT_DOS_PATHS));
}
void test_path_core__isvalid_nt_chars(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\001foo", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\037bar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf<bar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf>foo", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf:foo", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\"bar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf|foo", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf?bar", 0, 0));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf*bar", 0, 0));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\001foo", 0, GIT_PATH_REJECT_NT_CHARS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\037bar", 0, GIT_PATH_REJECT_NT_CHARS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf<bar", 0, GIT_PATH_REJECT_NT_CHARS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf>foo", 0, GIT_PATH_REJECT_NT_CHARS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf:foo", 0, GIT_PATH_REJECT_NT_CHARS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\"bar", 0, GIT_PATH_REJECT_NT_CHARS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf|foo", 0, GIT_PATH_REJECT_NT_CHARS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf?bar", 0, GIT_PATH_REJECT_NT_CHARS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf*bar", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf\001foo", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf\037bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf<bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf>foo", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf:foo", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf\"bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf|foo", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf?bar", 0, 0));
+ cl_assert_equal_b(true, git_path_validate(NULL, "asdf*bar", 0, 0));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf\001foo", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf\037bar", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf<bar", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf>foo", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf:foo", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf\"bar", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf|foo", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf?bar", 0, GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "asdf*bar", 0, GIT_PATH_REJECT_NT_CHARS));
}
void test_path_core__isvalid_dotgit_with_hfs_ignorables(void)
{
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".git\xe2\x80\x8c", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".gi\xe2\x80\x8dT", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".g\xe2\x80\x8eIt", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".\xe2\x80\x8fgIt", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x80\xaa.gIt", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
-
- cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x80\xab.\xe2\x80\xacG\xe2\x80\xadI\xe2\x80\xaet", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x81\xab.\xe2\x80\xaaG\xe2\x81\xabI\xe2\x80\xact", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x81\xad.\xe2\x80\xaeG\xef\xbb\xbfIT", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
-
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".g", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, " .git", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "..git\xe2\x80\x8c", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\xe2\x80\x8dT.", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".g\xe2\x80It", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".\xe2gIt", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, "\xe2\x80\xaa.gi", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\x80\x8dT", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\x8dT", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".g\xe2i\x80T\x8e", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\x80\xbf", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\xab\x81", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".git", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".git\xe2\x80\x8c", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".gi\xe2\x80\x8dT", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".g\xe2\x80\x8eIt", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".\xe2\x80\x8fgIt", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "\xe2\x80\xaa.gIt", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+
+ cl_assert_equal_b(false, git_path_validate(NULL, "\xe2\x80\xab.\xe2\x80\xacG\xe2\x80\xadI\xe2\x80\xaet", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "\xe2\x81\xab.\xe2\x80\xaaG\xe2\x81\xabI\xe2\x80\xact", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, "\xe2\x81\xad.\xe2\x80\xaeG\xef\xbb\xbfIT", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+
+ cl_assert_equal_b(true, git_path_validate(NULL, ".", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".g", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".gi", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, " .git", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "..git\xe2\x80\x8c", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".gi\xe2\x80\x8dT.", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".g\xe2\x80It", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".\xe2gIt", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, "\xe2\x80\xaa.gi", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".gi\x80\x8dT", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".gi\x8dT", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".g\xe2i\x80T\x8e", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".git\xe2\x80\xbf", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".git\xe2\xab\x81", 0, GIT_PATH_REJECT_DOT_GIT_HFS));
+}
+
+void test_path_core__validate_workdir(void)
+{
+ cl_must_pass(git_path_validate_workdir(NULL, "/foo/bar"));
+ cl_must_pass(git_path_validate_workdir(NULL, "C:\\Foo\\Bar"));
+ cl_must_pass(git_path_validate_workdir(NULL, "\\\\?\\C:\\Foo\\Bar"));
+ cl_must_pass(git_path_validate_workdir(NULL, "\\\\?\\C:\\Foo\\Bar"));
+ cl_must_pass(git_path_validate_workdir(NULL, "\\\\?\\UNC\\server\\C$\\folder"));
+
+#ifdef GIT_WIN32
+ /*
+ * In the absense of a repo configuration, 259 character paths
+ * succeed. >= 260 character paths fail.
+ */
+ cl_must_pass(git_path_validate_workdir(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\ok.txt"));
+ cl_must_pass(git_path_validate_workdir(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\260.txt"));
+ cl_must_fail(git_path_validate_workdir(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\longer_than_260.txt"));
+
+ /* count characters, not bytes */
+ cl_must_pass(git_path_validate_workdir(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\\260.txt"));
+ cl_must_fail(git_path_validate_workdir(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\\long.txt"));
+#else
+ cl_must_pass(git_path_validate_workdir(NULL, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/ok.txt"));
+ cl_must_pass(git_path_validate_workdir(NULL, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/260.txt"));
+ cl_must_pass(git_path_validate_workdir(NULL, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/longer_than_260.txt"));
+ cl_must_pass(git_path_validate_workdir(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\\260.txt"));
+ cl_must_pass(git_path_validate_workdir(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\\long.txt"));
+#endif
+}
+
+void test_path_core__validate_workdir_with_core_longpath(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+ git_config *config;
+
+ repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_git_pass(git_repository_open(&repo, "empty_bare.git"));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ /* fail by default */
+ cl_must_fail(git_path_validate_workdir(repo, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/longer_than_260.txt"));
+
+ /* set core.longpaths explicitly on */
+ cl_git_pass(git_config_set_bool(config, "core.longpaths", 1));
+ cl_must_pass(git_path_validate_workdir(repo, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/longer_than_260.txt"));
+
+ /* set core.longpaths explicitly off */
+ cl_git_pass(git_config_set_bool(config, "core.longpaths", 0));
+ cl_must_fail(git_path_validate_workdir(repo, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/longer_than_260.txt"));
+
+ git_config_free(config);
+ git_repository_free(repo);
+#endif
}
static void test_join_unrooted(
void test_path_dotgit__dotgit_modules_symlink(void)
{
- cl_assert_equal_b(true, git_path_isvalid(NULL, ".gitmodules", 0, GIT_PATH_REJECT_DOT_GIT_HFS|GIT_PATH_REJECT_DOT_GIT_NTFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".gitmodules", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_HFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".gitmodules", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_NTFS));
- cl_assert_equal_b(false, git_path_isvalid(NULL, ".gitmodules . .::$DATA", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(true, git_path_validate(NULL, ".gitmodules", 0, GIT_PATH_REJECT_DOT_GIT_HFS|GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".gitmodules", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".gitmodules", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_validate(NULL, ".gitmodules . .::$DATA", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_NTFS));
}
#ifdef GIT_WIN32
git_win32_path path_utf16;
- test_utf8_to_utf16("C:\\This path is 259 chars and is the max length in windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij",
- L"\\\\?\\C:\\This path is 259 chars and is the max length in windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij");
- test_utf8_to_utf16("\\\\unc\\paths may also be 259 characters including the server\\123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij",
- L"\\\\?\\UNC\\unc\\paths may also be 259 characters including the server\\123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij");
+ test_utf8_to_utf16("C:\\This path is 261 characters which is fine for our path handling functions which cope with paths longer than MAX_PATH\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghijk",
+ L"\\\\?\\C:\\This path is 261 characters which is fine for our path handling functions which cope with paths longer than MAX_PATH\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghijk");
+
+ cl_check_fail(git_win32_path_from_utf8(path_utf16, "C:\\This path is 4097 chars and exceeds our maximum path length on Windows which is limited to 4096 characters\\alas\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij01"));
- cl_check_fail(git_win32_path_from_utf8(path_utf16, "C:\\This path is 260 chars and is sadly too long for windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij"));
- cl_check_fail(git_win32_path_from_utf8(path_utf16, "\\\\unc\\paths are also bound by 260 character restrictions\\including the server name portion\\bcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij"));
#endif
}
cl_git_sandbox_cleanup();
}
-static const char *expected_commit_content = "tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+static int create_cb_passthrough(
+ git_oid *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[],
+ void *payload)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(author);
+ GIT_UNUSED(committer);
+ GIT_UNUSED(message_encoding);
+ GIT_UNUSED(message);
+ GIT_UNUSED(tree);
+ GIT_UNUSED(parent_count);
+ GIT_UNUSED(parents);
+ GIT_UNUSED(payload);
+
+ return GIT_PASSTHROUGH;
+}
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__passthrough_create_cb(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_id;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+ git_commit *commit;
+ const char *expected_commit_raw_header = "tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+parent f87d14a4a236582a0278a916340a793714256864\n\
+author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
+committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n";
+
+ rebase_opts.commit_create_cb = create_cb_passthrough;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_oid_fromstr(&expected_id, "129183968a65abd6c52da35bff43325001bfc630");
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_s(expected_commit_raw_header, git_commit_raw_header(commit));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_commit_free(commit);
+ git_rebase_free(rebase);
+}
+
+int create_cb_signed_gpg(
+ git_oid *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[],
+ void *payload)
+{
+ git_buf commit_content = GIT_BUF_INIT;
+ const char *gpg_signature = "-----BEGIN PGP SIGNATURE-----\n\
+\n\
+iQIzBAEBCgAdFiEEgVlDEfSlmKn0fvGgK++h5T2/ctIFAlwZcrAACgkQK++h5T2/\n\
+ctIPVhAA42RyZhMdKl5Bm0KtQco2scsukIg2y7tjSwhti91zDu3HQgpusjjo0fQx\n\
+ZzB+OrmlvQ9CDcGpZ0THIzXD8GRJoDMPqdrvZVrBWkGcHvw7/YPA8skzsjkauJ8W\n\
+7lzF5LCuHSS6OUmPT/+5hEHPin5PB3zhfszyC+Q7aujnIuPJMrKiMnUa+w1HWifM\n\
+km49OOygQ9S6NQoVuEQede22+c76DlDL7yFghGoo1f0sKCE/9LW6SEnwI/bWv9eo\n\
+nom5vOPrvQeJiYCQk+2DyWo8RdSxINtY+G9bPE4RXm+6ZgcXECPm9TYDIWpL36fC\n\
+jvtGLs98woWFElOziBMp5Tb630GMcSI+q5ivHfJ3WS5NKLYLHBNK4iSFN0/dgAnB\n\
+dj6GcKXKWnIBWn6ZM4o40pcM5KSRUUCLtA0ZmjJH4c4zx3X5fUxd+enwkf3e9VZO\n\
+fNKC/+xfq6NfoPUPK9+UnchHpJaJw7RG5tZS+sWCz2xpQ1y3/o49xImNyM3wnpvB\n\
+cRAZabqIHpZa9/DIUkELOtCzln6niqkjRgg3M/YCCNznwV+0RNgz87VtyTPerdef\n\
+xrqn0+ROMF6ebVqIs6PPtuPkxnAJu7TMKXVB5rFnAewS24e6cIGFzeIA7810py3l\n\
+cttVRsdOoego+fiy08eFE+aJIeYiINRGhqOBTsuqG4jIdpdKxPE=\n\
+=KbsY\n\
+-----END PGP SIGNATURE-----";
+
+ git_repository *repo = (git_repository *)payload;
+ int error;
+
+ if ((error = git_commit_create_buffer(&commit_content,
+ repo, author, committer, message_encoding, message,
+ tree, parent_count, parents)) < 0)
+ goto done;
+
+ error = git_commit_create_with_signature(out, repo,
+ commit_content.ptr,
+ gpg_signature,
+ NULL);
+
+done:
+ git_buf_dispose(&commit_content);
+ return error;
+}
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__create_gpg_signed(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_id;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+ git_commit *commit;
+ const char *expected_commit_raw_header = "tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+parent f87d14a4a236582a0278a916340a793714256864\n\
+author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
+committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n\
+gpgsig -----BEGIN PGP SIGNATURE-----\n\
+ \n\
+ iQIzBAEBCgAdFiEEgVlDEfSlmKn0fvGgK++h5T2/ctIFAlwZcrAACgkQK++h5T2/\n\
+ ctIPVhAA42RyZhMdKl5Bm0KtQco2scsukIg2y7tjSwhti91zDu3HQgpusjjo0fQx\n\
+ ZzB+OrmlvQ9CDcGpZ0THIzXD8GRJoDMPqdrvZVrBWkGcHvw7/YPA8skzsjkauJ8W\n\
+ 7lzF5LCuHSS6OUmPT/+5hEHPin5PB3zhfszyC+Q7aujnIuPJMrKiMnUa+w1HWifM\n\
+ km49OOygQ9S6NQoVuEQede22+c76DlDL7yFghGoo1f0sKCE/9LW6SEnwI/bWv9eo\n\
+ nom5vOPrvQeJiYCQk+2DyWo8RdSxINtY+G9bPE4RXm+6ZgcXECPm9TYDIWpL36fC\n\
+ jvtGLs98woWFElOziBMp5Tb630GMcSI+q5ivHfJ3WS5NKLYLHBNK4iSFN0/dgAnB\n\
+ dj6GcKXKWnIBWn6ZM4o40pcM5KSRUUCLtA0ZmjJH4c4zx3X5fUxd+enwkf3e9VZO\n\
+ fNKC/+xfq6NfoPUPK9+UnchHpJaJw7RG5tZS+sWCz2xpQ1y3/o49xImNyM3wnpvB\n\
+ cRAZabqIHpZa9/DIUkELOtCzln6niqkjRgg3M/YCCNznwV+0RNgz87VtyTPerdef\n\
+ xrqn0+ROMF6ebVqIs6PPtuPkxnAJu7TMKXVB5rFnAewS24e6cIGFzeIA7810py3l\n\
+ cttVRsdOoego+fiy08eFE+aJIeYiINRGhqOBTsuqG4jIdpdKxPE=\n\
+ =KbsY\n\
+ -----END PGP SIGNATURE-----\n";
+
+ rebase_opts.commit_create_cb = create_cb_signed_gpg;
+ rebase_opts.payload = repo;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_oid_fromstr(&expected_id, "bf78348e45c8286f52b760f1db15cb6da030f2ef");
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_s(expected_commit_raw_header, git_commit_raw_header(commit));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_commit_free(commit);
+ git_rebase_free(rebase);
+}
+
+static int create_cb_error(
+ git_oid *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[],
+ void *payload)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(author);
+ GIT_UNUSED(committer);
+ GIT_UNUSED(message_encoding);
+ GIT_UNUSED(message);
+ GIT_UNUSED(tree);
+ GIT_UNUSED(parent_count);
+ GIT_UNUSED(parents);
+ GIT_UNUSED(payload);
+
+ return GIT_EUSER;
+}
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__create_propagates_error(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_oid commit_id;
+ git_rebase_operation *rebase_operation;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+
+ rebase_opts.commit_create_cb = create_cb_error;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_fail_with(GIT_EUSER, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_rebase_free(rebase);
+}
+
+#ifndef GIT_DEPRECATE_HARD
+static const char *expected_commit_content = "\
+tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
parent f87d14a4a236582a0278a916340a793714256864\n\
author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n\
cl_assert_equal_p(NULL, payload);
return GIT_PASSTHROUGH;
}
+#endif /* !GIT_DEPRECATE_HARD */
/* git checkout gravy ; git rebase --merge veal */
void test_rebase_sign__passthrough_signing_cb(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_rebase *rebase;
git_reference *branch_ref, *upstream_ref;
git_annotated_commit *branch_head, *upstream_head;
git_annotated_commit_free(upstream_head);
git_commit_free(commit);
git_rebase_free(rebase);
+#endif /* !GIT_DEPRECATE_HARD */
}
+#ifndef GIT_DEPRECATE_HARD
int signing_cb_gpg(
git_buf *signature,
git_buf *signature_field,
const char *commit_content,
void *payload)
{
- const char *gpg_signature = "-----BEGIN PGP SIGNATURE-----\n\
+ const char *gpg_signature = "\
+-----BEGIN PGP SIGNATURE-----\n\
\n\
iQIzBAEBCgAdFiEEgVlDEfSlmKn0fvGgK++h5T2/ctIFAlwZcrAACgkQK++h5T2/\n\
ctIPVhAA42RyZhMdKl5Bm0KtQco2scsukIg2y7tjSwhti91zDu3HQgpusjjo0fQx\n\
cl_git_pass(git_buf_set(signature, gpg_signature, strlen(gpg_signature) + 1));
return GIT_OK;
}
+#endif /* !GIT_DEPRECATE_HARD */
/* git checkout gravy ; git rebase --merge veal */
void test_rebase_sign__gpg_with_no_field(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_rebase *rebase;
git_reference *branch_ref, *upstream_ref;
git_annotated_commit *branch_head, *upstream_head;
git_oid commit_id, expected_id;
git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
git_commit *commit;
- const char *expected_commit_raw_header = "tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+ const char *expected_commit_raw_header = "\
+tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
parent f87d14a4a236582a0278a916340a793714256864\n\
author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n\
git_annotated_commit_free(upstream_head);
git_commit_free(commit);
git_rebase_free(rebase);
+#endif /* !GIT_DEPRECATE_HARD */
}
+#ifndef GIT_DEPRECATE_HARD
int signing_cb_magic_field(
git_buf *signature,
git_buf *signature_field,
return GIT_OK;
}
+#endif /* !GIT_DEPRECATE_HARD */
/* git checkout gravy ; git rebase --merge veal */
void test_rebase_sign__custom_signature_field(void)
{
+#ifndef GIT_DEPRECATE_HARD
git_rebase *rebase;
git_reference *branch_ref, *upstream_ref;
git_annotated_commit *branch_head, *upstream_head;
git_oid commit_id, expected_id;
git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
git_commit *commit;
- const char *expected_commit_raw_header = "tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+ const char *expected_commit_raw_header = "\
+tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
parent f87d14a4a236582a0278a916340a793714256864\n\
author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n\
git_annotated_commit_free(upstream_head);
git_commit_free(commit);
git_rebase_free(rebase);
+#endif /* !GIT_DEPRECATE_HARD */
}
git_reference_free(new_ref);
git_reference_free(ref);
}
+
+void test_refs_basic__longpaths(void)
+{
+#ifdef GIT_WIN32
+ const char *base;
+ size_t base_len, extra_len;
+ ssize_t remain_len, i;
+ git_buf refname = GIT_BUF_INIT;
+ git_reference *one = NULL, *two = NULL;
+ git_oid id;
+
+ cl_git_pass(git_oid_fromstr(&id, "099fabac3a9ea935598528c27f866e34089c2eff"));
+
+ base = git_repository_path(g_repo);
+ base_len = git_utf8_char_length(base, strlen(base));
+ extra_len = CONST_STRLEN("logs/refs/heads/") + CONST_STRLEN(".lock");
+
+ remain_len = (ssize_t)MAX_PATH - (base_len + extra_len);
+ cl_assert(remain_len > 0);
+
+ cl_git_pass(git_buf_puts(&refname, "refs/heads/"));
+
+ for (i = 0; i < remain_len; i++) {
+ cl_git_pass(git_buf_putc(&refname, 'a'));
+ }
+
+ /*
+ * The full path to the reflog lockfile is 260 characters,
+ * this is permitted.
+ */
+ cl_git_pass(git_reference_create(&one, g_repo, refname.ptr, &id, 0, NULL));
+
+ /* Adding one more character gives us a path that is too long. */
+ cl_git_pass(git_buf_putc(&refname, 'z'));
+ cl_git_fail(git_reference_create(&two, g_repo, refname.ptr, &id, 0, NULL));
+
+ git_reference_free(one);
+ git_reference_free(two);
+ git_buf_dispose(&refname);
+#endif
+}
cl_git_pass(git_reference_lookup(&ref,repo,"refs/notes/fanout"));
cl_git_fail(git_branch_name(&name,ref));
}
+
+static int name_is_valid(const char *name)
+{
+ int valid;
+ cl_git_pass(git_branch_name_is_valid(&valid, name));
+ return valid;
+}
+
+void test_refs_branches_is_name_valid(void)
+{
+ cl_assert_equal_i(true, name_is_valid("master"));
+ cl_assert_equal_i(true, name_is_valid("test/master"));
+
+ cl_assert_equal_i(false, name_is_valid(""));
+ cl_assert_equal_i(false, name_is_valid("HEAD"));
+ cl_assert_equal_i(false, name_is_valid("-dash"));
+}
git_buf_dispose(&buf);
}
+void test_refs_branches_upstream__upstream_merge(void)
+{
+ git_reference *branch;
+ git_repository *repository;
+ git_buf buf = GIT_BUF_INIT;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ /* check repository */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "test/master"));
+
+ assert_config_entry_value(repository, "branch.test.remote", "test");
+ assert_config_entry_value(repository, "branch.test.merge", "refs/heads/master");
+
+ git_reference_free(branch);
+
+ /* check merge branch */
+ cl_git_pass(git_branch_upstream_merge(&buf, repository, "refs/heads/test"));
+ cl_assert_equal_s("refs/heads/master", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_sandbox_cleanup();
+}
+
void test_refs_branches_upstream__upstream_remote_empty_value(void)
{
git_repository *repository;
#include "clar_libgit2.h"
+static bool is_valid_name(const char *name)
+{
+ int valid;
+ cl_git_pass(git_reference_name_is_valid(&valid, name));
+ return valid;
+}
+
void test_refs_isvalidname__can_detect_invalid_formats(void)
{
- cl_assert_equal_i(false, git_reference_is_valid_name("refs/tags/0.17.0^{}"));
- cl_assert_equal_i(false, git_reference_is_valid_name("TWO/LEVELS"));
- cl_assert_equal_i(false, git_reference_is_valid_name("ONE.LEVEL"));
- cl_assert_equal_i(false, git_reference_is_valid_name("HEAD/"));
- cl_assert_equal_i(false, git_reference_is_valid_name("NO_TRAILING_UNDERSCORE_"));
- cl_assert_equal_i(false, git_reference_is_valid_name("_NO_LEADING_UNDERSCORE"));
- cl_assert_equal_i(false, git_reference_is_valid_name("HEAD/aa"));
- cl_assert_equal_i(false, git_reference_is_valid_name("lower_case"));
- cl_assert_equal_i(false, git_reference_is_valid_name("/stupid/name/master"));
- cl_assert_equal_i(false, git_reference_is_valid_name("/"));
- cl_assert_equal_i(false, git_reference_is_valid_name("//"));
- cl_assert_equal_i(false, git_reference_is_valid_name(""));
- cl_assert_equal_i(false, git_reference_is_valid_name("refs/heads/sub.lock/webmatrix"));
+ cl_assert_equal_i(false, is_valid_name("refs/tags/0.17.0^{}"));
+ cl_assert_equal_i(false, is_valid_name("TWO/LEVELS"));
+ cl_assert_equal_i(false, is_valid_name("ONE.LEVEL"));
+ cl_assert_equal_i(false, is_valid_name("HEAD/"));
+ cl_assert_equal_i(false, is_valid_name("NO_TRAILING_UNDERSCORE_"));
+ cl_assert_equal_i(false, is_valid_name("_NO_LEADING_UNDERSCORE"));
+ cl_assert_equal_i(false, is_valid_name("HEAD/aa"));
+ cl_assert_equal_i(false, is_valid_name("lower_case"));
+ cl_assert_equal_i(false, is_valid_name("/stupid/name/master"));
+ cl_assert_equal_i(false, is_valid_name("/"));
+ cl_assert_equal_i(false, is_valid_name("//"));
+ cl_assert_equal_i(false, is_valid_name(""));
+ cl_assert_equal_i(false, is_valid_name("refs/heads/sub.lock/webmatrix"));
}
void test_refs_isvalidname__wont_hopefully_choke_on_valid_formats(void)
{
- cl_assert_equal_i(true, git_reference_is_valid_name("refs/tags/0.17.0"));
- cl_assert_equal_i(true, git_reference_is_valid_name("refs/LEVELS"));
- cl_assert_equal_i(true, git_reference_is_valid_name("HEAD"));
- cl_assert_equal_i(true, git_reference_is_valid_name("ONE_LEVEL"));
- cl_assert_equal_i(true, git_reference_is_valid_name("refs/stash"));
- cl_assert_equal_i(true, git_reference_is_valid_name("refs/remotes/origin/bim_with_3d@11296"));
- cl_assert_equal_i(true, git_reference_is_valid_name("refs/master{yesterday"));
- cl_assert_equal_i(true, git_reference_is_valid_name("refs/master}yesterday"));
- cl_assert_equal_i(true, git_reference_is_valid_name("refs/master{yesterday}"));
+ cl_assert_equal_i(true, is_valid_name("refs/tags/0.17.0"));
+ cl_assert_equal_i(true, is_valid_name("refs/LEVELS"));
+ cl_assert_equal_i(true, is_valid_name("HEAD"));
+ cl_assert_equal_i(true, is_valid_name("ONE_LEVEL"));
+ cl_assert_equal_i(true, is_valid_name("refs/stash"));
+ cl_assert_equal_i(true, is_valid_name("refs/remotes/origin/bim_with_3d@11296"));
+ cl_assert_equal_i(true, is_valid_name("refs/master{yesterday"));
+ cl_assert_equal_i(true, is_valid_name("refs/master}yesterday"));
+ cl_assert_equal_i(true, is_valid_name("refs/master{yesterday}"));
}
cl_git_sandbox_cleanup();
}
+void test_refs_races__create_matching_zero_old(void)
+{
+ git_reference *ref;
+ git_oid id, zero_id;
+
+ git_oid_fromstr(&id, commit_id);
+ git_oid_fromstr(&zero_id, "0000000000000000000000000000000000000000");
+
+ cl_git_fail(git_reference_create_matching(&ref, g_repo, refname, &id, 1, &zero_id, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_create_matching(&ref, g_repo, other_refname, &id, 1, &zero_id, NULL));
+ git_reference_free(ref);
+
+ cl_git_fail(git_reference_create_matching(&ref, g_repo, other_refname, &id, 1, &zero_id, NULL));
+ git_reference_free(ref);
+}
+
void test_refs_races__create_matching(void)
{
git_reference *ref, *ref2, *ref3;
const char *spec,
const char *expected_left,
const char *expected_right,
- git_revparse_mode_t expected_flags,
+ git_revspec_t expected_flags,
git_repository *repo)
{
git_revspec revspec;
static void test_rangelike(const char *rangelike,
const char *expected_left,
const char *expected_right,
- git_revparse_mode_t expected_revparseflags)
+ git_revspec_t expected_revparseflags)
{
char objstr[64] = {0};
git_revspec revspec;
const char *spec,
const char *expected_left,
const char *expected_right,
- git_revparse_mode_t expected_flags)
+ git_revspec_t expected_flags)
{
test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo);
}
test_rangelike("be3563a^1..be3563a",
"9fd738e8f7967c078dceed8190330fc8648ee56a",
"be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
- GIT_REVPARSE_RANGE);
+ GIT_REVSPEC_RANGE);
test_rangelike("be3563a^1...be3563a",
"9fd738e8f7967c078dceed8190330fc8648ee56a",
"be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
- GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
test_rangelike("be3563a^1.be3563a", NULL, NULL, 0);
}
void test_refs_revparse__parses_range_operator(void)
{
- test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVPARSE_SINGLE);
+ test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE);
test_id("HEAD~3..HEAD",
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- GIT_REVPARSE_RANGE);
+ GIT_REVSPEC_RANGE);
test_id("HEAD~3...HEAD",
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
test_id("HEAD~3..",
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- GIT_REVPARSE_RANGE);
+ GIT_REVSPEC_RANGE);
test_id("HEAD~3...",
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
test_id("..HEAD~3",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
- GIT_REVPARSE_RANGE);
+ GIT_REVSPEC_RANGE);
test_id("...HEAD~3",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
- GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
test_id("...",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
test_invalid_revspec("..");
}
--- /dev/null
+#include "clar_libgit2.h"
+
+static int name_is_valid(const char *name)
+{
+ int valid;
+ cl_git_pass(git_tag_name_is_valid(&valid, name));
+ return valid;
+}
+
+void test_refs_tags_is_name_valid(void)
+{
+ cl_assert_equal_i(true, name_is_valid("sometag"));
+ cl_assert_equal_i(true, name_is_valid("test/sometag"));
+
+ cl_assert_equal_i(false, name_is_valid(""));
+ cl_assert_equal_i(false, name_is_valid("-dash"));
+}
--- /dev/null
+#include "../clar_libgit2.h"
+
+#include "remote.h"
+#include "repository.h"
+
+static git_repository *repo1;
+static git_repository *repo2;
+static char* repo1_path;
+static char* repo2_path;
+
+static const char *REPO1_REFNAME = "refs/heads/main";
+static const char *REPO2_REFNAME = "refs/remotes/repo1/main";
+static char *FORCE_FETCHSPEC = "+refs/heads/main:refs/remotes/repo1/main";
+static char *NON_FORCE_FETCHSPEC = "refs/heads/main:refs/remotes/repo1/main";
+
+void test_remote_fetch__initialize(void) {
+ git_config *c;
+ git_buf repo1_path_buf = GIT_BUF_INIT;
+ git_buf repo2_path_buf = GIT_BUF_INIT;
+ const char *sandbox = clar_sandbox_path();
+
+ cl_git_pass(git_buf_joinpath(&repo1_path_buf, sandbox, "fetchtest_repo1"));
+ repo1_path = git_buf_detach(&repo1_path_buf);
+ cl_git_pass(git_repository_init(&repo1, repo1_path, true));
+
+ cl_git_pass(git_buf_joinpath(&repo2_path_buf, sandbox, "fetchtest_repo2"));
+ repo2_path = git_buf_detach(&repo2_path_buf);
+ cl_git_pass(git_repository_init(&repo2, repo2_path, true));
+
+ cl_git_pass(git_repository_config(&c, repo1));
+ cl_git_pass(git_config_set_string(c, "user.email", "some@email"));
+ cl_git_pass(git_config_set_string(c, "user.name", "some@name"));
+ git_config_free(c);
+ git_buf_dispose(&repo1_path_buf);
+ git_buf_dispose(&repo2_path_buf);
+}
+
+void test_remote_fetch__cleanup(void) {
+ git_repository_free(repo1);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_futils_rmdir_r(repo1_path, NULL, GIT_RMDIR_REMOVE_FILES));
+ free(repo1_path);
+
+ cl_git_pass(git_futils_rmdir_r(repo2_path, NULL, GIT_RMDIR_REMOVE_FILES));
+ free(repo2_path);
+}
+
+
+/**
+ * This checks that the '+' flag on fetchspecs is respected. We create a
+ * repository that has a reference to two commits, one a child of the other.
+ * We fetch this repository into a second repository. Then we reset the
+ * reference in the first repository and run the fetch again. If the '+' flag
+ * is used then the reference in the second repository will change, but if it
+ * is not then it should stay the same.
+ *
+ * @param commit1id A pointer to an OID which will be populated with the first
+ * commit.
+ * @param commit2id A pointer to an OID which will be populated with the second
+ * commit, which is a descendant of the first.
+ * @param force Whether to use a spec with '+' prefixed to force the refs
+ * to update
+ */
+void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
+ bool force) {
+ char *refspec_strs = {
+ force ? FORCE_FETCHSPEC : NON_FORCE_FETCHSPEC,
+ };
+ git_strarray refspecs = {
+ .count = 1,
+ .strings = &refspec_strs,
+ };
+
+ /* create two commits in repo 1 and a reference to them */
+ {
+ git_oid empty_tree_id;
+ git_tree *empty_tree;
+ git_signature *sig;
+ git_treebuilder *tb;
+ cl_git_pass(git_treebuilder_new(&tb, repo1, NULL));
+ cl_git_pass(git_treebuilder_write(&empty_tree_id, tb));
+ cl_git_pass(git_tree_lookup(&empty_tree, repo1, &empty_tree_id));
+ cl_git_pass(git_signature_default(&sig, repo1));
+ cl_git_pass(git_commit_create(commit1id, repo1, REPO1_REFNAME, sig,
+ sig, NULL, "one", empty_tree, 0, NULL));
+ cl_git_pass(git_commit_create_v(commit2id, repo1, REPO1_REFNAME, sig,
+ sig, NULL, "two", empty_tree, 1, commit1id));
+
+ git_tree_free(empty_tree);
+ git_signature_free(sig);
+ git_treebuilder_free(tb);
+ }
+
+ /* fetch the reference via the remote */
+ {
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo2,
+ git_repository_path(repo1)));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, "some message"));
+
+ git_remote_free(remote);
+ }
+
+ /* assert that repo2 references the second commit */
+ {
+ const git_oid *target;
+ git_reference *ref;
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, commit2id), 0);
+ git_reference_free(ref);
+ }
+
+ /* set the reference in repo1 to point to the older commit */
+ {
+ git_reference *ref;
+ git_reference *ref2;
+ cl_git_pass(git_reference_lookup(&ref, repo1, REPO1_REFNAME));
+ cl_git_pass(git_reference_set_target(&ref2, ref, commit1id,
+ "rollback"));
+ git_reference_free(ref);
+ git_reference_free(ref2);
+ }
+
+ /* fetch the reference again */
+ {
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo2,
+ git_repository_path(repo1)));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, "some message"));
+
+ git_remote_free(remote);
+ }
+}
+
+void test_remote_fetch__dont_update_refs_if_not_descendant_and_not_force(void) {
+ const git_oid *target;
+ git_oid commit1id;
+ git_oid commit2id;
+ git_reference *ref;
+
+ do_time_travelling_fetch(&commit1id, &commit2id, false);
+
+ /* assert that the reference in repo2 has not changed */
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, &commit2id), 0);
+
+ git_reference_free(ref);
+}
+
+void test_remote_fetch__do_update_refs_if_not_descendant_and_force(void) {
+ const git_oid *target;
+ git_oid commit1id;
+ git_oid commit2id;
+ git_reference *ref;
+
+ do_time_travelling_fetch(&commit1id, &commit2id, true);
+
+ /* assert that the reference in repo2 has changed */
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, &commit1id), 0);
+
+ git_reference_free(ref);
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "net.h"
+#include "remote.h"
+
+static git_repository *repo;
+static git_net_url url = GIT_NET_URL_INIT;
+
+static int orig_proxies_need_reset = 0;
+static char *orig_http_proxy = NULL;
+static char *orig_https_proxy = NULL;
+static char *orig_no_proxy = NULL;
+
+void test_remote_httpproxy__initialize(void)
+{
+ git_remote *remote;
+
+ repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_remote_create(&remote, repo, "lg2", "https://github.com/libgit2/libgit2"));
+ cl_git_pass(git_net_url_parse(&url, "https://github.com/libgit2/libgit2"));
+
+ git_remote_free(remote);
+
+ orig_proxies_need_reset = 0;
+}
+
+void test_remote_httpproxy__cleanup(void)
+{
+ if (orig_proxies_need_reset) {
+ cl_setenv("HTTP_PROXY", orig_http_proxy);
+ cl_setenv("HTTPS_PROXY", orig_https_proxy);
+ cl_setenv("NO_PROXY", orig_no_proxy);
+
+ git__free(orig_http_proxy);
+ git__free(orig_https_proxy);
+ git__free(orig_no_proxy);
+ }
+
+ git_net_url_dispose(&url);
+ cl_git_sandbox_cleanup();
+}
+
+void assert_proxy_is(const char *expected)
+{
+ git_remote *remote;
+ char *proxy;
+
+ cl_git_pass(git_remote_lookup(&remote, repo, "lg2"));
+ cl_git_pass(git_remote__http_proxy(&proxy, remote, &url));
+
+ if (expected)
+ cl_assert_equal_s(proxy, expected);
+ else
+ cl_assert_equal_p(proxy, expected);
+
+ git_remote_free(remote);
+ git__free(proxy);
+}
+
+void assert_config_match(const char *config, const char *expected)
+{
+ git_remote *remote;
+ char *proxy;
+
+ if (config)
+ cl_repo_set_string(repo, config, expected);
+
+ cl_git_pass(git_remote_lookup(&remote, repo, "lg2"));
+ cl_git_pass(git_remote__http_proxy(&proxy, remote, &url));
+
+ if (expected)
+ cl_assert_equal_s(proxy, expected);
+ else
+ cl_assert_equal_p(proxy, expected);
+
+ git_remote_free(remote);
+ git__free(proxy);
+}
+
+void test_remote_httpproxy__config_overrides(void)
+{
+ /*
+ * http.proxy should be honored, then http.<url>.proxy should
+ * be honored in increasing specificity of the url. finally,
+ * remote.<name>.proxy is the most specific.
+ */
+ assert_config_match(NULL, NULL);
+ assert_config_match("http.proxy", "http://localhost:1/");
+ assert_config_match("http.https://github.com.proxy", "http://localhost:2/");
+ assert_config_match("http.https://github.com/.proxy", "http://localhost:3/");
+ assert_config_match("http.https://github.com/libgit2.proxy", "http://localhost:4/");
+ assert_config_match("http.https://github.com/libgit2/.proxy", "http://localhost:5/");
+ assert_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:6/");
+ assert_config_match("remote.lg2.proxy", "http://localhost:7/");
+}
+
+void test_remote_httpproxy__config_empty_overrides(void)
+{
+ /*
+ * with greater specificity, an empty config entry overrides
+ * a set one
+ */
+ assert_config_match("http.proxy", "http://localhost:1/");
+ assert_config_match("http.https://github.com.proxy", "");
+ assert_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:2/");
+ assert_config_match("remote.lg2.proxy", "");
+}
+
+void assert_global_config_match(const char *config, const char *expected)
+{
+ git_remote *remote;
+ char *proxy;
+ git_config* cfg;
+
+ if (config) {
+ cl_git_pass(git_config_open_default(&cfg));
+ git_config_set_string(cfg, config, expected);
+ git_config_free(cfg);
+ }
+
+ cl_git_pass(git_remote_create_detached(&remote, "https://github.com/libgit2/libgit2"));
+ cl_git_pass(git_remote__http_proxy(&proxy, remote, &url));
+
+ if (expected)
+ cl_assert_equal_s(proxy, expected);
+ else
+ cl_assert_equal_p(proxy, expected);
+
+ git_remote_free(remote);
+ git__free(proxy);
+}
+
+void test_remote_httpproxy__config_overrides_detached_remote(void)
+{
+ cl_fake_home();
+
+ assert_global_config_match(NULL, NULL);
+ assert_global_config_match("http.proxy", "http://localhost:1/");
+ assert_global_config_match("http.https://github.com.proxy", "http://localhost:2/");
+ assert_global_config_match("http.https://github.com/.proxy", "http://localhost:3/");
+ assert_global_config_match("http.https://github.com/libgit2.proxy", "http://localhost:4/");
+ assert_global_config_match("http.https://github.com/libgit2/.proxy", "http://localhost:5/");
+ assert_global_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:6/");
+
+ cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_remote_httpproxy__env(void)
+{
+ orig_http_proxy = cl_getenv("HTTP_PROXY");
+ orig_https_proxy = cl_getenv("HTTPS_PROXY");
+ orig_no_proxy = cl_getenv("NO_PROXY");
+ orig_proxies_need_reset = 1;
+
+ /* Clear everything for a fresh start */
+ cl_setenv("HTTP_PROXY", NULL);
+ cl_setenv("HTTPS_PROXY", NULL);
+ cl_setenv("NO_PROXY", NULL);
+
+ /* HTTP proxy is ignored for HTTPS */
+ cl_setenv("HTTP_PROXY", "http://localhost:9/");
+ assert_proxy_is(NULL);
+
+ /* HTTPS proxy is honored for HTTPS */
+ cl_setenv("HTTPS_PROXY", "http://localhost:10/");
+ assert_proxy_is("http://localhost:10/");
+
+ /* NO_PROXY is honored */
+ cl_setenv("NO_PROXY", "github.com:443");
+ assert_proxy_is(NULL);
+
+ cl_setenv("NO_PROXY", "github.com:80");
+ assert_proxy_is("http://localhost:10/");
+
+ cl_setenv("NO_PROXY", "github.com");
+ assert_proxy_is(NULL);
+
+ cl_setenv("NO_PROXY", "github.dev,github.com,github.foo");
+ assert_proxy_is(NULL);
+
+ cl_setenv("HTTPS_PROXY", "");
+ assert_proxy_is(NULL);
+
+ /* configuration overrides environment variables */
+ cl_setenv("HTTPS_PROXY", "http://localhost:10/");
+ cl_setenv("NO_PROXY", "github.none");
+ assert_config_match("http.https://github.com.proxy", "http://localhost:11/");
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+#include <ctype.h>
+
+git_repository *repo;
+
+void test_repo_extensions__initialize(void)
+{
+ git_config *config;
+
+ repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1));
+ git_config_free(config);
+}
+
+void test_repo_extensions__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, NULL, 0));
+}
+
+void test_repo_extensions__builtin(void)
+{
+ git_repository *extended;
+
+ cl_repo_set_string(repo, "extensions.noop", "foobar");
+
+ cl_git_pass(git_repository_open(&extended, "empty_bare.git"));
+ cl_assert(git_repository_path(extended) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(extended), "/") == 0);
+ git_repository_free(extended);
+}
+
+void test_repo_extensions__negate_builtin(void)
+{
+ const char *in[] = { "foo", "!noop", "baz" };
+ git_repository *extended;
+
+ cl_repo_set_string(repo, "extensions.noop", "foobar");
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
+
+ cl_git_fail(git_repository_open(&extended, "empty_bare.git"));
+ git_repository_free(extended);
+}
+
+void test_repo_extensions__unsupported(void)
+{
+ git_repository *extended = NULL;
+
+ cl_repo_set_string(repo, "extensions.unknown", "foobar");
+
+ cl_git_fail(git_repository_open(&extended, "empty_bare.git"));
+ git_repository_free(extended);
+}
+
+void test_repo_extensions__adds_extension(void)
+{
+ const char *in[] = { "foo", "!noop", "newextension", "baz" };
+ git_repository *extended;
+
+ cl_repo_set_string(repo, "extensions.newextension", "foobar");
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
+
+ cl_git_pass(git_repository_open(&extended, "empty_bare.git"));
+ cl_assert(git_repository_path(extended) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(extended), "/") == 0);
+ git_repository_free(extended);
+}
#include "clar_libgit2.h"
+#include "repo/repo_helpers.h"
void test_repo_getters__is_empty_correctly_deals_with_pristine_looking_repos(void)
{
git_repository_free(repo);
}
+void test_repo_getters__is_empty_can_detect_repositories_with_defaultbranch_config_empty(void)
+{
+ git_repository *repo;
+
+ create_tmp_global_config("tmp_global_path", "init.defaultBranch", "");
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_assert_equal_i(false, git_repository_is_empty(repo));
+
+ git_repository_free(repo);
+}
+
void test_repo_getters__retrieving_the_odb_honors_the_refcount(void)
{
git_odb *odb;
void test_repo_hashfile__cleanup(void)
{
+ cl_fixture_cleanup("absolute");
cl_git_sandbox_cleanup();
_repo = NULL;
}
git_buf_dispose(&full);
}
-void test_repo_hashfile__filtered(void)
+void test_repo_hashfile__filtered_in_workdir(void)
{
+ git_buf root = GIT_BUF_INIT, txt = GIT_BUF_INIT, bin = GIT_BUF_INIT;
+ char cwd[GIT_PATH_MAX];
git_oid a, b;
+ cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
+ cl_must_pass(p_mkdir("absolute", 0777));
+ cl_git_pass(git_buf_joinpath(&root, cwd, "status"));
+ cl_git_pass(git_buf_joinpath(&txt, root.ptr, "testfile.txt"));
+ cl_git_pass(git_buf_joinpath(&bin, root.ptr, "testfile.bin"));
+
cl_repo_set_bool(_repo, "core.autocrlf", true);
cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n");
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, NULL));
cl_assert(git_oid_cmp(&a, &b));
+ /* not equal hashes because of filtering when specified by absolute path */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert(git_oid_cmp(&a, &b));
+
/* equal hashes because filter is binary */
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, NULL));
cl_assert_equal_oid(&a, &b);
+ /* equal hashes because filter is binary when specified by absolute path */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
/* equal hashes when 'as_file' points to binary filtering */
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, "foo.bin"));
cl_assert_equal_oid(&a, &b);
+ /* equal hashes when 'as_file' points to binary filtering (absolute path) */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "foo.bin"));
+ cl_assert_equal_oid(&a, &b);
+
/* not equal hashes when 'as_file' points to text filtering */
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, "foo.txt"));
cl_assert(git_oid_cmp(&a, &b));
+ /* not equal hashes when 'as_file' points to text filtering */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "foo.txt"));
+ cl_assert(git_oid_cmp(&a, &b));
+
/* equal hashes when 'as_file' is empty and turns off filtering */
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, ""));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, ""));
cl_assert_equal_oid(&a, &b);
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, ""));
+ cl_assert_equal_oid(&a, &b);
+
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, ""));
+ cl_assert_equal_oid(&a, &b);
+
/* some hash type failures */
cl_git_fail(git_odb_hashfile(&a, "status/testfile.txt", 0));
cl_git_fail(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_ANY, NULL));
+
+ git_buf_dispose(&txt);
+ git_buf_dispose(&bin);
+ git_buf_dispose(&root);
+}
+
+void test_repo_hashfile__filtered_outside_workdir(void)
+{
+ git_buf root = GIT_BUF_INIT, txt = GIT_BUF_INIT, bin = GIT_BUF_INIT;
+ char cwd[GIT_PATH_MAX];
+ git_oid a, b;
+
+ cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
+ cl_must_pass(p_mkdir("absolute", 0777));
+ cl_git_pass(git_buf_joinpath(&root, cwd, "absolute"));
+ cl_git_pass(git_buf_joinpath(&txt, root.ptr, "testfile.txt"));
+ cl_git_pass(git_buf_joinpath(&bin, root.ptr, "testfile.bin"));
+
+ cl_repo_set_bool(_repo, "core.autocrlf", true);
+ cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n");
+
+ /* create some sample content with CRLF in it */
+ cl_git_mkfile("absolute/testfile.txt", "content\r\n");
+ cl_git_mkfile("absolute/testfile.bin", "other\r\nstuff\r\n");
+
+ /* not equal hashes because of filtering */
+ cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.txt", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "testfile.txt"));
+ cl_assert(git_oid_cmp(&a, &b));
+
+ /* equal hashes because filter is binary */
+ cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.bin", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "testfile.bin"));
+ cl_assert_equal_oid(&a, &b);
+
+ /*
+ * equal hashes because no filtering occurs for absolute paths outside the working
+ * directory unless as_path is specified
+ */
+ cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.txt", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
+ cl_git_pass(git_odb_hashfile(&a, "absolute/testfile.bin", GIT_OBJECT_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
+ git_buf_dispose(&txt);
+ git_buf_dispose(&bin);
+ git_buf_dispose(&root);
}
BARE_REPOSITORY = 1
};
-static git_repository *_repo = NULL;
-static git_buf _global_path = GIT_BUF_INIT;
+static git_repository *g_repo = NULL;
+static git_buf g_global_path = GIT_BUF_INIT;
void test_repo_init__initialize(void)
{
- _repo = NULL;
+ g_repo = NULL;
git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
- &_global_path);
+ &g_global_path);
}
void test_repo_init__cleanup(void)
{
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
- _global_path.ptr);
- git_buf_dispose(&_global_path);
+ g_global_path.ptr);
+ git_buf_dispose(&g_global_path);
cl_fixture_cleanup("tmp_global_path");
}
static void cleanup_repository(void *path)
{
- git_repository_free(_repo);
- _repo = NULL;
+ git_repository_free(g_repo);
+ g_repo = NULL;
cl_fixture_cleanup((const char *)path);
}
cl_assert(!git_path_isdir(working_directory));
- cl_git_pass(git_repository_init(&_repo, working_directory, is_bare));
+ cl_git_pass(git_repository_init(&g_repo, working_directory, is_bare));
- workdir = git_repository_workdir(_repo);
+ workdir = git_repository_workdir(g_repo);
if (workdir != NULL || expected_working_directory != NULL) {
cl_assert(
git__suffixcmp(workdir, expected_working_directory) == 0
}
cl_assert(
- git__suffixcmp(git_repository_path(_repo), expected_path_repository) == 0
+ git__suffixcmp(git_repository_path(g_repo), expected_path_repository) == 0
);
- cl_assert(git_repository_is_bare(_repo) == is_bare);
+ cl_assert(git_repository_is_bare(g_repo) == is_bare);
#ifdef GIT_WIN32
if (!is_bare) {
- DWORD fattrs = GetFileAttributes(git_repository_path(_repo));
+ DWORD fattrs = GetFileAttributes(git_repository_path(g_repo));
cl_assert((fattrs & FILE_ATTRIBUTE_HIDDEN) != 0);
}
#endif
- cl_assert(git_repository_is_empty(_repo));
+ cl_assert(git_repository_is_empty(g_repo));
}
void test_repo_init__standard_repo(void)
cl_git_pass(chdir(git_buf_cstr(&path_repository)));
/* Initialize a bare repo with a relative path escaping out of the current working directory */
- cl_git_pass(git_repository_init(&_repo, "../d/e.git", 1));
- cl_git_pass(git__suffixcmp(git_repository_path(_repo), "/a/b/d/e.git/"));
+ cl_git_pass(git_repository_init(&g_repo, "../d/e.git", 1));
+ cl_git_pass(git__suffixcmp(git_repository_path(g_repo), "/a/b/d/e.git/"));
- git_repository_free(_repo);
+ git_repository_free(g_repo);
+ g_repo = NULL;
/* Open a bare repo with a relative path escaping out of the current working directory */
- cl_git_pass(git_repository_open(&_repo, "../d/e.git"));
+ cl_git_pass(git_repository_open(&g_repo, "../d/e.git"));
cl_git_pass(chdir(git_buf_cstr(&path_current_workdir)));
cl_set_cleanup(&cleanup_repository, "reinit.git");
/* Initialize the repository */
- cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
- git_repository_free(_repo);
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+ git_repository_free(g_repo);
+ g_repo = NULL;
/* Reinitialize the repository */
- cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
}
void test_repo_init__reinit_too_recent_bare_repo(void)
git_config *config;
/* Initialize the repository */
- cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
- git_repository_config(&config, _repo);
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+ git_repository_config(&config, g_repo);
/*
* Hack the config of the repository to make it look like it has
cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 42));
git_config_free(config);
- git_repository_free(_repo);
+ git_repository_free(g_repo);
+ g_repo = NULL;
/* Try to reinitialize the repository */
- cl_git_fail(git_repository_init(&_repo, "reinit.git", 1));
+ cl_git_fail(git_repository_init(&g_repo, "reinit.git", 1));
cl_fixture_cleanup("reinit.git");
}
ensure_repository_init("tester", 0, "tester/.git/", "tester/");
cl_git_pass(
- git_buf_joinpath(&path, git_repository_path(_repo), "description"));
+ git_buf_joinpath(&path, git_repository_path(g_repo), "description"));
cl_assert(git_path_isfile(git_buf_cstr(&path)));
cl_git_pass(
- git_buf_joinpath(&path, git_repository_path(_repo), "info/exclude"));
+ git_buf_joinpath(&path, git_repository_path(g_repo), "info/exclude"));
cl_assert(git_path_isfile(git_buf_cstr(&path)));
cl_git_pass(
- git_buf_joinpath(&path, git_repository_path(_repo), "hooks"));
+ git_buf_joinpath(&path, git_repository_path(g_repo), "hooks"));
cl_assert(git_path_isdir(git_buf_cstr(&path)));
/* won't confirm specific contents of hooks dir since it may vary */
cl_set_cleanup(&cleanup_repository, "config_entry");
- cl_git_pass(git_repository_init(&_repo, repo_path, is_bare));
+ cl_git_pass(git_repository_init(&g_repo, repo_path, is_bare));
- cl_git_pass(git_repository_config(&config, _repo));
+ cl_git_pass(git_repository_config(&config, g_repo));
error = git_config_get_bool(¤t_value, config, config_key);
git_config_free(config);
const char *config_key, int expected_value)
{
assert_config_entry_on_init_bytype(config_key, expected_value, true);
- git_repository_free(_repo);
+ git_repository_free(g_repo);
+ g_repo = NULL;
assert_config_entry_on_init_bytype(config_key, expected_value, false);
}
* Create a new repository (can't use `assert_config_on_init` since we
* want to examine configuration levels with more granularity.)
*/
- cl_git_pass(git_repository_init(&_repo, "config_entry/test.non.bare.git", false));
+ cl_git_pass(git_repository_init(&g_repo, "config_entry/test.non.bare.git", false));
/* Ensure that core.symlinks remains set (via the global config). */
- cl_git_pass(git_repository_config(&config, _repo));
+ cl_git_pass(git_repository_config(&config, g_repo));
cl_git_pass(git_config_get_bool(&val, config, "core.symlinks"));
cl_assert_equal_i(1, val);
git_config_free(repo_config);
git_config_free(config);
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
#endif
}
/* Init a new repo */
cl_set_cleanup(&cleanup_repository, "not.overwrite.git");
- cl_git_pass(git_repository_init(&_repo, "not.overwrite.git", 1));
+ cl_git_pass(git_repository_init(&g_repo, "not.overwrite.git", 1));
/* Change the "core.ignorecase" config value to something unlikely */
- git_repository_config(&config, _repo);
+ git_repository_config(&config, g_repo);
git_config_set_int32(config, "core.ignorecase", 42);
git_config_free(config);
- git_repository_free(_repo);
- _repo = NULL;
+ git_repository_free(g_repo);
+ g_repo = NULL;
/* Reinit the repository */
- cl_git_pass(git_repository_init(&_repo, "not.overwrite.git", 1));
- git_repository_config(&config, _repo);
+ cl_git_pass(git_repository_init(&g_repo, "not.overwrite.git", 1));
+ git_repository_config(&config, g_repo);
/* Ensure the "core.ignorecase" config value hasn't been updated */
cl_git_pass(git_config_get_int32(¤t_value, config, "core.ignorecase"));
/* Init a new repo */
cl_set_cleanup(&cleanup_repository, "overwrite.git");
- cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
+ cl_git_pass(git_repository_init(&g_repo, "overwrite.git", 1));
/* Change the "core.filemode" config value to something unlikely */
- cl_repo_set_bool(_repo, "core.filemode", !expected);
+ cl_repo_set_bool(g_repo, "core.filemode", !expected);
- git_repository_free(_repo);
- _repo = NULL;
+ git_repository_free(g_repo);
+ g_repo = NULL;
/* Reinit the repository */
- cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
+ cl_git_pass(git_repository_init(&g_repo, "overwrite.git", 1));
/* Ensure the "core.filemode" config value has been reset */
- current_value = cl_repo_get_bool(_repo, "core.filemode");
+ current_value = cl_repo_get_bool(g_repo, "core.filemode");
cl_assert_equal_i(expected, current_value);
}
void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
{
assert_config_entry_on_init_bytype("core.logallrefupdates", GIT_ENOTFOUND, true);
- git_repository_free(_repo);
+ git_repository_free(g_repo);
assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
}
git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
/* without MKDIR this should fail */
- cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts));
+ cl_git_fail(git_repository_init_ext(&g_repo, "extended", &opts));
/* make the directory first, then it should succeed */
cl_git_pass(git_futils_mkdir("extended", 0775, 0));
- cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts));
+ cl_git_pass(git_repository_init_ext(&g_repo, "extended", &opts));
- cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/"));
- cl_assert(!git__suffixcmp(git_repository_path(_repo), "/extended/.git/"));
- cl_assert(!git_repository_is_bare(_repo));
- cl_assert(git_repository_is_empty(_repo));
+ cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "/extended/"));
+ cl_assert(!git__suffixcmp(git_repository_path(g_repo), "/extended/.git/"));
+ cl_assert(!git_repository_is_bare(g_repo));
+ cl_assert(git_repository_is_empty(g_repo));
cleanup_repository("extended");
}
opts.initial_head = "development";
opts.origin_url = "https://github.com/libgit2/libgit2.git";
- cl_git_pass(git_repository_init_ext(&_repo, "root/b/c.git", &opts));
+ cl_git_pass(git_repository_init_ext(&g_repo, "root/b/c.git", &opts));
- cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/c_wd/"));
- cl_assert(!git__suffixcmp(git_repository_path(_repo), "/c.git/"));
+ cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(g_repo), "/c.git/"));
cl_assert(git_path_isfile("root/b/c_wd/.git"));
- cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(!git_repository_is_bare(g_repo));
/* repo will not be counted as empty because we set head to "development" */
- cl_assert(!git_repository_is_empty(_repo));
+ cl_assert(!git_repository_is_empty(g_repo));
- cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
+ cl_git_pass(git_path_lstat(git_repository_path(g_repo), &st));
cl_assert(S_ISDIR(st.st_mode));
if (cl_is_chmod_supported())
cl_assert((S_ISGID & st.st_mode) == S_ISGID);
else
cl_assert((S_ISGID & st.st_mode) == 0);
- cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
cl_assert(git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC);
cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref));
git_reference_free(ref);
- cl_git_pass(git_remote_lookup(&remote, _repo, "origin"));
+ cl_git_pass(git_remote_lookup(&remote, g_repo, "origin"));
cl_assert_equal_s("origin", git_remote_name(remote));
cl_assert_equal_s(opts.origin_url, git_remote_url(remote));
git_remote_free(remote);
- git_repository_free(_repo);
+ git_repository_free(g_repo);
cl_fixture_cleanup("root");
}
GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
/* make the directory first, then it should succeed */
- cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
+ cl_git_pass(git_repository_init_ext(&g_repo, "root/b/my_repository", &opts));
- cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
- cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
- cl_assert(!git_repository_is_bare(_repo));
- cl_assert(git_repository_is_empty(_repo));
+ cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(g_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(g_repo));
+ cl_assert(git_repository_is_empty(g_repo));
/* Verify that the gitlink and worktree entries are relative */
/* Verify worktree */
- assert_config_entry_value(_repo, "core.worktree", "../c_wd/");
+ assert_config_entry_value(g_repo, "core.worktree", "../c_wd/");
/* Verify gitlink */
cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
/* make the directory first, then it should succeed */
- cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
+ cl_git_pass(git_repository_init_ext(&g_repo, "root/b/my_repository", &opts));
git_buf_dispose(&full_path);
- cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
- cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
- cl_assert(!git_repository_is_bare(_repo));
- cl_assert(git_repository_is_empty(_repo));
+ cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(g_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(g_repo));
+ cl_assert(git_repository_is_empty(g_repo));
/* Verify that the gitlink and worktree entries are relative */
/* Verify worktree */
- assert_config_entry_value(_repo, "core.worktree", "../c_wd/");
+ assert_config_entry_value(g_repo, "core.worktree", "../c_wd/");
/* Verify gitlink */
cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
cl_set_cleanup(&cleanup_repository, "extended");
cl_git_pass(git_futils_mkdir("extended", 0775, 0));
- cl_git_pass(git_repository_init(&_repo, "extended", false));
+ cl_git_pass(git_repository_init(&g_repo, "extended", false));
cl_git_pass(git_repository_init(&reinit, "extended", false));
- cl_assert_equal_s(git_repository_path(_repo), git_repository_path(reinit));
+ cl_assert_equal_s(git_repository_path(g_repo), git_repository_path(reinit));
git_repository_free(reinit);
}
cl_set_cleanup(&cleanup_repository, "committed");
/* Initialize the repository */
- cl_git_pass(git_repository_init(&_repo, "committed", 0));
+ cl_git_pass(git_repository_init(&g_repo, "committed", 0));
/* Index will be automatically created when requested for a new repo */
- cl_git_pass(git_repository_index(&index, _repo));
+ cl_git_pass(git_repository_index(&index, g_repo));
/* Create a file so we can commit it
*
/* Make sure we're ready to use git_signature_default :-) */
{
git_config *cfg, *local;
- cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_open_level(&local, cfg, GIT_CONFIG_LEVEL_LOCAL));
cl_git_pass(git_config_set_string(local, "user.name", "Test User"));
cl_git_pass(git_config_set_string(local, "user.email", "t@example.com"));
git_oid tree_id, commit_id;
git_tree *tree;
- cl_git_pass(git_signature_default(&sig, _repo));
+ cl_git_pass(git_signature_default(&sig, g_repo));
cl_git_pass(git_index_write_tree(&tree_id, index));
- cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
cl_git_pass(git_commit_create_v(
- &commit_id, _repo, "HEAD", sig, sig,
+ &commit_id, g_repo, "HEAD", sig, sig,
NULL, "First", tree, 0));
git_tree_free(tree);
create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");
- cl_git_pass(git_repository_init(&_repo, "repo", 0));
- cl_git_pass(git_reference_lookup(&head, _repo, "HEAD"));
+ cl_git_pass(git_repository_init(&g_repo, "repo", 0));
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
cl_assert_equal_s("refs/heads/my_default_branch", git_reference_symbolic_target(head));
git_reference_free(head);
}
+
+void test_repo_init__defaultbranch_config_empty(void)
+{
+ git_reference *head;
+
+ cl_set_cleanup(&cleanup_repository, "repo");
+
+ create_tmp_global_config("tmp_global_path", "init.defaultbranch", "");
+
+ cl_git_pass(git_repository_init(&g_repo, "repo", 0));
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+}
+
+void test_repo_init__longpath(void)
+{
+#ifdef GIT_WIN32
+ size_t padding = CONST_STRLEN("objects/pack/pack-.pack.lock") + GIT_OID_HEXSZ;
+ size_t max, i;
+ git_buf path = GIT_BUF_INIT;
+ git_repository *one = NULL, *two = NULL;
+
+ /*
+ * Files within repositories need to fit within MAX_PATH;
+ * that means a repo path must be at most (MAX_PATH - 18).
+ */
+ cl_git_pass(git_buf_puts(&path, clar_sandbox_path()));
+ cl_git_pass(git_buf_putc(&path, '/'));
+
+ max = ((MAX_PATH) - path.size) - padding;
+
+ for (i = 0; i < max - 1; i++)
+ cl_git_pass(git_buf_putc(&path, 'a'));
+
+ cl_git_pass(git_repository_init(&one, path.ptr, 1));
+
+ /* Paths longer than this are rejected */
+ cl_git_pass(git_buf_putc(&path, 'z'));
+ cl_git_fail(git_repository_init(&two, path.ptr, 1));
+
+ git_repository_free(one);
+ git_repository_free(two);
+ git_buf_dispose(&path);
+#endif
+}
git_repository_free(repo);
}
-void test_repo_open__format_version_1_with_valid_extension(void)
-{
- git_repository *repo;
- git_config *config;
-
- repo = cl_git_sandbox_init("empty_bare.git");
-
- cl_git_pass(git_repository_open(&repo, "empty_bare.git"));
- cl_git_pass(git_repository_config(&config, repo));
-
- cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1));
- cl_git_pass(git_config_set_int32(config, "extensions.noop", 1));
-
- git_config_free(config);
- git_repository_free(repo);
-
- cl_git_pass(git_repository_open(&repo, "empty_bare.git"));
- cl_assert(git_repository_path(repo) != NULL);
- cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
- git_repository_free(repo);
-}
-
-void test_repo_open__format_version_1_with_invalid_extension(void)
-{
- git_repository *repo;
- git_config *config;
-
- repo = cl_git_sandbox_init("empty_bare.git");
-
- cl_git_pass(git_repository_open(&repo, "empty_bare.git"));
- cl_git_pass(git_repository_config(&config, repo));
-
- cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1));
- cl_git_pass(git_config_set_int32(config, "extensions.invalid", 1));
-
- git_config_free(config);
- git_repository_free(repo);
-
- cl_git_fail(git_repository_open(&repo, "empty_bare.git"));
- git_repository_free(repo);
-}
-
void test_repo_open__standard_empty_repo_through_gitdir(void)
{
git_repository *repo;
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIFWzCCA0MCFESY816VkhBPUOsdp7djKW5q4ZVzMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxFDASBgNVBAoMC2xpYmdpdDIub3JnMRkwFwYDVQQDDBB0ZXN0Lmxp
+YmdpdDIub3JnMB4XDTIxMDgyNTE4NTExMVoXDTMxMDgyMzE4NTExMVowajELMAkG
+A1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcMCUNhbWJy
+aWRnZTEUMBIGA1UECgwLbGliZ2l0Mi5vcmcxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvaRUaM3IJh9N
+G6Yc7tHioUsIGU0MkzSvy/X6O/vONnuuioiJQyPIvfRSvZR2iQj8THTypDGhWn3r
+6h2wk5eOUGwJH2N9FrrlEBdpsMc7SKdiJXwTI30mkK3/qru8NzE71dgCkYp1xhKw
+edTkAFK+PkvyVLFL7K35cx8Bxfamyssdb+qGWa7g4P27CWUdvQgmurrzzPIMZiLD
+/cI1Kwer/N7nTY/6CSs9dcHTlanyZdf+mQ50+//vI4F6+OduGHJkxRF48jLUz1rz
+P3WGRMRbHjCmvWpX/9DLgqGk7XTy0hNgNUCit6kawwcv5y7SP/ii86MkynAHn5i8
+d+zhXjdrSSy8i0IbRJafnxmtrsmjGeIzraJSRqMlv7KKWEBz+alm6vlePnRUbWB7
+0po5uSsRPya6kJJCzMjIfKq1dgXq33m9jCG2wU+L4fEHVlEkFGXYTspMlIBNUjTc
+c45+e1EpamF8aHm32PP8gTF8fGZzQjOXmNW5g7t0joWMGZ+Ao2jYc1pG3SOARi36
+azrmB5/XJqbbfVZEzIue01fO/5R8RgabOP1qWUjH2KLb8zTDok+CW0ULNseU+MKf
+PHXG2OjxcR0vTqop2V6JlKTXXx3/TOD16/+mSrrPzNDejLrkvAH9oN38YpMBM8eg
+vfivHNRm0jjdGbv2OOPEBLEf1cNimQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBZ
+znFta24sWoqdgKXKAK5RHAh/HyOvTInwcXi9RU4XjYlbqNVs0ODR74VRZINoyAL2
+bo+x/iUuAp9+b8fjr79fpVof3nSMU7UtMcT1nvzVmaUYSkKQ0f/9vK4yg0kao1bV
+WwhIc0slKgOJjEicPVs3kd+duv5vakQeUajLPGM8SiS1F/nF67rIuZLdJn2Qp+im
+w5Q3Pjgqw5VrJxyk3AaUcntKHpWy1POLyNV79tXra6BxbtQVlRS0+h1MHELARDFx
+1ZtgyAe5YbWM7WrIiFKD4mmKZu4GMnJDXVpfUub5g0U/e7L/gg6Z1UyYZuln6axw
+RojuAHo1uAWFUsjhWLYV/7P/l/dC+7gFjvSsUqb1+U7jXObzfKjXo/FwYcy4VsVv
+xNbglbhdVjAo/YBTJuf3L0UZjSbxvQIYS+v8u1ECeWE6SH6cHRzryeo5wO4h8NJR
+n30xsvocHFbs4LWy5BVfMUo6wGUy0Y+1gSwSqVMv3JPuLwxUsv0HPdeC00Ab9cHq
+kYXPNZXg3a6orTDa4hJLdAm2V/fn/2KKJYlNj7iCL664QgoCHl7LFyLMiwFVCu5h
+4JjGL3Q+8MondaLZlq5YDmvtj979AyM/7qL4XAE2oofQ4J5dqnKKpMkWdAM/fI/9
+N5DK/4zMXJWgIED0yo2SSZHQmuqZplacOhmfjjZigQ==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIFWzCCA0MCFESY816VkhBPUOsdp7djKW5q4ZVzMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxFDASBgNVBAoMC2xpYmdpdDIub3JnMRkwFwYDVQQDDBB0ZXN0Lmxp
+YmdpdDIub3JnMB4XDTIxMDgyNTE4NTExMVoXDTMxMDgyMzE4NTExMVowajELMAkG
+A1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcMCUNhbWJy
+aWRnZTEUMBIGA1UECgwLbGliZ2l0Mi5vcmcxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvaRUaM3IJh9N
+G6Yc7tHioUsIGU0MkzSvy/X6O/vONnuuioiJQyPIvfRSvZR2iQj8THTypDGhWn3r
+6h2wk5eOUGwJH2N9FrrlEBdpsMc7SKdiJXwTI30mkK3/qru8NzE71dgCkYp1xhKw
+edTkAFK+PkvyVLFL7K35cx8Bxfamyssdb+qGWa7g4P27CWUdvQgmurrzzPIMZiLD
+/cI1Kwer/N7nTY/6CSs9dcHTlanyZdf+mQ50+//vI4F6+OduGHJkxRF48jLUz1rz
+P3WGRMRbHjCmvWpX/9DLgqGk7XTy0hNgNUCit6kawwcv5y7SP/ii86MkynAHn5i8
+d+zhXjdrSSy8i0IbRJafnxmtrsmjGeIzraJSRqMlv7KKWEBz+alm6vlePnRUbWB7
+0po5uSsRPya6kJJCzMjIfKq1dgXq33m9jCG2wU+L4fEHVlEkFGXYTspMlIBNUjTc
+c45+e1EpamF8aHm32PP8gTF8fGZzQjOXmNW5g7t0joWMGZ+Ao2jYc1pG3SOARi36
+azrmB5/XJqbbfVZEzIue01fO/5R8RgabOP1qWUjH2KLb8zTDok+CW0ULNseU+MKf
+PHXG2OjxcR0vTqop2V6JlKTXXx3/TOD16/+mSrrPzNDejLrkvAH9oN38YpMBM8eg
+vfivHNRm0jjdGbv2OOPEBLEf1cNimQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBZ
+znFta24sWoqdgKXKAK5RHAh/HyOvTInwcXi9RU4XjYlbqNVs0ODR74VRZINoyAL2
+bo+x/iUuAp9+b8fjr79fpVof3nSMU7UtMcT1nvzVmaUYSkKQ0f/9vK4yg0kao1bV
+WwhIc0slKgOJjEicPVs3kd+duv5vakQeUajLPGM8SiS1F/nF67rIuZLdJn2Qp+im
+w5Q3Pjgqw5VrJxyk3AaUcntKHpWy1POLyNV79tXra6BxbtQVlRS0+h1MHELARDFx
+1ZtgyAe5YbWM7WrIiFKD4mmKZu4GMnJDXVpfUub5g0U/e7L/gg6Z1UyYZuln6axw
+RojuAHo1uAWFUsjhWLYV/7P/l/dC+7gFjvSsUqb1+U7jXObzfKjXo/FwYcy4VsVv
+xNbglbhdVjAo/YBTJuf3L0UZjSbxvQIYS+v8u1ECeWE6SH6cHRzryeo5wO4h8NJR
+n30xsvocHFbs4LWy5BVfMUo6wGUy0Y+1gSwSqVMv3JPuLwxUsv0HPdeC00Ab9cHq
+kYXPNZXg3a6orTDa4hJLdAm2V/fn/2KKJYlNj7iCL664QgoCHl7LFyLMiwFVCu5h
+4JjGL3Q+8MondaLZlq5YDmvtj979AyM/7qL4XAE2oofQ4J5dqnKKpMkWdAM/fI/9
+N5DK/4zMXJWgIED0yo2SSZHQmuqZplacOhmfjjZigQ==
+-----END CERTIFICATE-----
--- /dev/null
+[alias]
+ m = '\
+ ";" \
+ ";" \
+ '
--- /dev/null
+x\ 1¥\8fM
+Â0\10\85]ç\14s\ 1%3\19Ó\ 6Dtá\r¼À$\99bÁ6¥F¼¾Q¼\81»÷\ 3\1fï¥2Mc\ 5ònSWU`\8e6*ö¾C¡\9e\aĽGì¢w®×,\89|S\8cÉ,²ê\\ 1\89\a¦à\98Ù#\8bôYö®\ 18t\81\12);\rqÈÁȳÞÊ
+\97ü\925ÃõV¦G\99á -ý¨\93~\8b\9fÛ¥2\1d\ 1=ù`Ñ\11ÁÖ²µ¦¥mlÕ?1æ\9c3È\fcn\ f¶\rºH\1dã]a\18ïjÞ@\81U\e
\ No newline at end of file
--- /dev/null
+b8986fec0f7bde90f78ac72706e782d82f24f2f0
--- /dev/null
+1ec507638b806aba45d6142082885f2a9e88322d
--- /dev/null
+19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 2bc7f351d20b53f1c72c16c4b036e491c478c49a Russell Belfer <rb@github.com> 1351024817 -0700 commit: copy and rename with no change
2bc7f351d20b53f1c72c16c4b036e491c478c49a 1c068dee5790ef1580cfc4cd670915b48d790084 Russell Belfer <rb@github.com> 1361485758 -0800 commit: rewrites, copies with changes, etc.
1c068dee5790ef1580cfc4cd670915b48d790084 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Russell Belfer <rb@github.com> 1361486360 -0800 commit: more renames and smallish modifications
+19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 Kartikaya Gupta <kats@pancake.staktrace.com> 1618486966 -0400 checkout: moving from master to break_rewrite
+31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 db98035f715427eef1f5e17f03e1801c05301e9e Kartikaya Gupta <kats@pancake.staktrace.com> 1618487039 -0400 commit: This test needs to start with a minimum of four files
+db98035f715427eef1f5e17f03e1801c05301e9e 7e7bfb88ba9bc65fd700fee1819cf1c317aafa56 Kartikaya Gupta <kats@pancake.staktrace.com> 1618487097 -0400 commit: Copy/modify files around
+7e7bfb88ba9bc65fd700fee1819cf1c317aafa56 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Kartikaya Gupta <kats@pancake.staktrace.com> 1618487105 -0400 checkout: moving from break_rewrite to master
+19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Kartikaya Gupta <kats@pancake.staktrace.com> 1618487271 -0400 reset: moving to HEAD
--- /dev/null
+x\ 1¥\8eKn\840\10\ 5³ö)ú\ 2\99éö\a\e)\8a"e1\8b9EcÚ
+bÀÈ4\vn\1fî0ËWR\95^®Ë2)Ø\8e>´\89\80'ËV¸ØL\8eÐ ÷±\eÜ\80×\8e<\88Dv!ygÍÆMV\85qè\13ºP"\ 5o£H¡\12\84b¹TJH\19\83C\92^\f\1fúW\e<¹é4óÉð86eø\9aY÷\9f\8d×̳ÜvåY\eg¹åº|\ 3u\94|\8aØGøD\8fh.z½Uy·c~ëvÞ\97:Nå\842½d\anõXGó\ f\v©T´
\ No newline at end of file
--- /dev/null
+x\ 1+)JMU042a040031Qpttd83\83ÿ×Ñ\8f\96fÓ®\9c-°[yéï³ÚËéPi'''\86¨Â]»V\1dï*í\ e
+ÏJ8·ø\90Ä\89nPiggg\86×\9b·¯\97U=á©«\9f\94¡vR%bû¹¿ Pi\17\17\17¬º\ 1¼:6\8e
\ No newline at end of file
--- /dev/null
+7e7bfb88ba9bc65fd700fee1819cf1c317aafa56
--- /dev/null
+ref: refs/heads/master
--- /dev/null
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
--- /dev/null
+P pack-4363774fb90141e8aa7a326ace0566366114e869.pack
+
--- /dev/null
+# pack-refs with: peeled fully-peeled sorted
+ecef6a85173b6f446873a13f7b5a7b54a85cd912 refs/heads/master
--- /dev/null
+ecef6a85173b6f446873a13f7b5a7b54a85cd912
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjsCFAb11im6DYQyGJ0GNQCIehXtegq6MA0GCSqGSIb3DQEBCwUAMGYx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxEDAOBgNVBAoMB2xpYmdpdDIxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwHhcNMjEwODMwMDAyMTQyWhcNMzEwODI4MDAyMTQyWjBmMQswCQYDVQQG
+EwJVUzEWMBQGA1UECAwNTWFzc2FjaHVzZXR0czESMBAGA1UEBwwJQ2FtYnJpZGdl
+MRAwDgYDVQQKDAdsaWJnaXQyMRkwFwYDVQQDDBB0ZXN0LmxpYmdpdDIub3JnMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtqe6b1vnMni+z8Z+a2bGtykI
+ITvBged15rn+0qG6Fz+sn9bYG+ceFupztFfoN3cVpUgQDBTzr3CaAx036BlV0z8i
+CrG0Oh/XGL+9TITQLumEe4iGi8NoMSujBAyXPSNgmpzDmCTGrNFfmq3HzUtO8t3x
+i8OT7d9qCVjFimLvZbgnfHGQ38xvt1XyPgYIVqDQczmMEZ5BdYWB0A1VmnWuP2dH
+BgjwPEC3HwMmm1+PL0VoPTdvE5Su092Qdt8QsiA56466DQyll1d/omnOJfrK7z0N
+OnfDmnDpARSTy6vDofEAYUQoc3dyvBUk8IIzv2UDcR7fTVvYqseQReIOTEnXmQID
+AQABMA0GCSqGSIb3DQEBCwUAA4IBAQBmUEq+JhwWTbB5ODGOKrMG1fKJ+sf6ZH6M
+c4BgLEcdoi/nOTfPuw+ols72LuhH7NKaEcqxWev0jGF0WKqMcM8AGVbywZJ3mBWo
+sKdh6rAGFNkikW4TzhjtDfFbMR45Didl28Be7ieHQL4CQ0Lse3RMOxp250WpiEYV
+W2hIKMwIqOLKGShVD7lI+eHlv+QSH4yOYKHfRHve8s82Tac5OXinc8CJm9ySOtkO
+MfLgfkHtHdFBnV6OVbf4p/596MfMXdwT/bBxT6WPkDGc1AYhoDlmLFTpRgHIDCSK
+2wgV+qHppl7Kn+p3mFQ9sW/1IaRd+jNZOrgZ8Uu5tJ00OaqR/LVG
+-----END CERTIFICATE-----
--- /dev/null
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "git2/revert.h"
+#include "../merge/merge_helpers.h"
+
+#define TEST_REPO_PATH "revert-rename.git"
+
+static git_repository *repo;
+
+/* Fixture setup and teardown */
+void test_revert_rename__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_revert_rename__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+/* Attempt a revert when there is a file rename AND change of file mode,
+ * but the file contents remain the same. Check that the file mode doesn't
+ * change following the revert.
+ */
+void test_revert_rename__automerge(void)
+{
+ git_commit *head_commit, *revert_commit;
+ git_oid revert_oid;
+ git_index *index;
+ git_reference *head_ref;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "f0f64c618e1646d2948a456ed7c4bcfad5536d68", 0, "goodmode" }};
+
+ cl_git_pass(git_repository_head(&head_ref, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head_ref, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_oid_fromstr(&revert_oid, "7b4d7c3789b3581973c04087cb774c3c3576de2f"));
+ cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_oid));
+
+ cl_git_pass(git_revert_commit(&index, repo, revert_commit, head_commit, 0, NULL));
+ cl_assert(merge_test_index(index, merge_index_entries, 1));
+
+ git_commit_free(revert_commit);
+ git_commit_free(head_commit);
+ git_index_free(index);
+ git_reference_free(head_ref);
+}
cl_git_pass(git_reference_peel((git_object **) &tip, head, GIT_OBJECT_COMMIT));
/* Commit with a far-ahead timestamp, we should be able to parse it in the revwalk */
- cl_git_pass(git_signature_new(&sig, "Joe", "joe@example.com", 2399662595ll, 0));
+ cl_git_pass(git_signature_new(&sig, "Joe", "joe@example.com", INT64_C(2399662595), 0));
cl_git_pass(git_commit_tree(&tree, tip));
cl_git_pass(git_commit_create(&id, _repo, "HEAD", sig, sig, NULL, "some message", tree, 1,
cl_assert_equal_oid(&expected1, &result.ids[0]);
cl_assert_equal_oid(&expected2, &result.ids[1]);
- git_oidarray_free(&result);
+ git_oidarray_dispose(&result);
}
void test_revwalk_mergebase__multiple_merge_bases_many_commits(void)
cl_assert_equal_oid(&expected1, &result.ids[0]);
cl_assert_equal_oid(&expected2, &result.ids[1]);
- git_oidarray_free(&result);
+ git_oidarray_dispose(&result);
git__free(input);
}
static void assert_mergebase_many(const char *expected_sha, int count, ...)
{
va_list ap;
- int i;
+ int i;
git_oid *oids;
git_oid oid, expected;
char *partial_oid;
* * commit 8496071c1b46c854b31185ea97743be6a8774479
* Author: Scott Chacon <schacon@gmail.com>
* Date: Sat May 8 16:13:06 2010 -0700
- *
+ *
* testing
- *
+ *
* * commit 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9
* | Author: Scott Chacon <schacon@gmail.com>
* | Date: Tue May 11 13:40:41 2010 -0700
* * commit 5001298e0c09ad9c34e4249bc5801c75e9754fa5
* Author: Scott Chacon <schacon@gmail.com>
* Date: Tue May 11 13:40:23 2010 -0700
- *
+ *
* packed commit one
*/
* |\ Merge: c37a783 2224e19
* | | Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 20:31:04 2012 -0800
- * | |
+ * | |
* | | Merge branch 'first-branch' into second-branch
- * | |
+ * | |
* | * commit 2224e191514cb4bd8c566d80dac22dfcb1e9bb83
* | | Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 20:28:51 2012 -0800
- * | |
+ * | |
* | | j
- * | |
+ * | |
* | * commit a41a49f8f5cd9b6cb14a076bf8394881ed0b4d19
* | | Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 20:28:39 2012 -0800
- * | |
+ * | |
* | | i
- * | |
+ * | |
* | * commit 82bf9a1a10a4b25c1f14c9607b60970705e92545
* | | Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 20:28:28 2012 -0800
- * | |
+ * | |
* | | h
- * | |
+ * | |
* * | commit c37a783c20d92ac92362a78a32860f7eebf938ef
* | | Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 20:30:57 2012 -0800
- * | |
+ * | |
* | | n
- * | |
+ * | |
* * | commit 8b82fb1794cb1c8c7f172ec730a4c2db0ae3e650
* | | Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 20:30:43 2012 -0800
- * | |
+ * | |
* | | m
- * | |
+ * | |
* * | commit 6ab5d28acbf3c3bdff276f7ccfdf29c1520e542f
* | | Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 20:30:38 2012 -0800
- * | |
+ * | |
* | | l
- * | |
+ * | |
* * | commit 7b8c336c45fc6895c1c60827260fe5d798e5d247
* | | Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 20:30:24 2012 -0800
- * | |
+ * | |
* | | k
- * | |
+ * | |
* | | * commit 1c30b88f5f3ee66d78df6520a7de9e89b890818b
* | | | Author: Scott J. Goldman <scottjg@github.com>
* | | | Date: Tue Nov 27 20:28:10 2012 -0800
- * | | |
+ * | | |
* | | | e
- * | | |
+ * | | |
* | | * commit 42b7311aa626e712891940c1ec5d5cba201946a4
* | | | Author: Scott J. Goldman <scottjg@github.com>
* | | | Date: Tue Nov 27 20:28:06 2012 -0800
- * | | |
+ * | | |
* | | | d
- * | | |
+ * | | |
* | | * commit a953a018c5b10b20c86e69fef55ebc8ad4c5a417
* | | |\ Merge: bd1732c cdf97fd
* | | |/ Author: Scott J. Goldman <scottjg@github.com>
* | |/| Date: Tue Nov 27 20:26:43 2012 -0800
- * | | |
+ * | | |
* | | | Merge branch 'first-branch'
- * | | |
+ * | | |
* | * | commit cdf97fd3bb48eb3827638bb33d208f5fd32d0aa6
* | | | Author: Scott J. Goldman <scottjg@github.com>
* | | | Date: Tue Nov 27 20:24:46 2012 -0800
- * | | |
+ * | | |
* | | | g
- * | | |
+ * | | |
* | * | commit ef0488f0b722f0be8bcb90a7730ac7efafd1d694
* | | | Author: Scott J. Goldman <scottjg@github.com>
* | | | Date: Tue Nov 27 20:24:39 2012 -0800
- * | | |
+ * | | |
* | | | f
- * | | |
+ * | | |
* | | * commit bd1732c43c68d712ad09e1d872b9be6d4b9efdc4
* | |/ Author: Scott J. Goldman <scottjg@github.com>
* | | Date: Tue Nov 27 17:43:58 2012 -0800
- * | |
+ * | |
* | | c
- * | |
+ * | |
* | * commit 0c8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
* |/ Author: Scott J. Goldman <scottjg@github.com>
* | Date: Tue Nov 27 17:43:48 2012 -0800
- * |
+ * |
* | b
- * |
+ * |
* * commit 1f4c0311a24b63f6fc209a59a1e404942d4a5006
* Author: Scott J. Goldman <scottjg@github.com>
* Date: Tue Nov 27 17:43:41 2012 -0800
cl_assert_equal_i(1, result.count);
cl_assert_equal_oid(&base, &result.ids[0]);
- git_oidarray_free(&result);
+ git_oidarray_dispose(&result);
git_repository_free(repo);
}
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
+void test_status_worktree__filemode_non755(void)
+{
+ git_repository *repo = cl_git_sandbox_init("filemodes");
+ status_entry_counts counts;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_buf executable_path = GIT_BUF_INIT;
+ git_buf nonexecutable_path = GIT_BUF_INIT;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
+
+ git_buf_joinpath(&executable_path, git_repository_workdir(repo), "exec_on");
+ cl_must_pass(p_chmod(git_buf_cstr(&executable_path), 0744));
+ git_buf_dispose(&executable_path);
+
+ git_buf_joinpath(&nonexecutable_path, git_repository_workdir(repo), "exec_off");
+
+ cl_must_pass(p_chmod(git_buf_cstr(&nonexecutable_path), 0655));
+ git_buf_dispose(&nonexecutable_path);
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = filemode_count;
+ counts.expected_paths = filemode_paths;
+ counts.expected_statuses = filemode_statuses;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+
static int cb_status__interrupt(const char *p, unsigned int s, void *payload)
{
volatile int *count = (int *)payload;
assert_submodule_exists(g_repo, "sm_added_and_uncommited/");
}
+void test_submodule_lookup__can_be_dupped(void)
+{
+ git_submodule *sm;
+ git_submodule *sm_duplicate;
+ const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
+
+ /* Check original */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(git_submodule_owner(sm) == g_repo);
+ cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
+ cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
+ cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
+
+ cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
+ cl_assert(git_submodule_update_strategy(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
+
+ /* Duplicate and free original */
+ cl_assert(git_submodule_dup(&sm_duplicate, sm) == 0);
+ git_submodule_free(sm);
+
+ /* Check duplicate */
+ cl_assert(git_submodule_owner(sm_duplicate) == g_repo);
+ cl_assert_equal_s("sm_unchanged", git_submodule_name(sm_duplicate));
+ cl_assert(git__suffixcmp(git_submodule_path(sm_duplicate), "sm_unchanged") == 0);
+ cl_assert(git__suffixcmp(git_submodule_url(sm_duplicate), "/submod2_target") == 0);
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm_duplicate), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm_duplicate), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm_duplicate), oid) == 0);
+
+ cl_assert(git_submodule_ignore(sm_duplicate) == GIT_SUBMODULE_IGNORE_NONE);
+ cl_assert(git_submodule_update_strategy(sm_duplicate) == GIT_SUBMODULE_UPDATE_CHECKOUT);
+
+ git_submodule_free(sm_duplicate);
+}
+
void test_submodule_lookup__accessors(void)
{
git_submodule *sm;
--- /dev/null
+#include "clar_libgit2.h"
+
+void test_threads_atomic__atomic32_set(void)
+{
+ git_atomic32 v = {0};
+ git_atomic32_set(&v, 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_get(void)
+{
+ git_atomic32 v = {1};
+ cl_assert_equal_i(git_atomic32_get(&v), 1);
+}
+
+void test_threads_atomic__atomic32_inc(void)
+{
+ git_atomic32 v = {0};
+ cl_assert_equal_i(git_atomic32_inc(&v), 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_add(void)
+{
+ git_atomic32 v = {0};
+ cl_assert_equal_i(git_atomic32_add(&v, 1), 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_dec(void)
+{
+ git_atomic32 v = {1};
+ cl_assert_equal_i(git_atomic32_dec(&v), 0);
+ cl_assert_equal_i(v.val, 0);
+}
+
+void test_threads_atomic__atomic64_set(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {0};
+ git_atomic64_set(&v, 1);
+ cl_assert_equal_i(v.val, 1);
+#endif
+}
+
+void test_threads_atomic__atomic64_get(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {1};
+ cl_assert_equal_i(git_atomic64_get(&v), 1);
+#endif
+}
+
+void test_threads_atomic__atomic64_add(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {0};
+ cl_assert_equal_i(git_atomic64_add(&v, 1), 1);
+ cl_assert_equal_i(v.val, 1);
+#endif
+}
+
+void test_threads_atomic__cas_pointer(void)
+{
+ int *value = NULL;
+ int newvalue1 = 1, newvalue2 = 2;
+
+ /* value is updated */
+ cl_assert_equal_p(git_atomic_compare_and_swap(&value, NULL, &newvalue1), NULL);
+ cl_assert_equal_p(value, &newvalue1);
+
+ /* value is not updated */
+ cl_assert_equal_p(git_atomic_compare_and_swap(&value, NULL, &newvalue2), &newvalue1);
+ cl_assert_equal_p(value, &newvalue1);
+}
+
+void test_threads_atomic__cas_intptr(void)
+{
+ intptr_t value = 0;
+ intptr_t oldvalue;
+ intptr_t newvalue;
+
+ /* value is updated */
+ oldvalue = 0;
+ newvalue = 1;
+ cl_assert_equal_i((intptr_t)git_atomic_compare_and_swap(&value, (void *)oldvalue, (void *)newvalue), 0);
+ cl_assert_equal_i(value, 1);
+
+ /* value is not updated */
+ oldvalue = 0;
+ newvalue = 2;
+ cl_assert_equal_i((intptr_t)git_atomic_compare_and_swap(&value, (void *)oldvalue, (void *)newvalue), 1);
+ cl_assert_equal_i(value, 1);
+}
+
+void test_threads_atomic__swap(void)
+{
+ int *value = NULL;
+ int newvalue = 1;
+
+ cl_assert_equal_p(git_atomic_swap(value, &newvalue), NULL);
+ cl_assert_equal_p(value, &newvalue);
+
+ cl_assert_equal_p(git_atomic_swap(value, NULL), &newvalue);
+ cl_assert_equal_p(value, NULL);
+}
+
+void test_threads_atomic__load_ptr(void)
+{
+ int value = 1;
+ int *ptr = &value;
+ cl_assert_equal_p(git_atomic_load(ptr), &value);
+}
+
+void test_threads_atomic__load_intptr(void)
+{
+ intptr_t value = 1;
+ cl_assert_equal_i((intptr_t)git_atomic_load(value), 1);
+}
{
return param;
}
+
+static void *exit_abruptly(void *param)
+{
+ git_thread_exit(param);
+ return NULL;
+}
#endif
void test_threads_basic__exit(void)
cl_assert_equal_sz(424242, (size_t)result);
/* Ensure that the return value of `git_thread_exit` is returned. */
- cl_git_pass(git_thread_create(&thread, return_normally, (void *)232323));
+ cl_git_pass(git_thread_create(&thread, exit_abruptly, (void *)232323));
cl_git_pass(git_thread_join(&thread, &result));
cl_assert_equal_sz(232323, (size_t)result);
#endif
static git_repository *_repo;
static git_tree *_a, *_b;
-static git_atomic _counts[4];
+static git_atomic32 _counts[4];
static int _check_counts;
#ifdef GIT_WIN32
static int _retries;
git_tree_free(_b); _b = NULL;
if (_check_counts) {
- cl_assert_equal_i(288, git_atomic_get(&_counts[0]));
- cl_assert_equal_i(112, git_atomic_get(&_counts[1]));
- cl_assert_equal_i( 80, git_atomic_get(&_counts[2]));
- cl_assert_equal_i( 96, git_atomic_get(&_counts[3]));
+ cl_assert_equal_i(288, git_atomic32_get(&_counts[0]));
+ cl_assert_equal_i(112, git_atomic32_get(&_counts[1]));
+ cl_assert_equal_i( 80, git_atomic32_get(&_counts[2]));
+ cl_assert_equal_i( 96, git_atomic32_get(&_counts[3]));
}
}
/* keep some diff stats to make sure results are as expected */
i = git_diff_num_deltas(diff);
- git_atomic_add(&_counts[0], (int32_t)i);
+ git_atomic32_add(&_counts[0], (int32_t)i);
exp[0] = (int)i;
while (i > 0) {
switch (git_diff_get_delta(diff, --i)->status) {
- case GIT_DELTA_MODIFIED: exp[1]++; git_atomic_inc(&_counts[1]); break;
- case GIT_DELTA_ADDED: exp[2]++; git_atomic_inc(&_counts[2]); break;
- case GIT_DELTA_DELETED: exp[3]++; git_atomic_inc(&_counts[3]); break;
+ case GIT_DELTA_MODIFIED: exp[1]++; git_atomic32_inc(&_counts[1]); break;
+ case GIT_DELTA_ADDED: exp[2]++; git_atomic32_inc(&_counts[2]); break;
+ case GIT_DELTA_DELETED: exp[3]++; git_atomic32_inc(&_counts[3]); break;
default: break;
}
}
-#include "thread-utils.h"
+#include "thread.h"
void run_in_parallel(
int repeats,
--- /dev/null
+#include "clar_libgit2.h"
+
+#include "thread_helpers.h"
+
+void test_threads_tlsdata__can_set_and_get(void)
+{
+ git_tlsdata_key key_one, key_two, key_three;
+
+ cl_git_pass(git_tlsdata_init(&key_one, NULL));
+ cl_git_pass(git_tlsdata_init(&key_two, NULL));
+ cl_git_pass(git_tlsdata_init(&key_three, NULL));
+
+ cl_git_pass(git_tlsdata_set(key_one, (void *)(size_t)42424242));
+ cl_git_pass(git_tlsdata_set(key_two, (void *)(size_t)0xdeadbeef));
+ cl_git_pass(git_tlsdata_set(key_three, (void *)(size_t)98761234));
+
+ cl_assert_equal_sz((size_t)42424242, git_tlsdata_get(key_one));
+ cl_assert_equal_sz((size_t)0xdeadbeef, git_tlsdata_get(key_two));
+ cl_assert_equal_sz((size_t)98761234, git_tlsdata_get(key_three));
+
+ cl_git_pass(git_tlsdata_dispose(key_one));
+ cl_git_pass(git_tlsdata_dispose(key_two));
+ cl_git_pass(git_tlsdata_dispose(key_three));
+}
+
+#ifdef GIT_THREADS
+
+static void *set_and_get(void *param)
+{
+ git_tlsdata_key *tlsdata_key = (git_tlsdata_key *)param;
+ int val;
+
+ if (git_tlsdata_set(*tlsdata_key, &val) != 0 ||
+ git_tlsdata_get(*tlsdata_key) != &val)
+ return (void *)0;
+
+ return (void *)1;
+}
+
+#endif
+
+#define THREAD_COUNT 10
+
+void test_threads_tlsdata__threads(void)
+{
+#ifdef GIT_THREADS
+ git_thread thread[THREAD_COUNT];
+ git_tlsdata_key tlsdata;
+ int i;
+
+ cl_git_pass(git_tlsdata_init(&tlsdata, NULL));
+
+ for (i = 0; i < THREAD_COUNT; i++)
+ cl_git_pass(git_thread_create(&thread[i], set_and_get, &tlsdata));
+
+ for (i = 0; i < THREAD_COUNT; i++) {
+ void *result;
+
+ cl_git_pass(git_thread_join(&thread[i], &result));
+ cl_assert_equal_sz(1, (size_t)result);
+ }
+
+ cl_git_pass(git_tlsdata_dispose(tlsdata));
+#endif
+}
#include "clar_libgit2.h"
-#include "win32/w32_stack.h"
-#include "win32/w32_crtdbg_stacktrace.h"
+#include "win32/w32_leakcheck.h"
-#if defined(GIT_MSVC_CRTDBG)
+#if defined(GIT_WIN32_LEAKCHECK)
static void a(void)
{
char buf[10000];
- cl_assert(git_win32__stack(buf, sizeof(buf), 0, NULL, NULL) == 0);
+ cl_assert(git_win32_leakcheck_stack(buf, sizeof(buf), 0, NULL, NULL) == 0);
#if 0
fprintf(stderr, "Stacktrace from [%s:%d]:\n%s\n", __FILE__, __LINE__, buf);
void test_trace_windows_stacktrace__basic(void)
{
-#if defined(GIT_MSVC_CRTDBG)
+#if defined(GIT_WIN32_LEAKCHECK)
c();
#endif
}
void test_trace_windows_stacktrace__leaks(void)
{
-#if defined(GIT_MSVC_CRTDBG)
+#if defined(GIT_WIN32_LEAKCHECK)
void * p1;
void * p2;
void * p3;
/* remember outstanding leaks due to set setup
* and set mark/checkpoint.
*/
- before = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL |
- GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
+ before = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK,
NULL);
p1 = git__malloc(5);
- leaks = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
"p1");
- cl_assert((leaks == 1));
+ cl_assert_equal_i(1, leaks);
p2 = git__malloc(5);
- leaks = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
"p1,p2");
- cl_assert((leaks == 2));
+ cl_assert_equal_i(2, leaks);
p3 = git__malloc(5);
- leaks = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
"p1,p2,p3");
- cl_assert((leaks == 3));
+ cl_assert_equal_i(3, leaks);
git__free(p2);
- leaks = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
"p1,p3");
- cl_assert((leaks == 2));
+ cl_assert_equal_i(2, leaks);
/* move the mark. only new leaks should appear afterwards */
- error = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
+ error = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK,
NULL);
- cl_assert((error == 0));
+ /* cannot use cl_git_pass() since that may allocate memory. */
+ cl_assert_equal_i(0, error);
- leaks = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
"not_p1,not_p3");
- cl_assert((leaks == 0));
+ cl_assert_equal_i(0, leaks);
p4 = git__malloc(5);
- leaks = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
"p4,not_p1,not_p3");
- cl_assert((leaks == 1));
+ cl_assert_equal_i(1, leaks);
git__free(p1);
git__free(p3);
- leaks = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
"p4");
- cl_assert((leaks == 1));
+ cl_assert_equal_i(1, leaks);
git__free(p4);
- leaks = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
"end");
- cl_assert((leaks == 0));
+ cl_assert_equal_i(0, leaks);
/* confirm current absolute leaks count matches beginning value. */
- after = git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL,
+ after = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL,
"total");
- cl_assert((before == after));
+ cl_assert_equal_i(before, after);
#endif
}
-#if defined(GIT_MSVC_CRTDBG)
+#if defined(GIT_WIN32_LEAKCHECK)
static void aux_cb_alloc__1(unsigned int *aux_id)
{
static unsigned int aux_counter = 0;
void test_trace_windows_stacktrace__aux1(void)
{
-#if defined(GIT_MSVC_CRTDBG)
- git_win32__stack__set_aux_cb(aux_cb_alloc__1, aux_cb_lookup__1);
+#if defined(GIT_WIN32_LEAKCHECK)
+ git_win32_leakcheck_stack_set_aux_cb(aux_cb_alloc__1, aux_cb_lookup__1);
c();
c();
c();
c();
- git_win32__stack__set_aux_cb(NULL, NULL);
+ git_win32_leakcheck_stack_set_aux_cb(NULL, NULL);
#endif
}
#include "clone.h"
#include "buffer.h"
#include "futils.h"
+#include "repository.h"
static git_buf path = GIT_BUF_INIT;
+#define LONG_FILENAME "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt"
+
void test_win32_longpath__initialize(void)
{
#ifdef GIT_WIN32
void test_win32_longpath__cleanup(void)
{
git_buf_dispose(&path);
+ cl_git_sandbox_cleanup();
}
+void test_win32_longpath__errmsg_on_checkout(void)
+{
#ifdef GIT_WIN32
-void assert_name_too_long(void)
+ git_repository *repo;
+
+ cl_git_fail(git_clone(&repo, cl_fixture("testrepo.git"), path.ptr, NULL));
+ cl_assert(git__prefixcmp(git_error_last()->message, "path too long") == 0);
+#endif
+}
+
+void test_win32_longpath__workdir_path_validated(void)
{
- const git_error *err;
- size_t expected_len, actual_len;
- char *expected_msg;
+#ifdef GIT_WIN32
+ git_repository *repo = cl_git_sandbox_init("testrepo");
+ git_buf out = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_workdir_path(&out, repo, "a.txt"));
+
+ /* even if the repo path is a drive letter, this is too long */
+ cl_git_fail(git_repository_workdir_path(&out, repo, LONG_FILENAME));
+ cl_assert(git__prefixcmp(git_error_last()->message, "path too long") == 0);
- err = git_error_last();
- actual_len = strlen(err->message);
+ cl_repo_set_bool(repo, "core.longpaths", true);
+ cl_git_pass(git_repository_workdir_path(&out, repo, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt"));
+ cl_git_pass(git_repository_workdir_path(&out, repo, LONG_FILENAME));
+ git_buf_dispose(&out);
+#endif
+}
+
+#ifdef GIT_WIN32
+static void assert_longpath_status_and_add(git_repository *repo, const char *wddata, const char *repodata) {
+ git_index *index;
+ git_blob *blob;
+ git_buf out = GIT_BUF_INIT;
+ const git_index_entry *entry;
+ unsigned int status_flags;
- expected_msg = git_win32_get_error_message(ERROR_FILENAME_EXCED_RANGE);
- expected_len = strlen(expected_msg);
+ cl_git_pass(git_repository_workdir_path(&out, repo, LONG_FILENAME));
- /* check the suffix */
- cl_assert_equal_s(expected_msg, err->message + (actual_len - expected_len));
+ cl_git_rewritefile(out.ptr, wddata);
- git__free(expected_msg);
+ cl_git_pass(git_status_file(&status_flags, repo, LONG_FILENAME));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status_flags);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, LONG_FILENAME));
+
+ cl_git_pass(git_status_file(&status_flags, repo, LONG_FILENAME));
+ cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status_flags);
+
+ cl_assert((entry = git_index_get_bypath(index, LONG_FILENAME, 0)) != NULL);
+ cl_git_pass(git_blob_lookup(&blob, repo, &entry->id));
+ cl_assert_equal_s(repodata, git_blob_rawcontent(blob));
+
+ git_blob_free(blob);
+ git_index_free(index);
+ git_buf_dispose(&out);
}
#endif
-void test_win32_longpath__errmsg_on_checkout(void)
+void test_win32_longpath__status_and_add(void)
{
#ifdef GIT_WIN32
- git_repository *repo;
+ git_repository *repo = cl_git_sandbox_init("testrepo");
- cl_git_fail(git_clone(&repo, cl_fixture("testrepo.git"), path.ptr, NULL));
- assert_name_too_long();
+ cl_repo_set_bool(repo, "core.longpaths", true);
+
+ /*
+ * Doing no content filtering, we expect the data we add
+ * to be the data in the repository.
+ */
+ assert_longpath_status_and_add(repo,
+ "This is a long path.\r\n",
+ "This is a long path.\r\n");
+#endif
+}
+
+void test_win32_longpath__status_and_add_with_filter(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo = cl_git_sandbox_init("testrepo");
+
+ cl_repo_set_bool(repo, "core.longpaths", true);
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ /*
+ * With `core.autocrlf`, we expect the data we add to have
+ * newline conversion performed.
+ */
+ assert_longpath_status_and_add(repo,
+ "This is a long path.\r\n",
+ "This is a long path.\n");
#endif
}
ours, (const git_annotated_commit **)&theirs, 1));
for (i = 0; i < ARRAY_SIZE(merge_files); i++) {
- git_buf_clear(&path);
- cl_git_pass(git_buf_printf(&path, "%s/%s",
- fixture.worktree->gitdir, merge_files[i]));
+ cl_git_pass(git_buf_joinpath(&path,
+ fixture.worktree->gitdir,
+ merge_files[i]));
cl_assert(git_path_exists(path.ptr));
}
git_strarray wts;
size_t i, j, len;
- cl_git_pass(git_buf_printf(&path, "%s/worktrees/invalid",
- fixture.repo->commondir));
+ cl_git_pass(git_buf_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/invalid"));
cl_git_pass(p_mkdir(path.ptr, 0755));
len = path.size;
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/commondir"));
- cl_git_pass(git_buf_printf(&path,
- "%s/worktrees/testrepo-worktree/commondir",
- fixture.repo->commondir));
+ cl_git_pass(git_buf_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/testrepo-worktree/commondir"));
cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir"));
- cl_git_pass(git_buf_printf(&path,
- "%s/worktrees/testrepo-worktree/gitdir",
- fixture.repo->commondir));
+ cl_git_pass(git_buf_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/testrepo-worktree/gitdir"));
cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
int counter = 0;
cl_git_pass(git_repository_foreach_worktree(fixture.repo, foreach_worktree_cb, &counter));
}
+
+void test_worktree_worktree__validate_invalid_worktreedir(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ p_rename("testrepo-worktree", "testrepo-worktree-tmp");
+ cl_git_fail(git_worktree_validate(wt));
+ p_rename("testrepo-worktree-tmp", "testrepo-worktree");
+
+ git_worktree_free(wt);
+}