New upstream version 1.5.0+ds

This commit is contained in:
Timo Röhling 2022-08-28 14:13:25 +02:00
parent e579e0f707
commit ad5611d85b
949 changed files with 8689 additions and 2655 deletions

92
.clang-format Normal file
View File

@ -0,0 +1,92 @@
# This file is an example configuration for clang-format 5.0.
#
# Note that this style definition should only be understood as a hint
# for writing new code. The rules are still work-in-progress and does
# not yet exactly match the style we have in the existing code.
# C Language specifics
Language: Cpp
# Use tabs whenever we need to fill whitespace that spans at least from one tab
# stop to the next one.
#
# These settings are mirrored in .editorconfig. Keep them in sync.
UseTab: ForIndentation
TabWidth: 8
IndentWidth: 8
ContinuationIndentWidth: 8
ColumnLimit: 80
AlignAfterOpenBracket: AlwaysBreak
AlignEscapedNewlines: Left
AlignTrailingComments: false
# Allow putting parameters onto the next line
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
# Don't allow short braced statements to be on a single line
# if (a) not if (a) return;
# return;
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLoopsOnASingleLine: false
AllowShortLambdasOnASingleLine: None
# Pack as many parameters or arguments onto the same line as possible
# int myFunction(int aaaaaaaaaaaa, int bbbbbbbb,
# int cccc);
BinPackArguments: true
BinPackParameters: false
BreakBeforeBraces: Linux
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: false
BreakStringLiterals: false
# The number of spaces before trailing line comments (// - comments).
# This does not affect trailing block comments (/* - comments).
SpacesBeforeTrailingComments: 1
# Don't insert spaces in casts
# x = (int32) y; not x = ( int32 ) y;
SpacesInCStyleCastParentheses: false
# Don't insert spaces inside container literals
# var arr = [1, 2, 3]; not var arr = [ 1, 2, 3 ];
SpacesInContainerLiterals: false
# Don't insert spaces after '(' or before ')'
# f(arg); not f( arg );
SpacesInParentheses: false
# Don't insert spaces after '[' or before ']'
# int a[5]; not int a[ 5 ];
SpacesInSquareBrackets: false
# Insert a space after '{' and before '}' in struct initializers
Cpp11BracedListStyle: false
# A list of macros that should be interpreted as foreach loops instead of as
# function calls.
ForEachMacros:
- 'git_array_foreach'
- 'git_vector_foreach'
# The maximum number of consecutive empty lines to keep.
MaxEmptyLinesToKeep: 1
# No empty line at the start of a block.
KeepEmptyLinesAtTheStartOfBlocks: false
# Penalties
# This decides what order things should be done if a line is too long
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
SortIncludes: false

5
.github/release.yml vendored
View File

@ -6,10 +6,13 @@ changelog:
- title: Bug fixes - title: Bug fixes
labels: labels:
- bug - bug
- title: Security fixes
labels:
- security
- title: Code cleanups - title: Code cleanups
labels: labels:
- cleanup - cleanup
- title: CI improvements - title: Build and CI improvements
labels: labels:
- build - build
- title: Documentation improvements - title: Documentation improvements

82
.github/workflows/benchmark.yml vendored Normal file
View File

@ -0,0 +1,82 @@
# Benchmark libgit2 against the git reference implementation.
name: Benchmark
on:
workflow_dispatch:
schedule:
- cron: '15 4 * * *'
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:
# Only run scheduled workflows on the main repository; prevents people
# from using build minutes on their forks.
if: github.repository == 'libgit2/libgit2'
strategy:
matrix:
platform:
- name: "Linux (clang, OpenSSL)"
env:
CC: clang
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
CMAKE_BUILD_OPTIONS: --config Release
id: linux
os: ubuntu-latest
setup-script: ubuntu
- name: "macOS"
os: macos-10.15
env:
CC: clang
CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
CMAKE_BUILD_OPTIONS: --config Release
PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
id: macos
setup-script: osx
- name: "Windows (amd64, Visual Studio)"
os: windows-2019
env:
ARCH: amd64
CMAKE_GENERATOR: Visual Studio 16 2019
CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
CMAKE_BUILD_OPTIONS: --config Release
id: windows
setup-script: win32
fail-fast: false
name: "Build ${{ matrix.platform.name }}"
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 benchmark environment
run: source/ci/setup-${{ matrix.platform.setup-script }}-benchmark.sh
shell: bash
if: matrix.platform.setup-script != ''
- name: Build
run: |
mkdir build && cd build
../source/ci/build.sh
shell: bash
- name: Benchmark
run: |
if [[ "$(uname -s)" == MINGW* ]]; then
GIT2_CLI="$(cygpath -w $(pwd))\\build\\Release\\git2_cli"
else
GIT2_CLI="$(pwd)/build/git2_cli"
fi
mkdir benchmark && cd benchmark
../source/tests/benchmarks/benchmark.sh --baseline-cli "git" --cli "${GIT2_CLI}" --json benchmarks.json --zip benchmarks.zip
shell: bash
- name: Upload results
uses: actions/upload-artifact@v2
with:
name: benchmark-${{ matrix.platform.id }}
path: benchmark
if: always()

View File

@ -1,36 +0,0 @@
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

View File

@ -10,7 +10,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
docker-registry: docker.pkg.github.com docker-registry: ghcr.io
docker-config-path: source/ci/docker docker-config-path: source/ci/docker
jobs: jobs:
@ -69,7 +69,7 @@ jobs:
if [ "${{ matrix.container.base }}" != "" ]; then if [ "${{ matrix.container.base }}" != "" ]; then
BASE_ARG="--build-arg BASE=${{ matrix.container.base }}" BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
fi fi
docker build -t ${{ env.docker-registry-container-sha }} ${BASE_ARG} -f ${{ env.dockerfile }} . docker build -t ${{ env.docker-registry-container-sha }} --build-arg UID=$(id -u) --build-arg GID=$(id -g) ${BASE_ARG} -f ${{ env.dockerfile }} .
docker tag ${{ env.docker-registry-container-sha }} ${{ env.docker-registry-container-latest }} 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-sha }}
docker push ${{ env.docker-registry-container-latest }} docker push ${{ env.docker-registry-container-latest }}
@ -85,6 +85,7 @@ jobs:
matrix: matrix:
platform: platform:
- name: "Linux (Xenial, GCC, OpenSSL)" - name: "Linux (Xenial, GCC, OpenSSL)"
id: xenial-gcc-openssl
container: container:
name: xenial name: xenial
env: env:
@ -93,6 +94,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
os: ubuntu-latest os: ubuntu-latest
- name: Linux (Xenial, GCC, mbedTLS) - name: Linux (Xenial, GCC, mbedTLS)
id: xenial-gcc-mbedtls
container: container:
name: xenial name: xenial
env: env:
@ -101,6 +103,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
os: ubuntu-latest os: ubuntu-latest
- name: "Linux (Xenial, Clang, OpenSSL)" - name: "Linux (Xenial, Clang, OpenSSL)"
id: xenial-clang-openssl
container: container:
name: xenial name: xenial
env: env:
@ -109,6 +112,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
os: ubuntu-latest os: ubuntu-latest
- name: "Linux (Xenial, Clang, mbedTLS)" - name: "Linux (Xenial, Clang, mbedTLS)"
id: xenial-clang-mbedtls
container: container:
name: xenial name: xenial
env: env:
@ -117,6 +121,7 @@ jobs:
CMAKE_GENERATOR: Ninja CMAKE_GENERATOR: Ninja
os: ubuntu-latest os: ubuntu-latest
- name: "Linux (MemorySanitizer)" - name: "Linux (MemorySanitizer)"
id: memorysanitizer
container: container:
name: focal name: focal
env: env:
@ -130,6 +135,7 @@ jobs:
UBSAN_OPTIONS: print_stacktrace=1 UBSAN_OPTIONS: print_stacktrace=1
os: ubuntu-latest os: ubuntu-latest
- name: "Linux (UndefinedBehaviorSanitizer)" - name: "Linux (UndefinedBehaviorSanitizer)"
id: ubsanitizer
container: container:
name: focal name: focal
env: env:
@ -143,6 +149,7 @@ jobs:
UBSAN_OPTIONS: print_stacktrace=1 UBSAN_OPTIONS: print_stacktrace=1
os: ubuntu-latest os: ubuntu-latest
- name: "Linux (ThreadSanitizer)" - name: "Linux (ThreadSanitizer)"
id: threadsanitizer
container: container:
name: focal name: focal
env: env:
@ -157,6 +164,7 @@ jobs:
TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1 TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
os: ubuntu-latest os: ubuntu-latest
- name: "macOS" - name: "macOS"
id: macos
os: macos-10.15 os: macos-10.15
env: env:
CC: clang CC: clang
@ -166,6 +174,7 @@ jobs:
SKIP_NEGOTIATE_TESTS: true SKIP_NEGOTIATE_TESTS: true
setup-script: osx setup-script: osx
- name: "Windows (amd64, Visual Studio)" - name: "Windows (amd64, Visual Studio)"
id: windows-amd64-vs
os: windows-2019 os: windows-2019
env: env:
ARCH: amd64 ARCH: amd64
@ -174,6 +183,7 @@ jobs:
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, Visual Studio)" - name: "Windows (x86, Visual Studio)"
id: windows-x86-vs
os: windows-2019 os: windows-2019
env: env:
ARCH: x86 ARCH: x86
@ -182,6 +192,7 @@ jobs:
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true SKIP_NEGOTIATE_TESTS: true
- name: "Windows (amd64, mingw)" - name: "Windows (amd64, mingw)"
id: windows-amd64-mingw
os: windows-2019 os: windows-2019
setup-script: mingw setup-script: mingw
env: env:
@ -193,6 +204,7 @@ jobs:
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, mingw)" - name: "Windows (x86, mingw)"
id: windows-x86-mingw
os: windows-2019 os: windows-2019
setup-script: mingw setup-script: mingw
env: env:
@ -214,7 +226,7 @@ jobs:
path: source path: source
fetch-depth: 0 fetch-depth: 0
- name: Set up build environment - name: Set up build environment
run: source/ci/setup-${{ matrix.platform.setup-script }}.sh run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh
shell: bash shell: bash
if: matrix.platform.setup-script != '' if: matrix.platform.setup-script != ''
- name: Setup QEMU - name: Setup QEMU
@ -229,7 +241,11 @@ jobs:
working-directory: ${{ env.docker-config-path }} working-directory: ${{ env.docker-config-path }}
if: matrix.platform.container.name != '' if: matrix.platform.container.name != ''
- name: Create container - name: Create container
run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ env.dockerfile }} . run: |
if [ "${{ matrix.container.base }}" != "" ]; then
BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
fi
docker build -t ${{ env.docker-registry-container-sha }} --build-arg UID=$(id -u) --build-arg GID=$(id -g) ${BASE_ARG} -f ${{ env.dockerfile }} .
working-directory: ${{ env.docker-config-path }} working-directory: ${{ env.docker-config-path }}
if: matrix.platform.container.name != '' && env.docker-container-exists != 'true' if: matrix.platform.container.name != '' && env.docker-container-exists != 'true'
- name: Build and test - name: Build and test
@ -237,10 +253,12 @@ jobs:
export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}" export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
if [ -n "${{ matrix.platform.container.name }}" ]; then if [ -n "${{ matrix.platform.container.name }}" ]; then
mkdir build
docker run \ docker run \
--rm \ --rm \
--user libgit2:libgit2 \ --user "$(id -u):$(id -g)" \
-v "$(pwd)/source:/home/libgit2/source" \ -v "$(pwd)/source:/home/libgit2/source" \
-v "$(pwd)/build:/home/libgit2/build" \
-w /home/libgit2 \ -w /home/libgit2 \
-e ASAN_SYMBOLIZER_PATH \ -e ASAN_SYMBOLIZER_PATH \
-e CC \ -e CC \
@ -248,19 +266,40 @@ jobs:
-e CMAKE_GENERATOR \ -e CMAKE_GENERATOR \
-e CMAKE_OPTIONS \ -e CMAKE_OPTIONS \
-e GITTEST_NEGOTIATE_PASSWORD \ -e GITTEST_NEGOTIATE_PASSWORD \
-e GITTEST_FLAKY_STAT \
-e PKG_CONFIG_PATH \ -e PKG_CONFIG_PATH \
-e SKIP_NEGOTIATE_TESTS \ -e SKIP_NEGOTIATE_TESTS \
-e SKIP_SSH_TESTS \ -e SKIP_SSH_TESTS \
-e TSAN_OPTIONS \ -e TSAN_OPTIONS \
-e UBSAN_OPTIONS \ -e UBSAN_OPTIONS \
${{ env.docker-registry-container-sha }} \ ${{ env.docker-registry-container-sha }} \
/bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh" /bin/bash -c "cd build && ../source/ci/build.sh && ../source/ci/test.sh"
else else
mkdir build && cd build mkdir build
cd build
../source/ci/build.sh ../source/ci/build.sh
../source/ci/test.sh ../source/ci/test.sh
fi fi
shell: bash shell: bash
- name: Upload test results
uses: actions/upload-artifact@v3
if: success() || failure()
with:
name: test-results-${{ matrix.platform.id }}
path: build/results_*.xml
test_results:
name: Test results
needs: [ build ]
runs-on: ubuntu-latest
steps:
- name: Download test results
uses: actions/download-artifact@v3
- name: Generate test summary
uses: test-summary/action@v1
with:
paths: 'test-results-*/*.xml'
# Generate documentation using docurium. We'll upload the documentation # Generate documentation using docurium. We'll upload the documentation
# as a build artifact so that it can be reviewed as part of a pull # as a build artifact so that it can be reviewed as part of a pull
@ -270,6 +309,7 @@ jobs:
documentation: documentation:
name: Generate documentation name: Generate documentation
needs: [ containers ] needs: [ containers ]
if: success() || failure()
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out repository - name: Check out repository

View File

@ -7,7 +7,7 @@ on:
- cron: '15 1 * * *' - cron: '15 1 * * *'
env: env:
docker-registry: docker.pkg.github.com docker-registry: ghcr.io
docker-config-path: source/ci/docker docker-config-path: source/ci/docker
jobs: jobs:
@ -15,6 +15,10 @@ jobs:
# targets and their details. Then we build either in a docker container # targets and their details. Then we build either in a docker container
# (Linux) or on the actual hosts (macOS, Windows). # (Linux) or on the actual hosts (macOS, Windows).
build: build:
# Only run scheduled workflows on the main repository; prevents people
# from using build minutes on their forks.
if: github.repository == 'libgit2/libgit2'
strategy: strategy:
matrix: matrix:
platform: platform:
@ -247,6 +251,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true RUN_INVASIVE_TESTS: true
SKIP_PROXY_TESTS: true SKIP_PROXY_TESTS: true
GITTEST_FLAKY_STAT: true
os: ubuntu-latest os: ubuntu-latest
- name: "Linux (arm64, Bionic, GCC, OpenSSL)" - name: "Linux (arm64, Bionic, GCC, OpenSSL)"
container: container:
@ -271,7 +276,7 @@ jobs:
path: source path: source
fetch-depth: 0 fetch-depth: 0
- name: Set up build environment - name: Set up build environment
run: source/ci/setup-${{ matrix.platform.setup-script }}.sh run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh
shell: bash shell: bash
if: matrix.platform.setup-script != '' if: matrix.platform.setup-script != ''
- name: Setup QEMU - name: Setup QEMU
@ -305,6 +310,7 @@ jobs:
-e CMAKE_GENERATOR \ -e CMAKE_GENERATOR \
-e CMAKE_OPTIONS \ -e CMAKE_OPTIONS \
-e GITTEST_NEGOTIATE_PASSWORD \ -e GITTEST_NEGOTIATE_PASSWORD \
-e GITTEST_FLAKY_STAT \
-e PKG_CONFIG_PATH \ -e PKG_CONFIG_PATH \
-e SKIP_NEGOTIATE_TESTS \ -e SKIP_NEGOTIATE_TESTS \
-e SKIP_SSH_TESTS \ -e SKIP_SSH_TESTS \
@ -319,6 +325,10 @@ jobs:
shell: bash shell: bash
coverity: coverity:
# Only run scheduled workflows on the main repository; prevents people
# from using build minutes on their forks.
if: github.repository == 'libgit2/libgit2'
name: Coverity name: Coverity
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -338,3 +348,32 @@ jobs:
run: source/ci/coverity.sh run: source/ci/coverity.sh
env: env:
COVERITY_TOKEN: ${{ secrets.coverity_token }} COVERITY_TOKEN: ${{ secrets.coverity_token }}
codeql:
# Only run scheduled workflows on the main repository; prevents people
# from using build minutes on their forks.
if: github.repository == 'libgit2/libgit2'
name: CodeQL
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

View File

@ -1,9 +1,12 @@
# CMake build script for the libgit2 project # libgit2: the cross-platform, linkable library implementation of git.
# See `README.md` for build instructions. # See `README.md` for build instructions.
#
# This top-level CMakeLists.txt sets up configuration options and
# determines which subprojects to build.
cmake_minimum_required(VERSION 3.5.1) cmake_minimum_required(VERSION 3.5.1)
project(libgit2 VERSION "1.4.3" LANGUAGES C) project(libgit2 VERSION "1.5.0" LANGUAGES C)
# Add find modules to the path # Add find modules to the path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
@ -15,6 +18,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
# Optional subsystems # Optional subsystems
option(BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON) option(BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON)
option(BUILD_TESTS "Build Tests using the Clar suite" ON) option(BUILD_TESTS "Build Tests using the Clar suite" ON)
option(BUILD_CLI "Build the command-line interface" ON)
option(BUILD_EXAMPLES "Build library usage example apps" OFF) option(BUILD_EXAMPLES "Build library usage example apps" OFF)
option(BUILD_FUZZERS "Build the fuzz targets" OFF) option(BUILD_FUZZERS "Build the fuzz targets" OFF)
@ -25,7 +29,8 @@ option(USE_NSEC "Support nanosecond precision file mtimes and cti
# Backend selection # Backend selection
option(USE_SSH "Link with libssh2 to enable SSH support" OFF) option(USE_SSH "Link with libssh2 to enable SSH support" OFF)
option(USE_HTTPS "Enable HTTPS support. Can be set to a specific backend" ON) option(USE_HTTPS "Enable HTTPS support. Can be set to a specific backend" ON)
option(USE_SHA1 "Enable SHA1. Can be set to CollisionDetection(ON)/HTTPS/Generic" ON) option(USE_SHA1 "Enable SHA1. Can be set to CollisionDetection(ON)/HTTPS" ON)
option(USE_SHA256 "Enable SHA256. Can be set to HTTPS/Builtin" ON)
option(USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF) option(USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF)
set(USE_HTTP_PARSER "" CACHE STRING "Specifies the HTTP Parser implementation; either system or builtin.") set(USE_HTTP_PARSER "" CACHE STRING "Specifies the HTTP Parser implementation; either system or builtin.")
set(REGEX_BACKEND "" CACHE STRING "Regular expression implementation. One of regcomp_l, pcre2, pcre, regcomp, or builtin.") set(REGEX_BACKEND "" CACHE STRING "Regular expression implementation. One of regcomp_l, pcre2, pcre, regcomp, or builtin.")
@ -127,6 +132,14 @@ if(BUILD_FUZZERS)
endif() endif()
# Export for people who use us as a dependency
if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
endif()
# Summary # Summary
feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:") feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")

40
COPYING
View File

@ -1144,3 +1144,43 @@ worldwide. This software is distributed without any warranty.
See <http://creativecommons.org/publicdomain/zero/1.0/>. See <http://creativecommons.org/publicdomain/zero/1.0/>.
----------------------------------------------------------------------
The built-in SHA256 support (src/hash/rfc6234) is taken from RFC 6234
under the following license:
Copyright (c) 2011 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
- Redistributions of source code must retain the above
copyright notice, this list of conditions and
the following disclaimer.
- 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.
- Neither the name of Internet Society, IETF or IETF Trust, nor
the names of specific contributors, may be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "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 COPYRIGHT OWNER 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.

View File

@ -392,6 +392,8 @@ Here are the bindings to libgit2 that are currently available:
* parrot-libgit2 <https://github.com/letolabs/parrot-libgit2> * parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
* Perl * Perl
* Git-Raw <https://github.com/jacquesg/p5-Git-Raw> * Git-Raw <https://github.com/jacquesg/p5-Git-Raw>
* Pharo Smalltalk
* libgit2-pharo-bindings <https://github.com/pharo-vcs/libgit2-pharo-bindings>
* PHP * PHP
* php-git <https://github.com/libgit2/php-git> * php-git <https://github.com/libgit2/php-git>
* Python * Python
@ -405,6 +407,8 @@ Here are the bindings to libgit2 that are currently available:
* git2-rs <https://github.com/rust-lang/git2-rs> * git2-rs <https://github.com/rust-lang/git2-rs>
* Swift * Swift
* SwiftGit2 <https://github.com/SwiftGit2/SwiftGit2> * SwiftGit2 <https://github.com/SwiftGit2/SwiftGit2>
* Tcl
* lg2 <https://github.com/apnadkarni/tcl-libgit2>
* Vala * Vala
* libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi> * libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>

View File

@ -59,7 +59,7 @@ echo "##########################################################################
echo "## Configuring build environment" echo "## Configuring build environment"
echo "##############################################################################" echo "##############################################################################"
echo cmake -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\" 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}" 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 ""
@ -69,10 +69,11 @@ echo "##########################################################################
# Determine parallelism; newer cmake supports `--build --parallel` but # Determine parallelism; newer cmake supports `--build --parallel` but
# we cannot yet rely on that. # we cannot yet rely on that.
if [ "${CMAKE_GENERATOR}" = "Unix Makefiles" -a "${CORES}" != "" ]; then if [ "${CMAKE_GENERATOR}" = "Unix Makefiles" -a "${CORES}" != "" -a "${CMAKE_BUILD_OPTIONS}" = "" ]; then
BUILDER=(make -j ${CORES}) BUILDER=(make -j ${CORES})
else else
BUILDER=("${CMAKE}" --build .) BUILDER=("${CMAKE}" --build . ${CMAKE_BUILD_OPTIONS})
fi fi
echo "${BUILDER[@]}"
env PATH="${BUILD_PATH}" "${BUILDER[@]}" env PATH="${BUILD_PATH}" "${BUILDER[@]}"

View File

@ -28,17 +28,22 @@ RUN apt-get update && \
FROM apt AS mbedtls FROM apt AS mbedtls
RUN cd /tmp && \ RUN cd /tmp && \
curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
tar -xz && \ tar -xz && \
cd mbedtls-2.16.2 && \ cd mbedtls-mbedtls-2.16.2 && \
scripts/config.pl set MBEDTLS_MD4_C 1 && \ 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 . && \ CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
ninja install && \ ninja install && \
cd .. && \ cd .. && \
rm -rf mbedtls-2.16.2 rm -rf mbedtls-mbedtls-2.16.2
FROM mbedtls AS adduser FROM mbedtls AS adduser
RUN useradd --shell /bin/bash libgit2 --create-home ARG UID=""
ARG GID=""
RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
groupadd ${GROUP_ARG} libgit2 && \
useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
FROM adduser AS configure FROM adduser AS configure
RUN mkdir /var/run/sshd RUN mkdir /var/run/sshd

View File

@ -48,7 +48,12 @@ RUN cd /tmp && \
rm -rf cmake-3.21.1 rm -rf cmake-3.21.1
FROM cmake AS adduser FROM cmake AS adduser
RUN useradd --shell /bin/bash libgit2 --create-home ARG UID=""
ARG GID=""
RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
groupadd ${GROUP_ARG} libgit2 && \
useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
FROM adduser AS configure FROM adduser AS configure
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig

View File

@ -1,6 +1,10 @@
ARG BASE=centos:8 ARG BASE=centos:8
FROM ${BASE} AS yum FROM ${BASE} AS stream
RUN dnf -y --disablerepo '*' --enablerepo=extras swap centos-linux-repos centos-stream-repos && \
dnf -y distro-sync
FROM stream AS yum
RUN yum install -y \ RUN yum install -y \
which \ which \
bzip2 \ bzip2 \
@ -40,7 +44,12 @@ RUN cd /tmp && \
rm -rf valgrind-3.15.0 rm -rf valgrind-3.15.0
FROM valgrind AS adduser FROM valgrind AS adduser
RUN useradd --shell /bin/bash libgit2 --create-home ARG UID=""
ARG GID=""
RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
groupadd ${GROUP_ARG} libgit2 && \
useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
FROM adduser AS configure FROM adduser AS configure
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig

View File

@ -32,9 +32,9 @@ RUN apt-get update && \
FROM apt AS mbedtls FROM apt AS mbedtls
RUN cd /tmp && \ RUN cd /tmp && \
curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
tar -xz && \ tar -xz && \
cd mbedtls-2.16.2 && \ cd mbedtls-mbedtls-2.16.2 && \
scripts/config.pl unset MBEDTLS_AESNI_C && \ scripts/config.pl unset MBEDTLS_AESNI_C && \
scripts/config.pl set MBEDTLS_MD4_C 1 && \ scripts/config.pl set MBEDTLS_MD4_C 1 && \
mkdir build build-msan && \ mkdir build build-msan && \
@ -45,7 +45,7 @@ RUN cd /tmp && \
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 .. && \ 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 && \ ninja install && \
cd .. && \ cd .. && \
rm -rf mbedtls-2.16.2 rm -rf mbedtls-mbedtls-2.16.2
FROM mbedtls AS libssh2 FROM mbedtls AS libssh2
RUN cd /tmp && \ RUN cd /tmp && \
@ -73,7 +73,13 @@ RUN cd /tmp && \
rm -rf valgrind-3.15.0 rm -rf valgrind-3.15.0
FROM valgrind AS adduser FROM valgrind AS adduser
RUN useradd --shell /bin/bash libgit2 --create-home ARG UID=""
ARG GID=""
RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
groupadd ${GROUP_ARG} libgit2 && \
useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
FROM adduser AS configure FROM adduser AS configure
RUN mkdir /var/run/sshd RUN mkdir /var/run/sshd

View File

@ -30,14 +30,14 @@ RUN apt-get update && \
FROM apt AS mbedtls FROM apt AS mbedtls
RUN cd /tmp && \ RUN cd /tmp && \
curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
tar -xz && \ tar -xz && \
cd mbedtls-2.16.2 && \ cd mbedtls-mbedtls-2.16.2 && \
scripts/config.pl set MBEDTLS_MD4_C 1 && \ 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 . && \ CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
ninja install && \ ninja install && \
cd .. && \ cd .. && \
rm -rf mbedtls-2.16.2 rm -rf mbedtls-mbedtls-2.16.2
FROM mbedtls AS libssh2 FROM mbedtls AS libssh2
RUN cd /tmp && \ RUN cd /tmp && \
@ -60,7 +60,13 @@ RUN cd /tmp && \
rm -rf valgrind-3.15.0 rm -rf valgrind-3.15.0
FROM valgrind AS adduser FROM valgrind AS adduser
RUN useradd --shell /bin/bash libgit2 --create-home ARG UID=""
ARG GID=""
RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
groupadd ${GROUP_ARG} libgit2 && \
useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
FROM adduser AS configure FROM adduser AS configure
RUN mkdir /var/run/sshd RUN mkdir /var/run/sshd

View File

@ -37,9 +37,13 @@ DOCKER_REGISTRY_CONTAINER_SHA="${DOCKER_REGISTRY_CONTAINER}:${DOCKER_SHA}"
echo "docker-registry-container-sha=${DOCKER_REGISTRY_CONTAINER_SHA}" >> $GITHUB_ENV echo "docker-registry-container-sha=${DOCKER_REGISTRY_CONTAINER_SHA}" >> $GITHUB_ENV
echo "docker-registry-container-latest=${DOCKER_REGISTRY_CONTAINER}:latest" >> $GITHUB_ENV echo "docker-registry-container-latest=${DOCKER_REGISTRY_CONTAINER}:latest" >> $GITHUB_ENV
echo "::: logging in to ${DOCKER_REGISTRY} as ${GITHUB_ACTOR}"
exists="true" exists="true"
docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false" docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false"
echo "::: pulling ${DOCKER_REGISTRY_CONTAINER_SHA}"
if [ "${exists}" != "false" ]; then if [ "${exists}" != "false" ]; then
docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false" docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false"
fi fi

View File

@ -1,4 +1,6 @@
#!/bin/sh -e #!/bin/sh
set -ex
echo "##############################################################################" echo "##############################################################################"
echo "## Downloading mingw" echo "## Downloading mingw"

6
ci/setup-osx-benchmark.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -ex
brew update
brew install hyperfine

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
set -x set -ex
brew update brew update
brew install pkgconfig zlib curl openssl libssh2 ninja brew install pkgconfig zlib curl openssl libssh2 ninja

20
ci/setup-ubuntu-benchmark.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/sh
set -ex
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
cargo \
cmake \
gcc \
git \
krb5-user \
libkrb5-dev \
libssl-dev \
libz-dev \
make \
ninja-build \
pkgconf
wget https://github.com/sharkdp/hyperfine/releases/download/v1.12.0/hyperfine_1.12.0_amd64.deb
sudo dpkg -i hyperfine_1.12.0_amd64.deb

9
ci/setup-win32-benchmark.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
set -ex
choco install hyperfine zip
CHOCO_PATH=$(mktemp -d)
curl -L https://github.com/ethomson/PurgeStandbyList/releases/download/v1.0/purgestandbylist.1.0.0.nupkg -o "${CHOCO_PATH}/purgestandbylist.1.0.0.nupkg"
choco install purgestandbylist -s $(cygpath -w "${CHOCO_PATH}")

View File

@ -159,10 +159,18 @@ fi
if [ -z "$SKIP_OFFLINE_TESTS" ]; then if [ -z "$SKIP_OFFLINE_TESTS" ]; then
echo "" echo ""
echo "##############################################################################" echo "##############################################################################"
echo "## Running (offline) tests" echo "## Running core tests"
echo "##############################################################################" echo "##############################################################################"
echo ""
echo "Running libgit2 integration (offline) tests"
echo ""
run_test offline run_test offline
echo ""
echo "Running utility tests"
echo ""
run_test util
fi fi
if [ -n "$RUN_INVASIVE_TESTS" ]; then if [ -n "$RUN_INVASIVE_TESTS" ]; then
@ -186,7 +194,7 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
echo "" echo ""
echo "##############################################################################" echo "##############################################################################"
echo "## Running (online) tests" echo "## Running networking (online) tests"
echo "##############################################################################" echo "##############################################################################"
export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository" export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository"
@ -198,9 +206,9 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
# Run the online tests that immutably change global state separately # Run the online tests that immutably change global state separately
# to avoid polluting the test environment. # to avoid polluting the test environment.
echo "" echo ""
echo "##############################################################################" echo "Running custom certificate (online_customcert) tests"
echo "## Running (online_customcert) tests" echo ""
echo "##############################################################################"
run_test online_customcert run_test online_customcert
fi fi

7
cmake/AddClarTest.cmake Normal file
View File

@ -0,0 +1,7 @@
function(ADD_CLAR_TEST project name)
if(NOT USE_LEAK_CHECKER STREQUAL "OFF")
add_test(${name} "${PROJECT_SOURCE_DIR}/script/${USE_LEAK_CHECKER}.sh" "${PROJECT_BINARY_DIR}/${project}" ${ARGN})
else()
add_test(${name} "${PROJECT_BINARY_DIR}/${project}" ${ARGN})
endif()
endfunction(ADD_CLAR_TEST)

View File

@ -16,19 +16,18 @@
# PCRE_FOUND - True if pcre found. # PCRE_FOUND - True if pcre found.
# Look for the header file. # Look for the header file.
find_path(PCRE_INCLUDE_DIR NAMES pcreposix.h) find_path(PCRE_INCLUDE_DIR NAMES pcre.h)
# Look for the library. # Look for the library.
find_library(PCRE_LIBRARY NAMES pcre) find_library(PCRE_LIBRARY NAMES pcre)
find_library(PCRE_POSIX_LIBRARY NAMES pcreposix)
# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE. # Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_POSIX_LIBRARY PCRE_INCLUDE_DIR) find_package_handle_standard_args(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR)
# Copy the results to the output variables. # Copy the results to the output variables.
if(PCRE_FOUND) if(PCRE_FOUND)
set(PCRE_LIBRARIES ${PCRE_LIBRARY} ${PCRE_POSIX_LIBRARY}) set(PCRE_LIBRARIES ${PCRE_LIBRARY})
set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR}) set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
else(PCRE_FOUND) else(PCRE_FOUND)
set(PCRE_LIBRARIES) set(PCRE_LIBRARIES)

View File

@ -16,7 +16,7 @@
# PCRE2_FOUND - True if pcre found. # PCRE2_FOUND - True if pcre found.
# Look for the header file. # Look for the header file.
find_path(PCRE2_INCLUDE_DIR NAMES pcre2posix.h) find_path(PCRE2_INCLUDE_DIR NAMES pcre2.h)
# Look for the library. # Look for the library.
find_library(PCRE2_LIBRARY NAMES pcre2-8) find_library(PCRE2_LIBRARY NAMES pcre2-8)

View File

@ -64,7 +64,7 @@ if(USE_HTTPS)
if(NOT CERT_LOCATION) if(NOT CERT_LOCATION)
message(STATUS "Auto-detecting default certificates location") message(STATUS "Auto-detecting default certificates location")
if(CMAKE_SYSTEM_NAME MATCHES Darwin) if(EXISTS "/usr/local/opt/openssl/bin/openssl")
# Check for an Homebrew installation # Check for an Homebrew installation
set(OPENSSL_CMD "/usr/local/opt/openssl/bin/openssl") set(OPENSSL_CMD "/usr/local/opt/openssl/bin/openssl")
else() else()

View File

@ -4,6 +4,9 @@ include(SanitizeBool)
# USE_SHA1=CollisionDetection(ON)/HTTPS/Generic/OFF # USE_SHA1=CollisionDetection(ON)/HTTPS/Generic/OFF
sanitizebool(USE_SHA1) sanitizebool(USE_SHA1)
sanitizebool(USE_SHA256)
# sha1
if(USE_SHA1 STREQUAL ON) if(USE_SHA1 STREQUAL ON)
SET(USE_SHA1 "CollisionDetection") SET(USE_SHA1 "CollisionDetection")
@ -21,32 +24,77 @@ endif()
if(USE_SHA1 STREQUAL "CollisionDetection") if(USE_SHA1 STREQUAL "CollisionDetection")
set(GIT_SHA1_COLLISIONDETECT 1) set(GIT_SHA1_COLLISIONDETECT 1)
add_definitions(-DSHA1DC_NO_STANDARD_INCLUDES=1)
add_definitions(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\")
add_definitions(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\")
elseif(USE_SHA1 STREQUAL "OpenSSL") elseif(USE_SHA1 STREQUAL "OpenSSL")
# OPENSSL_FOUND should already be set, we're checking USE_HTTPS
set(GIT_SHA1_OPENSSL 1) set(GIT_SHA1_OPENSSL 1)
elseif(USE_SHA1 STREQUAL "OpenSSL-Dynamic")
set(GIT_SHA1_OPENSSL 1)
set(GIT_SHA1_OPENSSL_DYNAMIC 1)
list(APPEND LIBGIT2_SYSTEM_LIBS dl)
elseif(USE_SHA1 STREQUAL "CommonCrypto")
set(GIT_SHA1_COMMON_CRYPTO 1)
elseif(USE_SHA1 STREQUAL "mbedTLS")
set(GIT_SHA1_MBEDTLS 1)
elseif(USE_SHA1 STREQUAL "Win32")
set(GIT_SHA1_WIN32 1)
else()
message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
endif()
# sha256
if(USE_SHA256 STREQUAL ON AND USE_HTTPS)
SET(USE_SHA256 "HTTPS")
elseif(USE_SHA256 STREQUAL ON)
SET(USE_SHA256 "Builtin")
endif()
if(USE_SHA256 STREQUAL "HTTPS")
if(USE_HTTPS STREQUAL "SecureTransport")
set(USE_SHA256 "CommonCrypto")
elseif(USE_HTTPS STREQUAL "WinHTTP")
set(USE_SHA256 "Win32")
elseif(USE_HTTPS)
set(USE_SHA256 ${USE_HTTPS})
endif()
endif()
if(USE_SHA256 STREQUAL "Builtin")
set(GIT_SHA256_BUILTIN 1)
elseif(USE_SHA256 STREQUAL "OpenSSL")
set(GIT_SHA256_OPENSSL 1)
elseif(USE_SHA256 STREQUAL "OpenSSL-Dynamic")
set(GIT_SHA256_OPENSSL 1)
set(GIT_SHA256_OPENSSL_DYNAMIC 1)
list(APPEND LIBGIT2_SYSTEM_LIBS dl)
elseif(USE_SHA256 STREQUAL "CommonCrypto")
set(GIT_SHA256_COMMON_CRYPTO 1)
elseif(USE_SHA256 STREQUAL "mbedTLS")
set(GIT_SHA256_MBEDTLS 1)
elseif(USE_SHA256 STREQUAL "Win32")
set(GIT_SHA256_WIN32 1)
else()
message(FATAL_ERROR "Asked for unknown SHA256 backend: ${USE_SHA256}")
endif()
# add library requirements
if(USE_SHA1 STREQUAL "OpenSSL" OR USE_SHA256 STREQUAL "OpenSSL")
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
list(APPEND LIBGIT2_PC_LIBS "-lssl") list(APPEND LIBGIT2_PC_LIBS "-lssl")
else() else()
list(APPEND LIBGIT2_PC_REQUIRES "openssl") list(APPEND LIBGIT2_PC_REQUIRES "openssl")
endif() endif()
elseif(USE_SHA1 STREQUAL "CommonCrypto") endif()
set(GIT_SHA1_COMMON_CRYPTO 1)
elseif(USE_SHA1 STREQUAL "mbedTLS") if(USE_SHA1 STREQUAL "mbedTLS" OR USE_SHA256 STREQUAL "mbedTLS")
set(GIT_SHA1_MBEDTLS 1)
list(APPEND LIBGIT2_SYSTEM_INCLUDES ${MBEDTLS_INCLUDE_DIR}) list(APPEND LIBGIT2_SYSTEM_INCLUDES ${MBEDTLS_INCLUDE_DIR})
list(APPEND LIBGIT2_SYSTEM_LIBS ${MBEDTLS_LIBRARIES}) list(APPEND LIBGIT2_SYSTEM_LIBS ${MBEDTLS_LIBRARIES})
# mbedTLS has no pkgconfig file, hence we can't require it # mbedTLS has no pkgconfig file, hence we can't require it
# https://github.com/ARMmbed/mbedtls/issues/228 # https://github.com/ARMmbed/mbedtls/issues/228
# For now, pass its link flags as our own # For now, pass its link flags as our own
list(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES}) list(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES})
elseif(USE_SHA1 STREQUAL "Win32")
set(GIT_SHA1_WIN32 1)
elseif(NOT (USE_SHA1 STREQUAL "Generic"))
message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
endif() endif()
add_feature_info(SHA ON "using ${USE_SHA1}") # notify feature enablement
add_feature_info(SHA1 ON "using ${USE_SHA1}")
add_feature_info(SHA256 ON "using ${USE_SHA256}")

View File

@ -1,39 +1,79 @@
v1.4.3 v1.5
------ ----
🔒 This is a security release to provide compatibility with git's changes to address [CVE 2022-24765](https://github.blog/2022-04-12-git-security-vulnerability-announced/). This is release v1.5.0, "Stubentiger". This release adds the basis for an experimental CLI, continues preparing for SHA256 support, adds a benchmarking utility, and has numerous new features and bugfixes.
**libgit2 is not directly affected** by this vulnerability, because libgit2 does not directly invoke any executable. But we are providing these changes as a security release for any users that use libgit2 for repository discovery and then _also_ use git on that repository. In this release, we will now validate that the user opening the repository is the same user that owns the on-disk repository. This is to match git's behavior.
In addition, we are providing several correctness fixes where invalid input can lead to a crash. These may prevent possible denial of service attacks. At this time there are not known exploits to these issues.
Full list of changes:
* Validate repository directory ownership (v1.4) by @ethomson in https://github.com/libgit2/libgit2/pull/6267
* midx: Fix an undefined behavior (left-shift signed overflow) by @lhchavez in https://github.com/libgit2/libgit2/pull/6260
* fetch: support OID refspec without dst by @ethomson in https://github.com/libgit2/libgit2/pull/6251
* Fix crash when regenerating a patch with unquoted spaces in filename by @jorio in https://github.com/libgit2/libgit2/pull/6244
All users of the v1.4 release line are recommended to upgrade.
**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.2...v1.4.3
v1.4.2
------
This is a bugfix release with the following changes:
## What's Changed
### New features
* The beginnings of a git-compatible CLI for testing and benchmarking by @ethomson in https://github.com/libgit2/libgit2/pull/6133
* Add `clone` support to the CLI @ethomson in https://github.com/libgit2/libgit2/pull/6274
* A benchmarking suite to compare libgit2 functionality against git by @ethomson in https://github.com/libgit2/libgit2/pull/6235
* SHA256: add a SHA256 implementation backend by @ethomson in https://github.com/libgit2/libgit2/pull/6144
* SHA256: support dynamically loaded openssl by @ethomson in https://github.com/libgit2/libgit2/pull/6258
* Transport: introduce `git_transport_smart_remote_connect_options` by @lhchavez in https://github.com/libgit2/libgit2/pull/6278
### Bug fixes
* Free parent and ref in lg2_commit before returning. by @apnadkarni in https://github.com/libgit2/libgit2/pull/6219
* xdiff: use xdl_free not free by @ethomson in https://github.com/libgit2/libgit2/pull/6223
* remote: do store the update_tips callback error value by @carlosmn in https://github.com/libgit2/libgit2/pull/6226 * remote: do store the update_tips callback error value by @carlosmn in https://github.com/libgit2/libgit2/pull/6226
* win32: `find_system_dirs` does not return `GIT_ENOTFOUND` by @ethomson in https://github.com/libgit2/libgit2/pull/6228 * win32: `find_system_dirs` does not return `GIT_ENOTFOUND` by @ethomson in https://github.com/libgit2/libgit2/pull/6228
* Some minor fixes for issues discovered by coverity by @ethomson in https://github.com/libgit2/libgit2/pull/6238
* Fix a string concatenation bug when validating extensions by @bierbaum in https://github.com/libgit2/libgit2/pull/6246
* fetch: support OID refspec without dst by @ethomson in https://github.com/libgit2/libgit2/pull/6251
* Fix crash when regenerating a patch with unquoted spaces in filename by @jorio in https://github.com/libgit2/libgit2/pull/6244
* midx: Fix an undefined behavior (left-shift signed overflow) by @lhchavez in https://github.com/libgit2/libgit2/pull/6260
* Validate repository directory ownership by @ethomson in https://github.com/libgit2/libgit2/pull/6266
* midx: fix large offset table check. by @ccstolley in https://github.com/libgit2/libgit2/pull/6309
* midx: do not verify the checksum on load by @carlosmn in https://github.com/libgit2/libgit2/pull/6291
* revparse: Remove error-prone, redundant test by @dongcarl in https://github.com/libgit2/libgit2/pull/6299
* refs: fix missing error message by @zawata in https://github.com/libgit2/libgit2/pull/6305
* CLI: progress updates by @ethomson in https://github.com/libgit2/libgit2/pull/6319
* A couple of simplications around mwindow by @carlosmn in https://github.com/libgit2/libgit2/pull/6288
* config: update config entry iteration lifecycle by @ethomson in https://github.com/libgit2/libgit2/pull/6320
* repo: allow administrator to own the configuration by @ethomson in https://github.com/libgit2/libgit2/pull/6321
* filter: Fix Segfault by @zawata in https://github.com/libgit2/libgit2/pull/6303
* ntlmclient: LibreSSL 3.5 removed HMAC_CTX_cleanup by @vishwin in https://github.com/libgit2/libgit2/pull/6340
* Fix internal git_sysdir_find* function usage within public git_config_find* functions by @kcsaul in https://github.com/libgit2/libgit2/pull/6335
* fix interactive rebase detect. by @i-tengfei in https://github.com/libgit2/libgit2/pull/6334
* cmake: drop posix dependency from pcre* detection by @jpalus in https://github.com/libgit2/libgit2/pull/6333
* Fix erroneously lax configuration ownership checks by @ethomson in https://github.com/libgit2/libgit2/pull/6341
* pack: don't pretend we support pack files v3 by @ethomson in https://github.com/libgit2/libgit2/pull/6347
* Fix creation of branches and tags with invalid names by @lya001 in https://github.com/libgit2/libgit2/pull/6348
### Security fixes
* Fixes for CVE 2022-29187 by @ethomson in https://github.com/libgit2/libgit2/pull/6349
* zlib: update bundled zlib to v1.2.12 by @ethomson in https://github.com/libgit2/libgit2/pull/6350
### Code cleanups
* sha256: refactoring in preparation for sha256 by @ethomson in https://github.com/libgit2/libgit2/pull/6265
* remote: Delete a now-inexistent API declaration by @lhchavez in https://github.com/libgit2/libgit2/pull/6276
* Fix missing include by @cschlack in https://github.com/libgit2/libgit2/pull/6277
### Build and CI improvements
* meta: show build status for v1.3 and v1.4 branches by @ethomson in https://github.com/libgit2/libgit2/pull/6216
* cmake: Fix package name for system http-parser by @mgorny in https://github.com/libgit2/libgit2/pull/6217
* meta: update version number to v1.5.0-alpha by @ethomson in https://github.com/libgit2/libgit2/pull/6220
* cmake: export libraries needed to compile against libgit2 by @ethomson in https://github.com/libgit2/libgit2/pull/6239
* clone: update bitbucket tests by @ethomson in https://github.com/libgit2/libgit2/pull/6252
* diff: don't stat empty file on arm32 (flaky test) by @ethomson in https://github.com/libgit2/libgit2/pull/6259
* tests: support flaky stat by @ethomson in https://github.com/libgit2/libgit2/pull/6262
* Include test results data in CI by @ethomson in https://github.com/libgit2/libgit2/pull/6306
* Add a .clang-format with our style by @ethomson in https://github.com/libgit2/libgit2/pull/6023
* CI: limits actions scheduled workflows to the main repo by @ethomson in https://github.com/libgit2/libgit2/pull/6342
* ci: update dockerfiles for mbedTLS new url by @ethomson in https://github.com/libgit2/libgit2/pull/6343
### Documentation improvements
* Add Pharo to language bindings by @theseion in https://github.com/libgit2/libgit2/pull/6310
* Add link to Tcl bindings for libgit2 by @apnadkarni in https://github.com/libgit2/libgit2/pull/6318
* fix couple of typos by @SkinnyMind in https://github.com/libgit2/libgit2/pull/6287
* update documentation for default status options by @ethomson in https://github.com/libgit2/libgit2/pull/6322
v1.4.1 ## New Contributors
------ * @bierbaum made their first contribution in https://github.com/libgit2/libgit2/pull/6246
* @dongcarl made their first contribution in https://github.com/libgit2/libgit2/pull/6299
* @SkinnyMind made their first contribution in https://github.com/libgit2/libgit2/pull/6287
* @zawata made their first contribution in https://github.com/libgit2/libgit2/pull/6305
* @vishwin made their first contribution in https://github.com/libgit2/libgit2/pull/6340
* @i-tengfei made their first contribution in https://github.com/libgit2/libgit2/pull/6334
* @jpalus made their first contribution in https://github.com/libgit2/libgit2/pull/6333
* @lya001 made their first contribution in https://github.com/libgit2/libgit2/pull/6348
This is a bugfix release with the following changes: **Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.0...v1.5.0
* xdiff: use xdl_free not free by @ethomson
* cmake: Fix package name for system http-parser by @mgorny
* Free parent and ref in lg2_commit before returning by @apnadkarni
v1.4 v1.4
---- ----

View File

@ -1,3 +1,5 @@
# examples: code usage examples of libgit2
file(GLOB SRC_EXAMPLES *.c *.h) file(GLOB SRC_EXAMPLES *.c *.h)
add_executable(lg2 ${SRC_EXAMPLES}) add_executable(lg2 ${SRC_EXAMPLES})
@ -10,7 +12,7 @@ target_include_directories(lg2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_
target_include_directories(lg2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES}) target_include_directories(lg2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
if(WIN32 OR ANDROID) if(WIN32 OR ANDROID)
target_link_libraries(lg2 git2) target_link_libraries(lg2 libgit2package)
else() else()
target_link_libraries(lg2 git2 pthread) target_link_libraries(lg2 libgit2package pthread)
endif() endif()

View File

@ -1,3 +1,5 @@
# fuzzers: libFuzzer and standalone fuzzing utilities
if(BUILD_FUZZERS AND NOT USE_STANDALONE_FUZZERS) if(BUILD_FUZZERS AND NOT USE_STANDALONE_FUZZERS)
set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer-no-link") set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer-no-link")
add_c_flag(-fsanitize=fuzzer) add_c_flag(-fsanitize=fuzzer)

View File

@ -94,7 +94,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
fprintf(stderr, "Failed to compute the SHA1 hash\n"); fprintf(stderr, "Failed to compute the SHA1 hash\n");
abort(); abort();
} }
if (git_indexer_append(indexer, &oid, sizeof(oid), &stats) < 0) { if (git_indexer_append(indexer, &oid.id, GIT_OID_RAWSZ, &stats) < 0) {
goto cleanup; goto cleanup;
} }
} }

View File

@ -129,8 +129,8 @@ GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter);
* See `git_tag_create()` for rules about valid names. * See `git_tag_create()` for rules about valid names.
* *
* Note that if the move succeeds, the old reference object will not * Note that if the move succeeds, the old reference object will not
+ be valid anymore, and should be freed immediately by the user using * be valid anymore, and should be freed immediately by the user using
+ `git_reference_free()`. * `git_reference_free()`.
* *
* @param out New reference object for the updated name. * @param out New reference object for the updated name.
* *

View File

@ -121,6 +121,17 @@ GIT_BEGIN_DECL
*/ */
GIT_EXTERN(int) git_libgit2_version(int *major, int *minor, int *rev); GIT_EXTERN(int) git_libgit2_version(int *major, int *minor, int *rev);
/**
* Return the prerelease state of the libgit2 library currently being
* used. For nightly builds during active development, this will be
* "alpha". Releases may have a "beta" or release candidate ("rc1",
* "rc2", etc) prerelease. For a final release, this function returns
* NULL.
*
* @return the name of the prerelease state or NULL
*/
GIT_EXTERN(const char *) git_libgit2_prerelease(void);
/** /**
* Combinations of these values describe the features with which libgit2 * Combinations of these values describe the features with which libgit2
* was compiled * was compiled

View File

@ -122,7 +122,7 @@ typedef struct {
* global configuration file. * global configuration file.
* *
* This method will not guess the path to the xdg compatible * This method will not guess the path to the xdg compatible
* config file (.config/git/config). * config file (`.config/git/config`).
* *
* @param out Pointer to a user-allocated git_buf in which to store the path * @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a global configuration file has been found. Its path will be stored in `out`. * @return 0 if a global configuration file has been found. Its path will be stored in `out`.
@ -149,8 +149,8 @@ GIT_EXTERN(int) git_config_find_xdg(git_buf *out);
/** /**
* Locate the path to the system configuration file * Locate the path to the system configuration file
* *
* If /etc/gitconfig doesn't exist, it will look for * If `/etc/gitconfig` doesn't exist, it will look for
* %PROGRAMFILES%\Git\etc\gitconfig. * `%PROGRAMFILES%\Git\etc\gitconfig`.
* *
* @param out Pointer to a user-allocated git_buf in which to store the path * @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a system configuration file has been * @return 0 if a system configuration file has been
@ -161,7 +161,7 @@ GIT_EXTERN(int) git_config_find_system(git_buf *out);
/** /**
* Locate the path to the configuration file in ProgramData * Locate the path to the configuration file in ProgramData
* *
* Look for the file in %PROGRAMDATA%\Git\config used by portable git. * Look for the file in `%PROGRAMDATA%\Git\config` used by portable git.
* *
* @param out Pointer to a user-allocated git_buf in which to store the path * @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a ProgramData configuration file has been * @return 0 if a ProgramData configuration file has been
@ -449,8 +449,8 @@ GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, cons
/** /**
* Return the current entry and advance the iterator * Return the current entry and advance the iterator
* *
* The pointers returned by this function are valid until the iterator * The pointers returned by this function are valid until the next call
* is freed. * to `git_config_next` or until the iterator is freed.
* *
* @param entry pointer to store the entry * @param entry pointer to store the entry
* @param iter the iterator * @param iter the iterator

View File

@ -436,6 +436,8 @@ GIT_EXTERN(int) git_diff_format_email_options_init(
#define GITERR_WORKTREE GIT_ERROR_WORKTREE #define GITERR_WORKTREE GIT_ERROR_WORKTREE
#define GITERR_SHA1 GIT_ERROR_SHA1 #define GITERR_SHA1 GIT_ERROR_SHA1
#define GIT_ERROR_SHA1 GIT_ERROR_SHA
/** /**
* Return the last `git_error` object that was generated for the * Return the last `git_error` object that was generated for the
* current thread. This is an alias of `git_error_last` and is * current thread. This is an alias of `git_error_last` and is

View File

@ -107,7 +107,7 @@ typedef enum {
GIT_ERROR_FILESYSTEM, GIT_ERROR_FILESYSTEM,
GIT_ERROR_PATCH, GIT_ERROR_PATCH,
GIT_ERROR_WORKTREE, GIT_ERROR_WORKTREE,
GIT_ERROR_SHA1, GIT_ERROR_SHA,
GIT_ERROR_HTTP, GIT_ERROR_HTTP,
GIT_ERROR_INTERNAL GIT_ERROR_INTERNAL
} git_error_t; } git_error_t;
@ -131,7 +131,8 @@ GIT_EXTERN(const git_error *) git_error_last(void);
GIT_EXTERN(void) git_error_clear(void); GIT_EXTERN(void) git_error_clear(void);
/** /**
* Set the error message string for this thread. * Set the error message string for this thread, using `printf`-style
* formatting.
* *
* This function is public so that custom ODB backends and the like can * This function is public so that custom ODB backends and the like can
* relay an error message through libgit2. Most regular users of libgit2 * relay an error message through libgit2. Most regular users of libgit2
@ -144,7 +145,20 @@ GIT_EXTERN(void) git_error_clear(void);
* *
* @param error_class One of the `git_error_t` enum above describing the * @param error_class One of the `git_error_t` enum above describing the
* general subsystem that is responsible for the error. * general subsystem that is responsible for the error.
* @param string The formatted error message to keep * @param fmt The `printf`-style format string; subsequent arguments must
* be the arguments for the format string.
*/
GIT_EXTERN(void) git_error_set(int error_class, const char *fmt, ...)
GIT_FORMAT_PRINTF(2, 3);
/**
* Set the error message string for this thread. This function is like
* `git_error_set` but takes a static string instead of a `printf`-style
* format.
*
* @param error_class One of the `git_error_t` enum above describing the
* general subsystem that is responsible for the error.
* @param string The error message to keep
* @return 0 on success or -1 on failure * @return 0 on success or -1 on failure
*/ */
GIT_EXTERN(int) git_error_set_str(int error_class, const char *string); GIT_EXTERN(int) git_error_set_str(int error_class, const char *string);

View File

@ -603,7 +603,7 @@ GIT_EXTERN(int) git_merge_commits(
* completes, resolve any conflicts and prepare a commit. * completes, resolve any conflicts and prepare a commit.
* *
* For compatibility with git, the repository is put into a merging * For compatibility with git, the repository is put into a merging
* state. Once the commit is done (or if the uses wishes to abort), * state. Once the commit is done (or if the user wishes to abort),
* you should clear this state by calling * you should clear this state by calling
* `git_repository_state_cleanup()`. * `git_repository_state_cleanup()`.
* *

View File

@ -227,13 +227,16 @@ typedef struct {
/** /**
* The `show` value is one of the `git_status_show_t` constants that * The `show` value is one of the `git_status_show_t` constants that
* control which files to scan and in what order. * control which files to scan and in what order. The default is
* `GIT_STATUS_SHOW_INDEX_AND_WORKDIR`.
*/ */
git_status_show_t show; git_status_show_t show;
/** /**
* The `flags` value is an OR'ed combination of the * The `flags` value is an OR'ed combination of the
* `git_status_opt_t` values above. * `git_status_opt_t` values above. The default is
* `GIT_STATUS_OPT_DEFAULTS`, which matches git's default
* behavior.
*/ */
unsigned int flags; unsigned int flags;

View File

@ -8,6 +8,8 @@
#ifndef INCLUDE_sys_git_remote_h #ifndef INCLUDE_sys_git_remote_h
#define INCLUDE_sys_git_remote_h #define INCLUDE_sys_git_remote_h
#include "git2/remote.h"
/** /**
* @file git2/sys/remote.h * @file git2/sys/remote.h
* @brief Low-level remote functionality for custom transports * @brief Low-level remote functionality for custom transports
@ -26,6 +28,19 @@ typedef enum {
GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1), GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1),
} git_remote_capability_t; } git_remote_capability_t;
/**
* Disposes libgit2-initialized fields from a git_remote_connect_options.
* This should only be used for git_remote_connect_options returned by
* git_transport_remote_connect_options.
*
* Note that this does not free the `git_remote_connect_options` itself, just
* the memory pointed to by it.
*
* @param opts The `git_remote_connect_options` struct to dispose.
*/
GIT_EXTERN(void) git_remote_connect_options_dispose(
git_remote_connect_options *opts);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL
#endif #endif

View File

@ -9,10 +9,11 @@
#define INCLUDE_sys_git_transport_h #define INCLUDE_sys_git_transport_h
#include "git2/net.h" #include "git2/net.h"
#include "git2/proxy.h"
#include "git2/remote.h"
#include "git2/strarray.h"
#include "git2/transport.h" #include "git2/transport.h"
#include "git2/types.h" #include "git2/types.h"
#include "git2/strarray.h"
#include "git2/proxy.h"
/** /**
* @file git2/sys/transport.h * @file git2/sys/transport.h
@ -261,14 +262,17 @@ GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport,
GIT_EXTERN(int) git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods); GIT_EXTERN(int) git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods);
/** /**
* Get a copy of the proxy options * Get a copy of the remote connect options
* *
* The url is copied and must be freed by the caller. * All data is copied and must be freed by the caller by calling
* `git_remote_connect_options_dispose`.
* *
* @param out options struct to fill * @param out options struct to fill
* @param transport the transport to extract the data from. * @param transport the transport to extract the data from.
*/ */
GIT_EXTERN(int) git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport); GIT_EXTERN(int) git_transport_remote_connect_options(
git_remote_connect_options *out,
git_transport *transport);
/* /*
*** End of base transport interface *** *** End of base transport interface ***

View File

@ -7,12 +7,33 @@
#ifndef INCLUDE_git_version_h__ #ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__ #define INCLUDE_git_version_h__
#define LIBGIT2_VERSION "1.4.3" /**
* The version string for libgit2. This string follows semantic
* versioning (v2) guidelines.
*/
#define LIBGIT2_VERSION "1.5.0"
/** The major version number for this version of libgit2. */
#define LIBGIT2_VER_MAJOR 1 #define LIBGIT2_VER_MAJOR 1
#define LIBGIT2_VER_MINOR 4
#define LIBGIT2_VER_REVISION 3 /** The minor version number for this version of libgit2. */
#define LIBGIT2_VER_MINOR 5
/** The revision ("teeny") version number for this version of libgit2. */
#define LIBGIT2_VER_REVISION 0
/** The Windows DLL patch number for this version of libgit2. */
#define LIBGIT2_VER_PATCH 0 #define LIBGIT2_VER_PATCH 0
#define LIBGIT2_SOVERSION "1.4" /**
* The prerelease string for this version of libgit2. For development
* (nightly) builds, this will be "alpha". For prereleases, this will be
* a prerelease name like "beta" or "rc1". For final releases, this will
* be `NULL`.
*/
#define LIBGIT2_VER_PRERELEASE NULL
/** The library ABI soversion for this version of libgit2. */
#define LIBGIT2_SOVERSION "1.5"
#endif #endif

View File

@ -1,6 +1,6 @@
{ {
"name": "libgit2", "name": "libgit2",
"version": "1.4.3", "version": "1.5.0",
"repo": "https://github.com/libgit2/libgit2", "repo": "https://github.com/libgit2/libgit2",
"description": " A cross-platform, linkable library implementation of Git that you can use in your application.", "description": " A cross-platform, linkable library implementation of Git that you can use in your application.",
"install": "mkdir build && cd build && cmake .. && cmake --build ." "install": "mkdir build && cd build && cmake .. && cmake --build ."

View File

@ -1,12 +1,22 @@
add_library(git2internal OBJECT) # The main libgit2 source tree: this CMakeLists.txt identifies platform
set_target_properties(git2internal PROPERTIES C_STANDARD 90) # support and includes the subprojects that make up core libgit2 support.
set_target_properties(git2internal PROPERTIES C_EXTENSIONS OFF)
#
# Optional build configuration settings
#
if(DEPRECATE_HARD) if(DEPRECATE_HARD)
add_definitions(-DGIT_DEPRECATE_HARD) add_definitions(-DGIT_DEPRECATE_HARD)
endif() endif()
if(USE_LEAK_CHECKER STREQUAL "valgrind")
add_definitions(-DVALGRIND)
endif()
#
# Optional debugging functionality
#
if(DEBUG_POOL) if(DEBUG_POOL)
set(GIT_DEBUG_POOL 1) set(GIT_DEBUG_POOL 1)
endif() endif()
@ -22,29 +32,32 @@ if(DEBUG_STRICT_OPEN)
endif() endif()
add_feature_info(debugopen GIT_DEBUG_STRICT_OPEN "path validation in open") add_feature_info(debugopen GIT_DEBUG_STRICT_OPEN "path validation in open")
#
# Optional feature enablement
#
include(PkgBuildConfig) include(SelectGSSAPI)
include(SanitizeBool) include(SelectHTTPSBackend)
include(SelectHashes)
include(SelectHTTPParser)
include(SelectRegex)
include(SelectSSH)
include(SelectWinHTTP)
include(SelectZlib)
# This variable will contain the libraries we need to put into #
# libgit2.pc's Requires.private. That is, what we're linking to or # Platform support
# what someone who's statically linking us needs to link to. #
set(LIBGIT2_PC_REQUIRES "")
# This will be set later if we use the system's http-parser library or
# use iconv (OSX) and will be written to the Libs.private field in the
# pc file.
set(LIBGIT2_PC_LIBS "")
set(LIBGIT2_INCLUDES # futimes/futimens
"${CMAKE_CURRENT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/src"
"${PROJECT_SOURCE_DIR}/include")
if(HAVE_FUTIMENS) if(HAVE_FUTIMENS)
set(GIT_USE_FUTIMENS 1) set(GIT_USE_FUTIMENS 1)
endif () endif ()
add_feature_info(futimens GIT_USE_FUTIMENS "futimens support") add_feature_info(futimens GIT_USE_FUTIMENS "futimens support")
# qsort
check_prototype_definition(qsort_r 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 *))" "void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))"
"" "stdlib.h" GIT_QSORT_R_BSD) "" "stdlib.h" GIT_QSORT_R_BSD)
@ -55,69 +68,85 @@ check_prototype_definition(qsort_r
check_function_exists(qsort_s GIT_QSORT_S) check_function_exists(qsort_s GIT_QSORT_S)
# random / entropy data
check_function_exists(getentropy GIT_RAND_GETENTROPY) check_function_exists(getentropy GIT_RAND_GETENTROPY)
check_function_exists(getloadavg GIT_RAND_GETLOADAVG)
# Find required dependencies # determine architecture of the machine
if(WIN32) if(CMAKE_SIZEOF_VOID_P EQUAL 8)
list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32) set(GIT_ARCH_64 1)
elseif(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl) set(GIT_ARCH_32 1)
list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl") elseif(CMAKE_SIZEOF_VOID_P)
elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku") message(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
list(APPEND LIBGIT2_SYSTEM_LIBS network) else()
list(APPEND LIBGIT2_PC_LIBS "-lnetwork") message(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
endif() endif()
# nanosecond mtime/ctime support
if(USE_NSEC)
set(GIT_USE_NSEC 1)
endif()
# high-resolution stat support
if(HAVE_STRUCT_STAT_ST_MTIM)
set(GIT_USE_STAT_MTIM 1)
elseif(HAVE_STRUCT_STAT_ST_MTIMESPEC)
set(GIT_USE_STAT_MTIMESPEC 1)
elseif(HAVE_STRUCT_STAT_ST_MTIME_NSEC)
set(GIT_USE_STAT_MTIME_NSEC 1)
endif()
# realtime support
check_library_exists(rt clock_gettime "time.h" NEED_LIBRT) check_library_exists(rt clock_gettime "time.h" NEED_LIBRT)
if(NEED_LIBRT) if(NEED_LIBRT)
list(APPEND LIBGIT2_SYSTEM_LIBS rt) list(APPEND LIBGIT2_SYSTEM_LIBS rt)
list(APPEND LIBGIT2_PC_LIBS "-lrt") list(APPEND LIBGIT2_PC_LIBS "-lrt")
endif() endif()
# platform libraries
if(WIN32)
list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl)
list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Haiku")
list(APPEND LIBGIT2_SYSTEM_LIBS gnu network)
list(APPEND LIBGIT2_PC_LIBS "-lgnu -lnetwork")
endif()
if(AMIGA)
add_definitions(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
endif()
# threads
if(USE_THREADS) if(USE_THREADS)
if(NOT WIN32)
find_package(Threads REQUIRED)
list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT}) list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT}) list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
endif() endif()
set(GIT_THREADS 1)
endif()
add_feature_info(threadsafe USE_THREADS "threadsafe support") add_feature_info(threadsafe USE_THREADS "threadsafe support")
#
# Optional bundled features
#
if(WIN32 AND EMBED_SSH_PATH) # ntlmclient
file(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
list(SORT SRC_SSH)
target_sources(git2internal PRIVATE ${SRC_SSH})
list(APPEND LIBGIT2_SYSTEM_INCLUDES "${EMBED_SSH_PATH}/include")
file(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
set(GIT_SSH 1)
endif()
include(SelectHTTPSBackend)
include(SelectHashes)
include(SelectHTTPParser)
include(SelectRegex)
include(SelectSSH)
include(SelectWinHTTP)
include(SelectZlib)
if(USE_SHA1 STREQUAL "CollisionDetection")
file(GLOB SRC_SHA1 hash/sha1/collisiondetect.* hash/sha1/sha1dc/*)
elseif(USE_SHA1 STREQUAL "OpenSSL")
file(GLOB SRC_SHA1 hash/sha1/openssl.*)
elseif(USE_SHA1 STREQUAL "CommonCrypto")
file(GLOB SRC_SHA1 hash/sha1/common_crypto.*)
elseif(USE_SHA1 STREQUAL "mbedTLS")
file(GLOB SRC_SHA1 hash/sha1/mbedtls.*)
elseif(USE_SHA1 STREQUAL "Win32")
file(GLOB SRC_SHA1 hash/sha1/win32.*)
elseif(USE_SHA1 STREQUAL "Generic")
file(GLOB SRC_SHA1 hash/sha1/generic.*)
endif()
list(APPEND SRC_SHA1 "hash/sha1.h")
target_sources(git2internal PRIVATE ${SRC_SHA1})
# Optional external dependency: ntlmclient
if(USE_NTLMCLIENT) if(USE_NTLMCLIENT)
set(GIT_NTLM 1) set(GIT_NTLM 1)
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/ntlmclient" "${PROJECT_BINARY_DIR}/deps/ntlmclient") add_subdirectory("${PROJECT_SOURCE_DIR}/deps/ntlmclient" "${PROJECT_BINARY_DIR}/deps/ntlmclient")
@ -126,11 +155,10 @@ if(USE_NTLMCLIENT)
endif() endif()
add_feature_info(ntlmclient GIT_NTLM "NTLM authentication support for Unix") add_feature_info(ntlmclient GIT_NTLM "NTLM authentication support for Unix")
# Optional external dependency: GSSAPI #
# Optional external dependencies
include(SelectGSSAPI) # iconv
# Optional external dependency: iconv
if(USE_ICONV) if(USE_ICONV)
find_package(Iconv) find_package(Iconv)
endif() endif()
@ -142,166 +170,28 @@ if(ICONV_FOUND)
endif() endif()
add_feature_info(iconv GIT_USE_ICONV "iconv encoding conversion support") add_feature_info(iconv GIT_USE_ICONV "iconv encoding conversion support")
#
if(USE_THREADS) # Configure support
if(NOT WIN32) #
find_package(Threads REQUIRED)
endif()
set(GIT_THREADS 1)
endif()
if(USE_NSEC)
set(GIT_USE_NSEC 1)
endif()
if(HAVE_STRUCT_STAT_ST_MTIM)
set(GIT_USE_STAT_MTIM 1)
elseif(HAVE_STRUCT_STAT_ST_MTIMESPEC)
set(GIT_USE_STAT_MTIMESPEC 1)
elseif(HAVE_STRUCT_STAT_ST_MTIME_NSEC)
set(GIT_USE_STAT_MTIME_NSEC 1)
endif()
target_compile_definitions(git2internal PRIVATE _FILE_OFFSET_BITS=64)
# Collect sourcefiles
file(GLOB SRC_H
"${PROJECT_SOURCE_DIR}/include/git2.h"
"${PROJECT_SOURCE_DIR}/include/git2/*.h"
"${PROJECT_SOURCE_DIR}/include/git2/sys/*.h")
list(SORT SRC_H)
target_sources(git2internal PRIVATE ${SRC_H})
# On Windows use specific platform sources
if(WIN32 AND NOT CYGWIN)
set(WIN_RC "win32/git2.rc")
file(GLOB SRC_OS win32/*.c win32/*.h)
list(SORT SRC_OS)
target_sources(git2internal PRIVATE ${SRC_OS})
elseif(AMIGA)
target_compile_definitions(git2internal PRIVATE NO_ADDRINFO NO_READDIR_R NO_MMAP)
else()
file(GLOB SRC_OS unix/*.c unix/*.h)
list(SORT SRC_OS)
target_sources(git2internal PRIVATE ${SRC_OS})
endif()
if(USE_LEAK_CHECKER STREQUAL "valgrind")
target_compile_definitions(git2internal PRIVATE VALGRIND)
endif()
file(GLOB SRC_GIT2 *.c *.h
allocators/*.c allocators/*.h
streams/*.c streams/*.h
transports/*.c transports/*.h
xdiff/*.c xdiff/*.h)
list(SORT SRC_GIT2)
target_sources(git2internal PRIVATE ${SRC_GIT2})
if(APPLE)
# The old Secure Transport API has been deprecated in macOS 10.15.
set_source_files_properties(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated)
endif()
# the xdiff dependency is not (yet) warning-free, disable warnings as
# errors for the xdiff sources until we've sorted them out
if(MSVC)
set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xmerge.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS -WX-)
else()
set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
endif()
# Determine architecture of the machine
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(GIT_ARCH_64 1)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(GIT_ARCH_32 1)
elseif(CMAKE_SIZEOF_VOID_P)
message(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
else()
message(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
endif()
configure_file(features.h.in git2/sys/features.h) configure_file(features.h.in git2/sys/features.h)
ide_split_sources(git2internal) #
list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:git2internal> ${LIBGIT2_DEPENDENCY_OBJECTS}) # Include child projects
#
target_include_directories(git2internal PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include) add_subdirectory(libgit2)
target_include_directories(git2internal SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES}) add_subdirectory(util)
if(BUILD_CLI)
add_subdirectory(cli)
endif()
# re-export these to the root so that peer projects (tests, fuzzers,
# examples) can use them
set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE) set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE) set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE) set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE) set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE) set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE) set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
if(XCODE_VERSION)
# This is required for Xcode to actually link the libgit2 library
# when using only object libraries.
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.c "")
list(APPEND LIBGIT2_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/dummy.c)
endif()
# Compile and link libgit2
add_library(git2 ${WIN_RC} ${LIBGIT2_OBJECTS})
target_link_libraries(git2 ${LIBGIT2_SYSTEM_LIBS})
set_target_properties(git2 PROPERTIES C_STANDARD 90)
set_target_properties(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(git2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(git2 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
# Win64+MSVC+static libs = linker error
if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
set_target_properties(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
endif()
ide_split_sources(git2)
if(SONAME)
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(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}")
endif()
endif()
pkg_build_config(NAME libgit2
VERSION ${libgit2_VERSION}
DESCRIPTION "The git library, take 2"
LIBS_SELF git2
PRIVATE_LIBS ${LIBGIT2_PC_LIBS}
REQUIRES ${LIBGIT2_PC_REQUIRES}
)
if(MSVC_IDE)
# Precompiled headers
set_target_properties(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
endif()
# Install
install(TARGETS git2
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/git2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${PROJECT_SOURCE_DIR}/include/git2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

12
src/README.md Normal file
View File

@ -0,0 +1,12 @@
# libgit2 sources
This is the source that makes up the core of libgit2 and its related
projects.
* `cli`
A git-compatible command-line interface that uses libgit2.
* `libgit2`
This is the libgit2 project, a cross-platform, linkable library
implementation of Git that you can use in your application.
* `util`
A shared utility library for these projects.

53
src/cli/CMakeLists.txt Normal file
View File

@ -0,0 +1,53 @@
set(CLI_INCLUDES
"${libgit2_BINARY_DIR}/src"
"${libgit2_SOURCE_DIR}/src/util"
"${libgit2_SOURCE_DIR}/src/cli"
"${libgit2_SOURCE_DIR}/include")
if(WIN32 AND NOT CYGWIN)
file(GLOB CLI_SRC_OS win32/*.c)
list(SORT CLI_SRC_OS)
else()
file(GLOB CLI_SRC_OS unix/*.c)
list(SORT CLI_SRC_OS)
endif()
file(GLOB CLI_SRC_C *.c *.h)
list(SORT CLI_SRC_C)
#
# The CLI currently needs to be statically linked against libgit2 because
# the utility library uses libgit2's thread-local error buffers. TODO:
# remove this dependency and allow us to dynamically link against libgit2.
#
if(BUILD_CLI STREQUAL "dynamic")
set(CLI_LIBGIT2_LIBRARY libgit2package)
else()
set(CLI_LIBGIT2_OBJECTS $<TARGET_OBJECTS:libgit2>)
endif()
#
# Compile and link the CLI
#
add_executable(git2_cli ${CLI_SRC_C} ${CLI_SRC_OS} ${CLI_OBJECTS}
$<TARGET_OBJECTS:util>
${CLI_LIBGIT2_OBJECTS}
${LIBGIT2_DEPENDENCY_OBJECTS})
target_link_libraries(git2_cli ${CLI_LIBGIT2_LIBRARY} ${LIBGIT2_SYSTEM_LIBS})
set_target_properties(git2_cli PROPERTIES C_STANDARD 90)
set_target_properties(git2_cli PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
ide_split_sources(git2_cli)
target_include_directories(git2_cli PRIVATE ${CLI_INCLUDES})
if(MSVC_IDE)
# Precompiled headers
set_target_properties(git2_cli PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
endif()
install(TARGETS git2_cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

26
src/cli/README.md Normal file
View File

@ -0,0 +1,26 @@
# cli
A git-compatible command-line interface that uses libgit2.
## Adding commands
1. Individual commands have a `main`-like top-level entrypoint. For example:
```c
int cmd_help(int argc, char **argv)
```
Although this is the same signature as `main`, commands are not built as
individual standalone executables, they'll be linked into the main cli.
(Though there may be an option for command executables to be built as
standalone executables in the future.)
2. Commands are prototyped in `cmd.h` and added to `main.c`'s list of
commands (`cli_cmds[]`). Commands should be specified with their name,
entrypoint and a brief description that can be printed in `git help`.
This is done because commands are linked into the main cli.
3. Commands should accept a `--help` option that displays their help
information. This will be shown when a user runs `<command> --help` and
when a user runs `help <command>`.

View File

@ -5,15 +5,16 @@
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#ifndef INCLUDE_hash_sha1_generic_h__ #ifndef CLI_cli_h__
#define INCLUDE_hash_sha1_generic_h__ #define CLI_cli_h__
#include "hash/sha1.h" #define PROGRAM_NAME "git2"
struct git_hash_sha1_ctx { #include "git2_util.h"
uint64_t size;
unsigned int H[5];
unsigned int W[16];
};
#endif #include "error.h"
#include "opt.h"
#include "opt_usage.h"
#include "sighandler.h"
#endif /* CLI_cli_h__ */

21
src/cli/cmd.c Normal file
View File

@ -0,0 +1,21 @@
/*
* 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 "cli.h"
#include "cmd.h"
const cli_cmd_spec *cli_cmd_spec_byname(const char *name)
{
const cli_cmd_spec *cmd;
for (cmd = cli_cmds; cmd->name; cmd++) {
if (!strcmp(cmd->name, name))
return cmd;
}
return NULL;
}

33
src/cli/cmd.h Normal file
View File

@ -0,0 +1,33 @@
/*
* 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 CLI_cmd_h__
#define CLI_cmd_h__
/* Command definitions */
typedef struct {
const char *name;
int (*fn)(int argc, char **argv);
const char *desc;
} cli_cmd_spec;
/* Options that are common to all commands (eg --help, --git-dir) */
extern const cli_opt_spec cli_common_opts[];
/* All the commands supported by the CLI */
extern const cli_cmd_spec cli_cmds[];
/* Find a command by name */
extern const cli_cmd_spec *cli_cmd_spec_byname(const char *name);
/* Commands */
extern int cmd_cat_file(int argc, char **argv);
extern int cmd_clone(int argc, char **argv);
extern int cmd_hash_object(int argc, char **argv);
extern int cmd_help(int argc, char **argv);
#endif /* CLI_cmd_h__ */

204
src/cli/cmd_cat_file.c Normal file
View File

@ -0,0 +1,204 @@
/*
* 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 <git2.h>
#include "cli.h"
#include "cmd.h"
#define COMMAND_NAME "cat-file"
typedef enum {
DISPLAY_CONTENT = 0,
DISPLAY_EXISTS,
DISPLAY_PRETTY,
DISPLAY_SIZE,
DISPLAY_TYPE
} display_t;
static int show_help;
static int display = DISPLAY_CONTENT;
static char *type_name, *object_spec;
static const cli_opt_spec opts[] = {
{ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
"display help about the " COMMAND_NAME " command" },
{ CLI_OPT_TYPE_SWITCH, NULL, 't', &display, DISPLAY_TYPE,
CLI_OPT_USAGE_REQUIRED, NULL, "display the type of the object" },
{ CLI_OPT_TYPE_SWITCH, NULL, 's', &display, DISPLAY_SIZE,
CLI_OPT_USAGE_CHOICE, NULL, "display the size of the object" },
{ CLI_OPT_TYPE_SWITCH, NULL, 'e', &display, DISPLAY_EXISTS,
CLI_OPT_USAGE_CHOICE, NULL, "displays nothing unless the object is corrupt" },
{ CLI_OPT_TYPE_SWITCH, NULL, 'p', &display, DISPLAY_PRETTY,
CLI_OPT_USAGE_CHOICE, NULL, "pretty-print the object" },
{ CLI_OPT_TYPE_ARG, "type", 0, &type_name, 0,
CLI_OPT_USAGE_CHOICE, "type", "the type of object to display" },
{ CLI_OPT_TYPE_ARG, "object", 0, &object_spec, 0,
CLI_OPT_USAGE_REQUIRED, "object", "the object to display" },
{ 0 },
};
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
printf("\n");
printf("Display the content for the given object in the repository.\n");
printf("\n");
printf("Options:\n");
cli_opt_help_fprint(stdout, opts);
}
static int print_odb(git_object *object, display_t display)
{
git_odb *odb = NULL;
git_odb_object *odb_object = NULL;
const unsigned char *content;
git_object_size_t size;
int ret = 0;
/*
* Our parsed blobs retain the raw content; all other objects are
* parsed into a working representation. To get the raw content,
* we need to do an ODB lookup. (Thankfully, this should be cached
* in-memory from our last call.)
*/
if (git_object_type(object) == GIT_OBJECT_BLOB) {
content = git_blob_rawcontent((git_blob *)object);
size = git_blob_rawsize((git_blob *)object);
} else {
if (git_repository_odb(&odb, git_object_owner(object)) < 0 ||
git_odb_read(&odb_object, odb, git_object_id(object)) < 0) {
ret = cli_error_git();
goto done;
}
content = git_odb_object_data(odb_object);
size = git_odb_object_size(odb_object);
}
switch (display) {
case DISPLAY_SIZE:
if (printf("%" PRIu64 "\n", size) < 0)
ret = cli_error_os();
break;
case DISPLAY_CONTENT:
if (p_write(fileno(stdout), content, (size_t)size) < 0)
ret = cli_error_os();
break;
default:
GIT_ASSERT(0);
}
done:
git_odb_object_free(odb_object);
git_odb_free(odb);
return ret;
}
static int print_type(git_object *object)
{
if (printf("%s\n", git_object_type2string(git_object_type(object))) < 0)
return cli_error_os();
return 0;
}
static int print_pretty(git_object *object)
{
const git_tree_entry *entry;
size_t i, count;
/*
* Only trees are stored in an unreadable format and benefit from
* pretty-printing.
*/
if (git_object_type(object) != GIT_OBJECT_TREE)
return print_odb(object, DISPLAY_CONTENT);
for (i = 0, count = git_tree_entrycount((git_tree *)object); i < count; i++) {
entry = git_tree_entry_byindex((git_tree *)object, i);
if (printf("%06o %s %s\t%s\n",
git_tree_entry_filemode_raw(entry),
git_object_type2string(git_tree_entry_type(entry)),
git_oid_tostr_s(git_tree_entry_id(entry)),
git_tree_entry_name(entry)) < 0)
return cli_error_os();
}
return 0;
}
int cmd_cat_file(int argc, char **argv)
{
git_repository *repo = NULL;
git_object *object = NULL;
git_object_t type;
cli_opt invalid_opt;
int giterr, ret = 0;
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
print_help();
return 0;
}
if (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0)
return cli_error_git();
if ((giterr = git_revparse_single(&object, repo, object_spec)) < 0) {
if (display == DISPLAY_EXISTS && giterr == GIT_ENOTFOUND)
ret = 1;
else
ret = cli_error_git();
goto done;
}
if (type_name) {
git_object *peeled;
if ((type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID) {
ret = cli_error_usage("invalid object type '%s'", type_name);
goto done;
}
if (git_object_peel(&peeled, object, type) < 0) {
ret = cli_error_git();
goto done;
}
git_object_free(object);
object = peeled;
}
switch (display) {
case DISPLAY_EXISTS:
ret = 0;
break;
case DISPLAY_TYPE:
ret = print_type(object);
break;
case DISPLAY_PRETTY:
ret = print_pretty(object);
break;
default:
ret = print_odb(object, display);
break;
}
done:
git_object_free(object);
git_repository_free(repo);
return ret;
}

176
src/cli/cmd_clone.c Normal file
View File

@ -0,0 +1,176 @@
/*
* 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 "cli.h"
#include "cmd.h"
#include "error.h"
#include "sighandler.h"
#include "progress.h"
#include "fs_path.h"
#include "futils.h"
#define COMMAND_NAME "clone"
static char *branch, *remote_path, *local_path;
static int show_help, quiet, checkout = 1, bare;
static bool local_path_exists;
static cli_progress progress = CLI_PROGRESS_INIT;
static const cli_opt_spec opts[] = {
{ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
"display help about the " COMMAND_NAME " command" },
{ CLI_OPT_TYPE_SWITCH, "quiet", 'q', &quiet, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display the type of the object" },
{ CLI_OPT_TYPE_SWITCH, "no-checkout", 'n', &checkout, 0,
CLI_OPT_USAGE_DEFAULT, NULL, "don't checkout HEAD" },
{ CLI_OPT_TYPE_SWITCH, "bare", 0, &bare, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "don't create a working directory" },
{ CLI_OPT_TYPE_VALUE, "branch", 'b', &branch, 0,
CLI_OPT_USAGE_DEFAULT, "name", "branch to check out" },
{ CLI_OPT_TYPE_LITERAL },
{ CLI_OPT_TYPE_ARG, "repository", 0, &remote_path, 0,
CLI_OPT_USAGE_REQUIRED, "repository", "repository path" },
{ CLI_OPT_TYPE_ARG, "directory", 0, &local_path, 0,
CLI_OPT_USAGE_DEFAULT, "directory", "directory to clone into" },
{ 0 }
};
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
printf("\n");
printf("Clone a repository into a new directory.\n");
printf("\n");
printf("Options:\n");
cli_opt_help_fprint(stdout, opts);
}
static char *compute_local_path(const char *orig_path)
{
const char *slash;
char *local_path;
if ((slash = strrchr(orig_path, '/')) == NULL &&
(slash = strrchr(orig_path, '\\')) == NULL)
local_path = git__strdup(orig_path);
else
local_path = git__strdup(slash + 1);
return local_path;
}
static bool validate_local_path(const char *path)
{
if (!git_fs_path_exists(path))
return false;
if (!git_fs_path_isdir(path) || !git_fs_path_is_empty_dir(path)) {
fprintf(stderr, "fatal: destination path '%s' already exists and is not an empty directory.\n",
path);
exit(128);
}
return true;
}
static void cleanup(void)
{
int rmdir_flags = GIT_RMDIR_REMOVE_FILES;
cli_progress_abort(&progress);
if (local_path_exists)
rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
if (!git_fs_path_isdir(local_path))
return;
git_futils_rmdir_r(local_path, NULL, rmdir_flags);
}
static void interrupt_cleanup(void)
{
cleanup();
exit(130);
}
int cmd_clone(int argc, char **argv)
{
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
git_repository *repo = NULL;
cli_opt invalid_opt;
char *computed_path = NULL;
int ret = 0;
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
print_help();
return 0;
}
if (!remote_path) {
ret = cli_error_usage("you must specify a repository to clone");
goto done;
}
if (bare)
clone_opts.bare = 1;
if (branch)
clone_opts.checkout_branch = branch;
if (!checkout)
clone_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
if (!local_path)
local_path = computed_path = compute_local_path(remote_path);
local_path_exists = validate_local_path(local_path);
cli_sighandler_set_interrupt(interrupt_cleanup);
if (!local_path_exists &&
git_futils_mkdir(local_path, 0777, 0) < 0) {
ret = cli_error_git();
goto done;
}
if (!quiet) {
clone_opts.fetch_opts.callbacks.sideband_progress = cli_progress_fetch_sideband;
clone_opts.fetch_opts.callbacks.transfer_progress = cli_progress_fetch_transfer;
clone_opts.fetch_opts.callbacks.payload = &progress;
clone_opts.checkout_opts.progress_cb = cli_progress_checkout;
clone_opts.checkout_opts.progress_payload = &progress;
printf("Cloning into '%s'...\n", local_path);
}
if (git_clone(&repo, remote_path, local_path, &clone_opts) < 0) {
cleanup();
ret = cli_error_git();
goto done;
}
cli_progress_finish(&progress);
done:
cli_progress_dispose(&progress);
git__free(computed_path);
git_repository_free(repo);
return ret;
}

135
src/cli/cmd_hash_object.c Normal file
View File

@ -0,0 +1,135 @@
/*
* 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 <git2.h>
#include "cli.h"
#include "cmd.h"
#include "futils.h"
#define COMMAND_NAME "hash-object"
static int show_help;
static char *type_name;
static int write_object, read_stdin, literally;
static char **filenames;
static const cli_opt_spec opts[] = {
{ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
"display help about the " COMMAND_NAME " command" },
{ CLI_OPT_TYPE_VALUE, NULL, 't', &type_name, 0,
CLI_OPT_USAGE_DEFAULT, "type", "the type of object to hash (default: \"blob\")" },
{ CLI_OPT_TYPE_SWITCH, NULL, 'w', &write_object, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "write the object to the object database" },
{ CLI_OPT_TYPE_SWITCH, "literally", 0, &literally, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "do not validate the object contents" },
{ CLI_OPT_TYPE_SWITCH, "stdin", 0, &read_stdin, 1,
CLI_OPT_USAGE_REQUIRED, NULL, "read content from stdin" },
{ CLI_OPT_TYPE_ARGS, "file", 0, &filenames, 0,
CLI_OPT_USAGE_CHOICE, "file", "the file (or files) to read and hash" },
{ 0 },
};
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
printf("\n");
printf("Compute the object ID for a given file and optionally write that file\nto the object database.\n");
printf("\n");
printf("Options:\n");
cli_opt_help_fprint(stdout, opts);
}
static int hash_buf(git_odb *odb, git_str *buf, git_object_t type)
{
git_oid oid;
if (!literally) {
int valid = 0;
if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, type) < 0 || !valid)
return cli_error_git();
}
if (write_object) {
if (git_odb_write(&oid, odb, buf->ptr, buf->size, type) < 0)
return cli_error_git();
} else {
if (git_odb_hash(&oid, buf->ptr, buf->size, type) < 0)
return cli_error_git();
}
if (printf("%s\n", git_oid_tostr_s(&oid)) < 0)
return cli_error_os();
return 0;
}
int cmd_hash_object(int argc, char **argv)
{
git_repository *repo = NULL;
git_odb *odb = NULL;
git_str buf = GIT_STR_INIT;
cli_opt invalid_opt;
git_object_t type = GIT_OBJECT_BLOB;
char **filename;
int ret = 0;
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
print_help();
return 0;
}
if (type_name && (type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID)
return cli_error_usage("invalid object type '%s'", type_name);
if (write_object &&
(git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0 ||
git_repository_odb(&odb, repo) < 0)) {
ret = cli_error_git();
goto done;
}
/*
* TODO: we're reading blobs, we shouldn't pull them all into main
* memory, we should just stream them into the odb instead.
* (Or create a `git_odb_writefile` API.)
*/
if (read_stdin) {
if (git_futils_readbuffer_fd_full(&buf, fileno(stdin)) < 0) {
ret = cli_error_git();
goto done;
}
if ((ret = hash_buf(odb, &buf, type)) != 0)
goto done;
} else {
for (filename = filenames; *filename; filename++) {
if (git_futils_readbuffer(&buf, *filename) < 0) {
ret = cli_error_git();
goto done;
}
if ((ret = hash_buf(odb, &buf, type)) != 0)
goto done;
}
}
done:
git_str_dispose(&buf);
git_odb_free(odb);
git_repository_free(repo);
return ret;
}

86
src/cli/cmd_help.c Normal file
View File

@ -0,0 +1,86 @@
/*
* 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 "cli.h"
#include "cmd.h"
#define COMMAND_NAME "help"
static char *command;
static int show_help;
static const cli_opt_spec opts[] = {
{ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
CLI_OPT_USAGE_HIDDEN, NULL, "display help about the help command" },
{ CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
CLI_OPT_USAGE_DEFAULT, "command", "the command to show help for" },
{ 0 },
};
static int print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
printf("\n");
printf("Display help information about %s. If a command is specified, help\n", PROGRAM_NAME);
printf("about that command will be shown. Otherwise, general information about\n");
printf("%s will be shown, including the commands available.\n", PROGRAM_NAME);
return 0;
}
static int print_commands(void)
{
const cli_cmd_spec *cmd;
cli_opt_usage_fprint(stdout, PROGRAM_NAME, NULL, cli_common_opts);
printf("\n");
printf("These are the %s commands available:\n\n", PROGRAM_NAME);
for (cmd = cli_cmds; cmd->name; cmd++)
printf(" %-11s %s\n", cmd->name, cmd->desc);
printf("\nSee '%s help <command>' for more information on a specific command.\n", PROGRAM_NAME);
return 0;
}
int cmd_help(int argc, char **argv)
{
char *fake_args[2];
const cli_cmd_spec *cmd;
cli_opt invalid_opt;
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
/* Show the meta-help */
if (show_help)
return print_help();
/* We were not asked to show help for a specific command. */
if (!command)
return print_commands();
/*
* If we were asked for help for a command (eg, `help <command>`),
* delegate back to that command's `--help` option. This lets
* commands own their help. Emulate the command-line arguments
* that would invoke `<command> --help` and invoke that command.
*/
fake_args[0] = command;
fake_args[1] = "--help";
if ((cmd = cli_cmd_spec_byname(command)) == NULL)
return cli_error("'%s' is not a %s command. See '%s help'.",
command, PROGRAM_NAME, PROGRAM_NAME);
return cmd->fn(2, fake_args);
}

51
src/cli/error.h Normal file
View File

@ -0,0 +1,51 @@
/*
* 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 CLI_error_h__
#define CLI_error_h__
#include "cli.h"
#include <stdio.h>
#define CLI_EXIT_OK 0
#define CLI_EXIT_ERROR 1
#define CLI_EXIT_OS 128
#define CLI_EXIT_GIT 128
#define CLI_EXIT_USAGE 129
#define cli_error__print(fmt) do { \
va_list ap; \
va_start(ap, fmt); \
fprintf(stderr, "%s: ", PROGRAM_NAME); \
vfprintf(stderr, fmt, ap); \
fprintf(stderr, "\n"); \
va_end(ap); \
} while(0)
GIT_INLINE(int) cli_error(const char *fmt, ...)
{
cli_error__print(fmt);
return CLI_EXIT_ERROR;
}
GIT_INLINE(int) cli_error_usage(const char *fmt, ...)
{
cli_error__print(fmt);
return CLI_EXIT_USAGE;
}
GIT_INLINE(int) cli_error_git(void)
{
const git_error *err = git_error_last();
fprintf(stderr, "%s: %s\n", PROGRAM_NAME,
err ? err->message : "unknown error");
return CLI_EXIT_GIT;
}
#define cli_error_os() (perror(PROGRAM_NAME), CLI_EXIT_OS)
#endif /* CLI_error_h__ */

106
src/cli/main.c Normal file
View File

@ -0,0 +1,106 @@
/*
* 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 "cli.h"
#include "cmd.h"
static int show_help = 0;
static int show_version = 0;
static char *command = NULL;
static char **args = NULL;
const cli_opt_spec cli_common_opts[] = {
{ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display help information" },
{ CLI_OPT_TYPE_SWITCH, "version", 0, &show_version, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display the version" },
{ CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
CLI_OPT_USAGE_REQUIRED, "command", "the command to run" },
{ CLI_OPT_TYPE_ARGS, "args", 0, &args, 0,
CLI_OPT_USAGE_DEFAULT, "args", "arguments for the command" },
{ 0 }
};
const cli_cmd_spec cli_cmds[] = {
{ "cat-file", cmd_cat_file, "Display an object in the repository" },
{ "clone", cmd_clone, "Clone a repository into a new directory" },
{ "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" },
{ "help", cmd_help, "Display help information" },
{ NULL }
};
int main(int argc, char **argv)
{
const cli_cmd_spec *cmd;
cli_opt_parser optparser;
cli_opt opt;
char *help_args[3] = { NULL };
int help_args_len;
int args_len = 0;
int ret = 0;
if (git_libgit2_init() < 0) {
cli_error("failed to initialize libgit2");
exit(CLI_EXIT_GIT);
}
cli_opt_parser_init(&optparser, cli_common_opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU);
/* Parse the top-level (common) options and command information */
while (cli_opt_parser_next(&opt, &optparser)) {
if (!opt.spec) {
cli_opt_status_fprint(stderr, PROGRAM_NAME, &opt);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, cli_common_opts);
ret = CLI_EXIT_USAGE;
goto done;
}
/*
* When we see a command, stop parsing and capture the
* remaining arguments as args for the command itself.
*/
if (command) {
args = &argv[optparser.idx];
args_len = (int)(argc - optparser.idx);
break;
}
}
if (show_version) {
printf("%s version %s\n", PROGRAM_NAME, LIBGIT2_VERSION);
goto done;
}
/*
* If `--help <command>` is specified, delegate to that command's
* `--help` option. If no command is specified, run the `help`
* command. Do this by updating the args to emulate that behavior.
*/
if (!command || show_help) {
help_args[0] = command ? (char *)command : "help";
help_args[1] = command ? "--help" : NULL;
help_args_len = command ? 2 : 1;
command = help_args[0];
args = help_args;
args_len = help_args_len;
}
if ((cmd = cli_cmd_spec_byname(command)) == NULL) {
ret = cli_error("'%s' is not a %s command. See '%s help'.",
command, PROGRAM_NAME, PROGRAM_NAME);
goto done;
}
ret = cmd->fn(args_len, args);
done:
git_libgit2_shutdown();
return ret;
}

669
src/cli/opt.c Normal file
View File

@ -0,0 +1,669 @@
/*
* Copyright (c), Edward Thomson <ethomson@edwardthomson.com>
* All rights reserved.
*
* This file is part of adopt, distributed under the MIT license.
* For full terms and conditions, see the included LICENSE file.
*
* THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT.
*
* This file was produced by using the `rename.pl` script included with
* adopt. The command-line specified was:
*
* ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <assert.h>
#include "cli.h"
#include "opt.h"
#ifdef _WIN32
# include <Windows.h>
#else
# include <fcntl.h>
# include <sys/ioctl.h>
#endif
#ifdef _MSC_VER
# define alloca _alloca
#endif
#define spec_is_option_type(x) \
((x)->type == CLI_OPT_TYPE_BOOL || \
(x)->type == CLI_OPT_TYPE_SWITCH || \
(x)->type == CLI_OPT_TYPE_VALUE)
GIT_INLINE(const cli_opt_spec *) spec_for_long(
int *is_negated,
int *has_value,
const char **value,
const cli_opt_parser *parser,
const char *arg)
{
const cli_opt_spec *spec;
char *eql;
size_t eql_pos;
eql = strchr(arg, '=');
eql_pos = (eql = strchr(arg, '=')) ? (size_t)(eql - arg) : strlen(arg);
for (spec = parser->specs; spec->type; ++spec) {
/* Handle -- (everything after this is literal) */
if (spec->type == CLI_OPT_TYPE_LITERAL && arg[0] == '\0')
return spec;
/* Handle --no-option arguments for bool types */
if (spec->type == CLI_OPT_TYPE_BOOL &&
strncmp(arg, "no-", 3) == 0 &&
strcmp(arg + 3, spec->name) == 0) {
*is_negated = 1;
return spec;
}
/* Handle the typical --option arguments */
if (spec_is_option_type(spec) &&
spec->name &&
strcmp(arg, spec->name) == 0)
return spec;
/* Handle --option=value arguments */
if (spec->type == CLI_OPT_TYPE_VALUE &&
eql &&
strncmp(arg, spec->name, eql_pos) == 0 &&
spec->name[eql_pos] == '\0') {
*has_value = 1;
*value = arg[eql_pos + 1] ? &arg[eql_pos + 1] : NULL;
return spec;
}
}
return NULL;
}
GIT_INLINE(const cli_opt_spec *) spec_for_short(
const char **value,
const cli_opt_parser *parser,
const char *arg)
{
const cli_opt_spec *spec;
for (spec = parser->specs; spec->type; ++spec) {
/* Handle -svalue short options with a value */
if (spec->type == CLI_OPT_TYPE_VALUE &&
arg[0] == spec->alias &&
arg[1] != '\0') {
*value = &arg[1];
return spec;
}
/* Handle typical -s short options */
if (arg[0] == spec->alias) {
*value = NULL;
return spec;
}
}
return NULL;
}
GIT_INLINE(const cli_opt_spec *) spec_for_arg(cli_opt_parser *parser)
{
const cli_opt_spec *spec;
size_t args = 0;
for (spec = parser->specs; spec->type; ++spec) {
if (spec->type == CLI_OPT_TYPE_ARG) {
if (args == parser->arg_idx) {
parser->arg_idx++;
return spec;
}
args++;
}
if (spec->type == CLI_OPT_TYPE_ARGS && args == parser->arg_idx)
return spec;
}
return NULL;
}
GIT_INLINE(int) spec_is_choice(const cli_opt_spec *spec)
{
return ((spec + 1)->type &&
((spec + 1)->usage & CLI_OPT_USAGE_CHOICE));
}
/*
* If we have a choice with switches and bare arguments, and we see
* the switch, then we no longer expect the bare argument.
*/
GIT_INLINE(void) consume_choices(const cli_opt_spec *spec, cli_opt_parser *parser)
{
/* back up to the beginning of the choices */
while (spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE))
--spec;
if (!spec_is_choice(spec))
return;
do {
if (spec->type == CLI_OPT_TYPE_ARG)
parser->arg_idx++;
++spec;
} while(spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE));
}
static cli_opt_status_t parse_long(cli_opt *opt, cli_opt_parser *parser)
{
const cli_opt_spec *spec;
char *arg = parser->args[parser->idx++];
const char *value = NULL;
int is_negated = 0, has_value = 0;
opt->arg = arg;
if ((spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2])) == NULL) {
opt->spec = NULL;
opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
goto done;
}
opt->spec = spec;
/* Future options parsed as literal */
if (spec->type == CLI_OPT_TYPE_LITERAL)
parser->in_literal = 1;
/* --bool or --no-bool */
else if (spec->type == CLI_OPT_TYPE_BOOL && spec->value)
*((int *)spec->value) = !is_negated;
/* --accumulate */
else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value)
*((int *)spec->value) += spec->switch_value ? spec->switch_value : 1;
/* --switch */
else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value)
*((int *)spec->value) = spec->switch_value;
/* Parse values as "--foo=bar" or "--foo bar" */
else if (spec->type == CLI_OPT_TYPE_VALUE) {
if (has_value)
opt->value = (char *)value;
else if ((parser->idx + 1) <= parser->args_len)
opt->value = parser->args[parser->idx++];
if (spec->value)
*((char **)spec->value) = opt->value;
}
/* Required argument was not provided */
if (spec->type == CLI_OPT_TYPE_VALUE &&
!opt->value &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
opt->status = CLI_OPT_STATUS_MISSING_VALUE;
else
opt->status = CLI_OPT_STATUS_OK;
consume_choices(opt->spec, parser);
done:
return opt->status;
}
static cli_opt_status_t parse_short(cli_opt *opt, cli_opt_parser *parser)
{
const cli_opt_spec *spec;
char *arg = parser->args[parser->idx++];
const char *value;
opt->arg = arg;
if ((spec = spec_for_short(&value, parser, &arg[1 + parser->in_short])) == NULL) {
opt->spec = NULL;
opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
goto done;
}
opt->spec = spec;
if (spec->type == CLI_OPT_TYPE_BOOL && spec->value)
*((int *)spec->value) = 1;
else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value)
*((int *)spec->value) += spec->switch_value ? spec->switch_value : 1;
else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value)
*((int *)spec->value) = spec->switch_value;
/* Parse values as "-ifoo" or "-i foo" */
else if (spec->type == CLI_OPT_TYPE_VALUE) {
if (value)
opt->value = (char *)value;
else if ((parser->idx + 1) <= parser->args_len)
opt->value = parser->args[parser->idx++];
if (spec->value)
*((char **)spec->value) = opt->value;
}
/*
* Handle compressed short arguments, like "-fbcd"; see if there's
* another character after the one we processed. If not, advance
* the parser index.
*/
if (spec->type != CLI_OPT_TYPE_VALUE && arg[2 + parser->in_short] != '\0') {
parser->in_short++;
parser->idx--;
} else {
parser->in_short = 0;
}
/* Required argument was not provided */
if (spec->type == CLI_OPT_TYPE_VALUE && !opt->value)
opt->status = CLI_OPT_STATUS_MISSING_VALUE;
else
opt->status = CLI_OPT_STATUS_OK;
consume_choices(opt->spec, parser);
done:
return opt->status;
}
static cli_opt_status_t parse_arg(cli_opt *opt, cli_opt_parser *parser)
{
const cli_opt_spec *spec = spec_for_arg(parser);
opt->spec = spec;
opt->arg = parser->args[parser->idx];
if (!spec) {
parser->idx++;
opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
} else if (spec->type == CLI_OPT_TYPE_ARGS) {
if (spec->value)
*((char ***)spec->value) = &parser->args[parser->idx];
/*
* We have started a list of arguments; the remainder of
* given arguments need not be examined.
*/
parser->in_args = (parser->args_len - parser->idx);
parser->idx = parser->args_len;
opt->args_len = parser->in_args;
opt->status = CLI_OPT_STATUS_OK;
} else {
if (spec->value)
*((char **)spec->value) = parser->args[parser->idx];
parser->idx++;
opt->status = CLI_OPT_STATUS_OK;
}
return opt->status;
}
static int support_gnu_style(unsigned int flags)
{
if ((flags & CLI_OPT_PARSE_FORCE_GNU) != 0)
return 1;
if ((flags & CLI_OPT_PARSE_GNU) == 0)
return 0;
/* TODO: Windows */
#if defined(_WIN32) && defined(UNICODE)
if (_wgetenv(L"POSIXLY_CORRECT") != NULL)
return 0;
#else
if (getenv("POSIXLY_CORRECT") != NULL)
return 0;
#endif
return 1;
}
void cli_opt_parser_init(
cli_opt_parser *parser,
const cli_opt_spec specs[],
char **args,
size_t args_len,
unsigned int flags)
{
assert(parser);
memset(parser, 0x0, sizeof(cli_opt_parser));
parser->specs = specs;
parser->args = args;
parser->args_len = args_len;
parser->flags = flags;
parser->needs_sort = support_gnu_style(flags);
}
GIT_INLINE(const cli_opt_spec *) spec_for_sort(
int *needs_value,
const cli_opt_parser *parser,
const char *arg)
{
int is_negated, has_value = 0;
const char *value;
const cli_opt_spec *spec = NULL;
size_t idx = 0;
*needs_value = 0;
if (strncmp(arg, "--", 2) == 0) {
spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2]);
*needs_value = !has_value;
}
else if (strncmp(arg, "-", 1) == 0) {
spec = spec_for_short(&value, parser, &arg[1]);
/*
* Advance through compressed short arguments to see if
* the last one has a value, eg "-xvffilename".
*/
while (spec && !value && arg[1 + ++idx] != '\0')
spec = spec_for_short(&value, parser, &arg[1 + idx]);
*needs_value = (value == NULL);
}
return spec;
}
/*
* Some parsers allow for handling arguments like "file1 --help file2";
* this is done by re-sorting the arguments in-place; emulate that.
*/
static int sort_gnu_style(cli_opt_parser *parser)
{
size_t i, j, insert_idx = parser->idx, offset;
const cli_opt_spec *spec;
char *option, *value;
int needs_value, changed = 0;
parser->needs_sort = 0;
for (i = parser->idx; i < parser->args_len; i++) {
spec = spec_for_sort(&needs_value, parser, parser->args[i]);
/* Not a "-" or "--" prefixed option. No change. */
if (!spec)
continue;
/* A "--" alone means remaining args are literal. */
if (spec->type == CLI_OPT_TYPE_LITERAL)
break;
option = parser->args[i];
/*
* If the argument is a value type and doesn't already
* have a value (eg "--foo=bar" or "-fbar") then we need
* to copy the next argument as its value.
*/
if (spec->type == CLI_OPT_TYPE_VALUE && needs_value) {
/*
* A required value is not provided; set parser
* index to this value so that we fail on it.
*/
if (i + 1 >= parser->args_len) {
parser->idx = i;
return 1;
}
value = parser->args[i + 1];
offset = 1;
} else {
value = NULL;
offset = 0;
}
/* Caller error if args[0] is an option. */
if (i == 0)
return 0;
/* Shift args up one (or two) and insert the option */
for (j = i; j > insert_idx; j--)
parser->args[j + offset] = parser->args[j - 1];
parser->args[insert_idx] = option;
if (value)
parser->args[insert_idx + 1] = value;
insert_idx += (1 + offset);
i += offset;
changed = 1;
}
return changed;
}
cli_opt_status_t cli_opt_parser_next(cli_opt *opt, cli_opt_parser *parser)
{
assert(opt && parser);
memset(opt, 0x0, sizeof(cli_opt));
if (parser->idx >= parser->args_len) {
opt->args_len = parser->in_args;
return CLI_OPT_STATUS_DONE;
}
/* Handle options in long form, those beginning with "--" */
if (strncmp(parser->args[parser->idx], "--", 2) == 0 &&
!parser->in_short &&
!parser->in_literal)
return parse_long(opt, parser);
/* Handle options in short form, those beginning with "-" */
else if (parser->in_short ||
(strncmp(parser->args[parser->idx], "-", 1) == 0 &&
!parser->in_literal))
return parse_short(opt, parser);
/*
* We've reached the first "bare" argument. In POSIX mode, all
* remaining items on the command line are arguments. In GNU
* mode, there may be long or short options after this. Sort any
* options up to this position then re-parse the current position.
*/
if (parser->needs_sort && sort_gnu_style(parser))
return cli_opt_parser_next(opt, parser);
return parse_arg(opt, parser);
}
GIT_INLINE(int) spec_included(const cli_opt_spec **specs, const cli_opt_spec *spec)
{
const cli_opt_spec **i;
for (i = specs; *i; ++i) {
if (spec == *i)
return 1;
}
return 0;
}
static cli_opt_status_t validate_required(
cli_opt *opt,
const cli_opt_spec specs[],
const cli_opt_spec **given_specs)
{
const cli_opt_spec *spec, *required;
int given;
/*
* Iterate over the possible specs to identify requirements and
* ensure that those have been given on the command-line.
* Note that we can have required *choices*, where one in a
* list of choices must be specified.
*/
for (spec = specs, required = NULL, given = 0; spec->type; ++spec) {
if (!required && (spec->usage & CLI_OPT_USAGE_REQUIRED)) {
required = spec;
given = 0;
} else if (!required) {
continue;
}
if (!given)
given = spec_included(given_specs, spec);
/*
* Validate the requirement unless we're in a required
* choice. In that case, keep the required state and
* validate at the end of the choice list.
*/
if (!spec_is_choice(spec)) {
if (!given) {
opt->spec = required;
opt->status = CLI_OPT_STATUS_MISSING_ARGUMENT;
break;
}
required = NULL;
given = 0;
}
}
return opt->status;
}
cli_opt_status_t cli_opt_parse(
cli_opt *opt,
const cli_opt_spec specs[],
char **args,
size_t args_len,
unsigned int flags)
{
cli_opt_parser parser;
const cli_opt_spec **given_specs;
size_t given_idx = 0;
cli_opt_parser_init(&parser, specs, args, args_len, flags);
given_specs = alloca(sizeof(const cli_opt_spec *) * (args_len + 1));
while (cli_opt_parser_next(opt, &parser)) {
if (opt->status != CLI_OPT_STATUS_OK &&
opt->status != CLI_OPT_STATUS_DONE)
return opt->status;
if ((opt->spec->usage & CLI_OPT_USAGE_STOP_PARSING))
return (opt->status = CLI_OPT_STATUS_DONE);
given_specs[given_idx++] = opt->spec;
}
given_specs[given_idx] = NULL;
return validate_required(opt, specs, given_specs);
}
static int spec_name_fprint(FILE *file, const cli_opt_spec *spec)
{
int error;
if (spec->type == CLI_OPT_TYPE_ARG)
error = fprintf(file, "%s", spec->value_name);
else if (spec->type == CLI_OPT_TYPE_ARGS)
error = fprintf(file, "%s", spec->value_name);
else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
error = fprintf(file, "-%c", spec->alias);
else
error = fprintf(file, "--%s", spec->name);
return error;
}
int cli_opt_status_fprint(
FILE *file,
const char *command,
const cli_opt *opt)
{
const cli_opt_spec *choice;
int error;
if (command && (error = fprintf(file, "%s: ", command)) < 0)
return error;
switch (opt->status) {
case CLI_OPT_STATUS_DONE:
error = fprintf(file, "finished processing arguments (no error)\n");
break;
case CLI_OPT_STATUS_OK:
error = fprintf(file, "no error\n");
break;
case CLI_OPT_STATUS_UNKNOWN_OPTION:
error = fprintf(file, "unknown option: %s\n", opt->arg);
break;
case CLI_OPT_STATUS_MISSING_VALUE:
if ((error = fprintf(file, "argument '")) < 0 ||
(error = spec_name_fprint(file, opt->spec)) < 0 ||
(error = fprintf(file, "' requires a value.\n")) < 0)
break;
break;
case CLI_OPT_STATUS_MISSING_ARGUMENT:
if (spec_is_choice(opt->spec)) {
int is_choice = 1;
if (spec_is_choice((opt->spec)+1))
error = fprintf(file, "one of");
else
error = fprintf(file, "either");
if (error < 0)
break;
for (choice = opt->spec; is_choice; ++choice) {
is_choice = spec_is_choice(choice);
if (!is_choice)
error = fprintf(file, " or");
else if (choice != opt->spec)
error = fprintf(file, ",");
if ((error < 0) ||
(error = fprintf(file, " '")) < 0 ||
(error = spec_name_fprint(file, choice)) < 0 ||
(error = fprintf(file, "'")) < 0)
break;
if (!spec_is_choice(choice))
break;
}
if ((error < 0) ||
(error = fprintf(file, " is required.\n")) < 0)
break;
} else {
if ((error = fprintf(file, "argument '")) < 0 ||
(error = spec_name_fprint(file, opt->spec)) < 0 ||
(error = fprintf(file, "' is required.\n")) < 0)
break;
}
break;
default:
error = fprintf(file, "unknown status: %d\n", opt->status);
break;
}
return error;
}

349
src/cli/opt.h Normal file
View File

@ -0,0 +1,349 @@
/*
* Copyright (c), Edward Thomson <ethomson@edwardthomson.com>
* All rights reserved.
*
* This file is part of adopt, distributed under the MIT license.
* For full terms and conditions, see the included LICENSE file.
*
* THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT.
*
* This file was produced by using the `rename.pl` script included with
* adopt. The command-line specified was:
*
* ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
*/
#ifndef CLI_opt_h__
#define CLI_opt_h__
#include <stdio.h>
#include <stdint.h>
/**
* The type of argument to be parsed.
*/
typedef enum {
CLI_OPT_TYPE_NONE = 0,
/**
* An option that, when specified, sets a given value to true.
* This is useful for options like "--debug". A negation
* option (beginning with "no-") is implicitly specified; for
* example "--no-debug". The `value` pointer in the returned
* option will be set to `1` when this is specified, and set to
* `0` when the negation "no-" option is specified.
*/
CLI_OPT_TYPE_BOOL,
/**
* An option that, when specified, sets the given `value` pointer
* to the specified `switch_value`. This is useful for booleans
* where you do not want the implicit negation that comes with an
* `CLI_OPT_TYPE_BOOL`, or for switches that multiplex a value, like
* setting a mode. For example, `--read` may set the `value` to
* `MODE_READ` and `--write` may set the `value` to `MODE_WRITE`.
*/
CLI_OPT_TYPE_SWITCH,
/**
* An option that, when specified, increments the given
* `value` by the given `switch_value`. This can be specified
* multiple times to continue to increment the `value`.
* (For example, "-vvv" to set verbosity to 3.)
*/
CLI_OPT_TYPE_ACCUMULATOR,
/**
* An option that takes a value, for example `-n value`,
* `-nvalue`, `--name value` or `--name=value`.
*/
CLI_OPT_TYPE_VALUE,
/**
* A bare "--" that indicates that arguments following this are
* literal. This allows callers to specify things that might
* otherwise look like options, for example to operate on a file
* named "-rf" then you can invoke "program -- -rf" to treat
* "-rf" as an argument not an option.
*/
CLI_OPT_TYPE_LITERAL,
/**
* A single argument, not an option. When options are exhausted,
* arguments will be matches in the order that they're specified
* in the spec list. For example, if two `CLI_OPT_TYPE_ARGS` are
* specified, `input_file` and `output_file`, then the first bare
* argument on the command line will be `input_file` and the
* second will be `output_file`.
*/
CLI_OPT_TYPE_ARG,
/**
* A collection of arguments. This is useful when you want to take
* a list of arguments, for example, multiple paths. When specified,
* the value will be set to the first argument in the list.
*/
CLI_OPT_TYPE_ARGS,
} cli_opt_type_t;
/**
* Additional information about an option, including parsing
* restrictions and usage information to be displayed to the end-user.
*/
typedef enum {
/** Defaults for the argument. */
CLI_OPT_USAGE_DEFAULT = 0,
/** This argument is required. */
CLI_OPT_USAGE_REQUIRED = (1u << 0),
/**
* This is a multiple choice argument, combined with the previous
* argument. For example, when the previous argument is `-f` and
* this optional is applied to an argument of type `-b` then one
* of `-f` or `-b` may be specified.
*/
CLI_OPT_USAGE_CHOICE = (1u << 1),
/**
* This argument short-circuits the remainder of parsing.
* Useful for arguments like `--help`.
*/
CLI_OPT_USAGE_STOP_PARSING = (1u << 2),
/** The argument's value is optional ("-n" or "-n foo") */
CLI_OPT_USAGE_VALUE_OPTIONAL = (1u << 3),
/** This argument should not be displayed in usage. */
CLI_OPT_USAGE_HIDDEN = (1u << 4),
/** In usage, show the long format instead of the abbreviated format. */
CLI_OPT_USAGE_SHOW_LONG = (1u << 5),
} cli_opt_usage_t;
typedef enum {
/** Default parsing behavior. */
CLI_OPT_PARSE_DEFAULT = 0,
/**
* Parse with GNU `getopt_long` style behavior, where options can
* be intermixed with arguments at any position (for example,
* "file1 --help file2".) Like `getopt_long`, this can mutate the
* arguments given.
*/
CLI_OPT_PARSE_GNU = (1u << 0),
/**
* Force GNU `getopt_long` style behavior; the `POSIXLY_CORRECT`
* environment variable is ignored.
*/
CLI_OPT_PARSE_FORCE_GNU = (1u << 1),
} cli_opt_flag_t;
/** Specification for an available option. */
typedef struct cli_opt_spec {
/** Type of option expected. */
cli_opt_type_t type;
/** Name of the long option. */
const char *name;
/** The alias is the short (one-character) option alias. */
const char alias;
/**
* If this spec is of type `CLI_OPT_TYPE_BOOL`, this is a pointer
* to an `int` that will be set to `1` if the option is specified.
*
* If this spec is of type `CLI_OPT_TYPE_SWITCH`, this is a pointer
* to an `int` that will be set to the opt's `switch_value` (below)
* when this option is specified.
*
* If this spec is of type `CLI_OPT_TYPE_ACCUMULATOR`, this is a
* pointer to an `int` that will be incremented by the opt's
* `switch_value` (below). If no `switch_value` is provided then
* the value will be incremented by 1.
*
* If this spec is of type `CLI_OPT_TYPE_VALUE`,
* `CLI_OPT_TYPE_VALUE_OPTIONAL`, or `CLI_OPT_TYPE_ARG`, this is
* a pointer to a `char *` that will be set to the value
* specified on the command line.
*
* If this spec is of type `CLI_OPT_TYPE_ARGS`, this is a pointer
* to a `char **` that will be set to the remaining values
* specified on the command line.
*/
void *value;
/**
* If this spec is of type `CLI_OPT_TYPE_SWITCH`, this is the value
* to set in the option's `value` pointer when it is specified. If
* this spec is of type `CLI_OPT_TYPE_ACCUMULATOR`, this is the value
* to increment in the option's `value` pointer when it is
* specified. This is ignored for other opt types.
*/
int switch_value;
/**
* Optional usage flags that change parsing behavior and how
* usage information is shown to the end-user.
*/
uint32_t usage;
/**
* The name of the value, provided when creating usage information.
* This is required only for the functions that display usage
* information and only when a spec is of type `CLI_OPT_TYPE_VALUE,
* `CLI_OPT_TYPE_ARG` or `CLI_OPT_TYPE_ARGS``.
*/
const char *value_name;
/**
* Optional short description of the option to display to the
* end-user. This is only used when creating usage information.
*/
const char *help;
} cli_opt_spec;
/** Return value for `cli_opt_parser_next`. */
typedef enum {
/** Parsing is complete; there are no more arguments. */
CLI_OPT_STATUS_DONE = 0,
/**
* This argument was parsed correctly; the `opt` structure is
* populated and the value pointer has been set.
*/
CLI_OPT_STATUS_OK = 1,
/**
* The argument could not be parsed correctly, it does not match
* any of the specifications provided.
*/
CLI_OPT_STATUS_UNKNOWN_OPTION = 2,
/**
* The argument matched a spec of type `CLI_OPT_VALUE`, but no value
* was provided.
*/
CLI_OPT_STATUS_MISSING_VALUE = 3,
/** A required argument was not provided. */
CLI_OPT_STATUS_MISSING_ARGUMENT = 4,
} cli_opt_status_t;
/** An option provided on the command-line. */
typedef struct cli_opt {
/** The status of parsing the most recent argument. */
cli_opt_status_t status;
/**
* The specification that was provided on the command-line, or
* `NULL` if the argument did not match an `cli_opt_spec`.
*/
const cli_opt_spec *spec;
/**
* The argument as it was specified on the command-line, including
* dashes, eg, `-f` or `--foo`.
*/
char *arg;
/**
* If the spec is of type `CLI_OPT_VALUE` or `CLI_OPT_VALUE_OPTIONAL`,
* this is the value provided to the argument.
*/
char *value;
/**
* If the argument is of type `CLI_OPT_ARGS`, this is the number of
* arguments remaining. This value is persisted even when parsing
* is complete and `status` == `CLI_OPT_STATUS_DONE`.
*/
size_t args_len;
} cli_opt;
/* The internal parser state. Callers should not modify this structure. */
typedef struct cli_opt_parser {
const cli_opt_spec *specs;
char **args;
size_t args_len;
unsigned int flags;
/* Parser state */
size_t idx;
size_t arg_idx;
size_t in_args;
size_t in_short;
int needs_sort : 1,
in_literal : 1;
} cli_opt_parser;
/**
* Parses all the command-line arguments and updates all the options using
* the pointers provided. Parsing stops on any invalid argument and
* information about the failure will be provided in the opt argument.
*
* This is the simplest way to parse options; it handles the initialization
* (`parser_init`) and looping (`parser_next`).
*
* @param opt The The `cli_opt` information that failed parsing
* @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed
* @param args The arguments that will be parsed
* @param args_len The length of arguments to be parsed
* @param flags The `cli_opt_flag_t flags for parsing
*/
cli_opt_status_t cli_opt_parse(
cli_opt *opt,
const cli_opt_spec specs[],
char **args,
size_t args_len,
unsigned int flags);
/**
* Initializes a parser that parses the given arguments according to the
* given specifications.
*
* @param parser The `cli_opt_parser` that will be initialized
* @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed
* @param args The arguments that will be parsed
* @param args_len The length of arguments to be parsed
* @param flags The `cli_opt_flag_t flags for parsing
*/
void cli_opt_parser_init(
cli_opt_parser *parser,
const cli_opt_spec specs[],
char **args,
size_t args_len,
unsigned int flags);
/**
* Parses the next command-line argument and places the information about
* the argument into the given `opt` data.
*
* @param opt The `cli_opt` information parsed from the argument
* @param parser An `cli_opt_parser` that has been initialized with
* `cli_opt_parser_init`
* @return true if the caller should continue iterating, or 0 if there are
* no arguments left to process.
*/
cli_opt_status_t cli_opt_parser_next(
cli_opt *opt,
cli_opt_parser *parser);
/**
* Prints the status after parsing the most recent argument. This is
* useful for printing an error message when an unknown argument was
* specified, or when an argument was specified without a value.
*
* @param file The file to print information to
* @param command The name of the command to use when printing (optional)
* @param opt The option that failed to parse
* @return 0 on success, -1 on failure
*/
int cli_opt_status_fprint(
FILE *file,
const char *command,
const cli_opt *opt);
#endif /* CLI_opt_h__ */

194
src/cli/opt_usage.c Normal file
View File

@ -0,0 +1,194 @@
/*
* 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 "cli.h"
#include "str.h"
static int print_spec_name(git_str *out, const cli_opt_spec *spec)
{
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) &&
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c <%s>", spec->alias, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c [<%s>]", spec->alias, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
return git_str_printf(out, "--%s[=<%s>]", spec->name, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE)
return git_str_printf(out, "--%s=<%s>", spec->name, spec->value_name);
if (spec->type == CLI_OPT_TYPE_ARG)
return git_str_printf(out, "<%s>", spec->value_name);
if (spec->type == CLI_OPT_TYPE_ARGS)
return git_str_printf(out, "<%s>...", spec->value_name);
if (spec->type == CLI_OPT_TYPE_LITERAL)
return git_str_printf(out, "--");
if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c", spec->alias);
if (spec->name)
return git_str_printf(out, "--%s", spec->name);
GIT_ASSERT(0);
}
/*
* This is similar to adopt's function, but modified to understand
* that we have a command ("git") and a "subcommand" ("checkout").
* It also understands a terminal's line length and wrap appropriately,
* using a `git_str` for storage.
*/
int cli_opt_usage_fprint(
FILE *file,
const char *command,
const char *subcommand,
const cli_opt_spec specs[])
{
git_str usage = GIT_BUF_INIT, opt = GIT_BUF_INIT;
const cli_opt_spec *spec;
size_t i, prefixlen, linelen;
bool choice = false, next_choice = false, optional = false;
int error;
/* TODO: query actual console width. */
int console_width = 80;
if ((error = git_str_printf(&usage, "usage: %s", command)) < 0)
goto done;
if (subcommand &&
(error = git_str_printf(&usage, " %s", subcommand)) < 0)
goto done;
linelen = git_str_len(&usage);
prefixlen = linelen + 1;
for (spec = specs; spec->type; ++spec) {
if (!choice)
optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE);
if (spec->usage & CLI_OPT_USAGE_HIDDEN)
continue;
if (choice)
git_str_putc(&opt, '|');
else
git_str_clear(&opt);
if (optional && !choice)
git_str_putc(&opt, '[');
if (!optional && !choice && next_choice)
git_str_putc(&opt, '(');
if ((error = print_spec_name(&opt, spec)) < 0)
goto done;
if (!optional && choice && !next_choice)
git_str_putc(&opt, ')');
else if (optional && !next_choice)
git_str_putc(&opt, ']');
if ((choice = next_choice))
continue;
if (git_str_oom(&opt)) {
error = -1;
goto done;
}
if (linelen > prefixlen &&
console_width > 0 &&
linelen + git_str_len(&opt) + 1 > (size_t)console_width) {
git_str_putc(&usage, '\n');
for (i = 0; i < prefixlen; i++)
git_str_putc(&usage, ' ');
linelen = prefixlen;
} else {
git_str_putc(&usage, ' ');
linelen += git_str_len(&opt) + 1;
}
git_str_puts(&usage, git_str_cstr(&opt));
if (git_str_oom(&usage)) {
error = -1;
goto done;
}
}
error = fprintf(file, "%s\n", git_str_cstr(&usage));
done:
error = (error < 0) ? -1 : 0;
git_str_dispose(&usage);
git_str_dispose(&opt);
return error;
}
int cli_opt_usage_error(
const char *subcommand,
const cli_opt_spec specs[],
const cli_opt *invalid_opt)
{
cli_opt_status_fprint(stderr, PROGRAM_NAME, invalid_opt);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs);
return CLI_EXIT_USAGE;
}
int cli_opt_help_fprint(
FILE *file,
const cli_opt_spec specs[])
{
git_str help = GIT_BUF_INIT;
const cli_opt_spec *spec;
int error = 0;
/* Display required arguments first */
for (spec = specs; spec->type; ++spec) {
if (! (spec->usage & CLI_OPT_USAGE_REQUIRED) ||
(spec->usage & CLI_OPT_USAGE_HIDDEN))
continue;
git_str_printf(&help, " ");
if ((error = print_spec_name(&help, spec)) < 0)
goto done;
git_str_printf(&help, ": %s\n", spec->help);
}
/* Display the remaining arguments */
for (spec = specs; spec->type; ++spec) {
if ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
(spec->usage & CLI_OPT_USAGE_HIDDEN))
continue;
git_str_printf(&help, " ");
if ((error = print_spec_name(&help, spec)) < 0)
goto done;
git_str_printf(&help, ": %s\n", spec->help);
}
if (git_str_oom(&help) ||
p_write(fileno(file), help.ptr, help.size) < 0)
error = -1;
done:
error = (error < 0) ? -1 : 0;
git_str_dispose(&help);
return error;
}

35
src/cli/opt_usage.h Normal file
View File

@ -0,0 +1,35 @@
/*
* 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 CLI_opt_usage_h__
#define CLI_opt_usage_h__
/**
* Prints usage information to the given file handle.
*
* @param file The file to print information to
* @param command The name of the command to use when printing
* @param subcommand The name of the subcommand (eg "checkout") to use when printing, or NULL to skip
* @param specs The specifications allowed by the command
* @return 0 on success, -1 on failure
*/
int cli_opt_usage_fprint(
FILE *file,
const char *command,
const char *subcommand,
const cli_opt_spec specs[]);
int cli_opt_usage_error(
const char *subcommand,
const cli_opt_spec specs[],
const cli_opt *invalid_opt);
int cli_opt_help_fprint(
FILE *file,
const cli_opt_spec specs[]);
#endif /* CLI_opt_usage_h__ */

345
src/cli/progress.c Normal file
View File

@ -0,0 +1,345 @@
/*
* 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 <stdarg.h>
#include <stdint.h>
#include "progress.h"
#include "error.h"
/*
* Show updates to the percentage and number of objects received
* separately from the throughput to give an accurate progress while
* avoiding too much noise on the screen.
*/
#define PROGRESS_UPDATE_TIME 0.10
#define THROUGHPUT_UPDATE_TIME 1.00
#define is_nl(c) ((c) == '\r' || (c) == '\n')
#define return_os_error(msg) do { \
git_error_set(GIT_ERROR_OS, "%s", msg); return -1; } while(0)
GIT_INLINE(size_t) no_nl_len(const char *str, size_t len)
{
size_t i = 0;
while (i < len && !is_nl(str[i]))
i++;
return i;
}
GIT_INLINE(size_t) nl_len(bool *has_nl, const char *str, size_t len)
{
size_t i = no_nl_len(str, len);
*has_nl = false;
while (i < len && is_nl(str[i])) {
*has_nl = true;
i++;
}
return i;
}
static int progress_write(cli_progress *progress, bool force, git_str *line)
{
bool has_nl;
size_t no_nl = no_nl_len(line->ptr, line->size);
size_t nl = nl_len(&has_nl, line->ptr + no_nl, line->size - no_nl);
double now = git__timer();
size_t i;
/* Avoid spamming the console with progress updates */
if (!force && line->ptr[line->size - 1] != '\n' && progress->last_update) {
if (now - progress->last_update < PROGRESS_UPDATE_TIME) {
git_str_clear(&progress->deferred);
git_str_put(&progress->deferred, line->ptr, line->size);
return git_str_oom(&progress->deferred) ? -1 : 0;
}
}
/*
* If there's something on this line already (eg, a progress line
* with only a trailing `\r` that we'll print over) then we need
* to really print over it in case we're writing a shorter line.
*/
if (printf("%.*s", (int)no_nl, line->ptr) < 0)
return_os_error("could not print status");
if (progress->onscreen.size) {
for (i = no_nl; i < progress->onscreen.size; i++) {
if (printf(" ") < 0)
return_os_error("could not print status");
}
}
if (printf("%.*s", (int)nl, line->ptr + no_nl) < 0 ||
fflush(stdout) != 0)
return_os_error("could not print status");
git_str_clear(&progress->onscreen);
if (line->ptr[line->size - 1] == '\n') {
progress->last_update = 0;
} else {
git_str_put(&progress->onscreen, line->ptr, line->size);
progress->last_update = now;
}
git_str_clear(&progress->deferred);
return git_str_oom(&progress->onscreen) ? -1 : 0;
}
static int progress_printf(cli_progress *progress, bool force, const char *fmt, ...)
GIT_FORMAT_PRINTF(3, 4);
int progress_printf(cli_progress *progress, bool force, const char *fmt, ...)
{
git_str buf = GIT_BUF_INIT;
va_list ap;
int error;
va_start(ap, fmt);
error = git_str_vprintf(&buf, fmt, ap);
va_end(ap);
if (error < 0)
return error;
error = progress_write(progress, force, &buf);
git_str_dispose(&buf);
return error;
}
static int progress_complete(cli_progress *progress)
{
if (progress->deferred.size)
progress_write(progress, true, &progress->deferred);
if (progress->onscreen.size)
if (printf("\n") < 0)
return_os_error("could not print status");
git_str_clear(&progress->deferred);
git_str_clear(&progress->onscreen);
progress->last_update = 0;
progress->action_start = 0;
progress->action_finish = 0;
return 0;
}
GIT_INLINE(int) percent(size_t completed, size_t total)
{
if (total == 0)
return (completed == 0) ? 100 : 0;
return (int)(((double)completed / (double)total) * 100);
}
int cli_progress_fetch_sideband(const char *str, int len, void *payload)
{
cli_progress *progress = (cli_progress *)payload;
size_t remain;
if (len <= 0)
return 0;
/* Accumulate the sideband data, then print it line-at-a-time. */
if (git_str_put(&progress->sideband, str, len) < 0)
return -1;
str = progress->sideband.ptr;
remain = progress->sideband.size;
while (remain) {
bool has_nl;
size_t line_len = nl_len(&has_nl, str, remain);
if (!has_nl)
break;
if (line_len < INT_MAX) {
int error = progress_printf(progress, true,
"remote: %.*s", (int)line_len, str);
if (error < 0)
return error;
}
str += line_len;
remain -= line_len;
}
git_str_consume_bytes(&progress->sideband, (progress->sideband.size - remain));
return 0;
}
static int fetch_receiving(
cli_progress *progress,
const git_indexer_progress *stats)
{
char *recv_units[] = { "B", "KiB", "MiB", "GiB", "TiB", NULL };
char *rate_units[] = { "B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s", NULL };
double now, recv_len, rate, elapsed;
size_t recv_unit_idx = 0, rate_unit_idx = 0;
bool done = (stats->received_objects == stats->total_objects);
if (!progress->action_start)
progress->action_start = git__timer();
if (done && progress->action_finish)
now = progress->action_finish;
else if (done)
progress->action_finish = now = git__timer();
else
now = git__timer();
if (progress->throughput_update &&
now - progress->throughput_update < THROUGHPUT_UPDATE_TIME) {
elapsed = progress->throughput_update -
progress->action_start;
recv_len = progress->throughput_bytes;
} else {
elapsed = now - progress->action_start;
recv_len = (double)stats->received_bytes;
progress->throughput_update = now;
progress->throughput_bytes = recv_len;
}
rate = elapsed ? recv_len / elapsed : 0;
while (recv_len > 1024 && recv_units[recv_unit_idx+1]) {
recv_len /= 1024;
recv_unit_idx++;
}
while (rate > 1024 && rate_units[rate_unit_idx+1]) {
rate /= 1024;
rate_unit_idx++;
}
return progress_printf(progress, false,
"Receiving objects: %3d%% (%d/%d), %.2f %s | %.2f %s%s\r",
percent(stats->received_objects, stats->total_objects),
stats->received_objects,
stats->total_objects,
recv_len, recv_units[recv_unit_idx],
rate, rate_units[rate_unit_idx],
done ? ", done." : "");
}
static int fetch_resolving(
cli_progress *progress,
const git_indexer_progress *stats)
{
bool done = (stats->indexed_deltas == stats->total_deltas);
return progress_printf(progress, false,
"Resolving deltas: %3d%% (%d/%d)%s\r",
percent(stats->indexed_deltas, stats->total_deltas),
stats->indexed_deltas, stats->total_deltas,
done ? ", done." : "");
}
int cli_progress_fetch_transfer(const git_indexer_progress *stats, void *payload)
{
cli_progress *progress = (cli_progress *)payload;
int error = 0;
switch (progress->action) {
case CLI_PROGRESS_NONE:
progress->action = CLI_PROGRESS_RECEIVING;
/* fall through */
case CLI_PROGRESS_RECEIVING:
if ((error = fetch_receiving(progress, stats)) < 0)
break;
/*
* Upgrade from receiving to resolving; do this after the
* final call to cli_progress_fetch_receiving (above) to
* ensure that we've printed a final "done" string after
* any sideband data.
*/
if (!stats->indexed_deltas)
break;
progress_complete(progress);
progress->action = CLI_PROGRESS_RESOLVING;
/* fall through */
case CLI_PROGRESS_RESOLVING:
error = fetch_resolving(progress, stats);
break;
default:
/* should not be reached */
GIT_ASSERT(!"unexpected progress state");
}
return error;
}
void cli_progress_checkout(
const char *path,
size_t completed_steps,
size_t total_steps,
void *payload)
{
cli_progress *progress = (cli_progress *)payload;
bool done = (completed_steps == total_steps);
GIT_UNUSED(path);
if (progress->action != CLI_PROGRESS_CHECKING_OUT) {
progress_complete(progress);
progress->action = CLI_PROGRESS_CHECKING_OUT;
}
progress_printf(progress, false,
"Checking out files: %3d%% (%" PRIuZ "/%" PRIuZ ")%s\r",
percent(completed_steps, total_steps),
completed_steps, total_steps,
done ? ", done." : "");
}
int cli_progress_abort(cli_progress *progress)
{
if (progress->onscreen.size > 0 && printf("\n") < 0)
return_os_error("could not print status");
return 0;
}
int cli_progress_finish(cli_progress *progress)
{
int error = progress->action ? progress_complete(progress) : 0;
progress->action = 0;
return error;
}
void cli_progress_dispose(cli_progress *progress)
{
if (progress == NULL)
return;
git_str_dispose(&progress->sideband);
git_str_dispose(&progress->onscreen);
git_str_dispose(&progress->deferred);
memset(progress, 0, sizeof(cli_progress));
}

117
src/cli/progress.h Normal file
View File

@ -0,0 +1,117 @@
/*
* 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 CLI_progress_h__
#define CLI_progress_h__
#include <git2.h>
#include "str.h"
/*
* A general purpose set of progress printing functions. An individual
* `cli_progress` object is capable of displaying progress for a single
* function, even if that function displays multiple pieces of progress
* (like `git_clone`). `cli_progress_finish` should be called after
* any function invocation to re-set state.
*/
typedef enum {
CLI_PROGRESS_NONE,
CLI_PROGRESS_RECEIVING,
CLI_PROGRESS_RESOLVING,
CLI_PROGRESS_CHECKING_OUT
} cli_progress_t;
typedef struct {
cli_progress_t action;
/* Actions may time themselves (eg fetch) but are not required to */
double action_start;
double action_finish;
/* Last console update, avoid too frequent updates. */
double last_update;
/* Accumulators for partial output and deferred updates. */
git_str sideband;
git_str onscreen;
git_str deferred;
/* Last update about throughput */
double throughput_update;
double throughput_bytes;
} cli_progress;
#define CLI_PROGRESS_INIT { 0 }
/**
* Prints sideband data from fetch to the console. Suitable for a
* `sideband_progress` callback for `git_fetch_options`.
*
* @param str The sideband string
* @param len The length of the sideband string
* @param payload A pointer to the cli_progress
* @return 0 on success, -1 on failure
*/
extern int cli_progress_fetch_sideband(
const char *str,
int len,
void *payload);
/**
* Prints fetch transfer statistics to the console. Suitable for a
* `transfer_progress` callback for `git_fetch_options`.
*
* @param stats The indexer stats
* @param payload A pointer to the cli_progress
* @return 0 on success, -1 on failure
*/
extern int cli_progress_fetch_transfer(
const git_indexer_progress *stats,
void *payload);
/**
* Prints checkout progress to the console. Suitable for a
* `progress_cb` callback for `git_checkout_options`.
*
* @param path The path being written
* @param completed_steps The completed checkout steps
* @param total_steps The total number of checkout steps
* @param payload A pointer to the cli_progress
*/
extern void cli_progress_checkout(
const char *path,
size_t completed_steps,
size_t total_steps,
void *payload);
/**
* Stop displaying progress quickly; suitable for stopping an application
* quickly. Does not display any lines that were buffered, just gets the
* console back to a sensible place.
*
* @param progress The progress information
* @return 0 on success, -1 on failure
*/
extern int cli_progress_abort(cli_progress *progress);
/**
* Finishes displaying progress; flushes any buffered output.
*
* @param progress The progress information
* @return 0 on success, -1 on failure
*/
extern int cli_progress_finish(cli_progress *progress);
/**
* Disposes the progress information.
*
* @param progress The progress information
*/
extern void cli_progress_dispose(cli_progress *progress);
#endif /* CLI_progress_h__ */

20
src/cli/sighandler.h Normal file
View File

@ -0,0 +1,20 @@
/*
* 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 CLI_sighandler_h__
#define CLI_sighandler_h__
/**
* Sets up a signal handler that will run when the process is interrupted
* (via SIGINT on POSIX or Control-C or Control-Break on Windows).
*
* @param handler The function to run on interrupt
* @return 0 on success, -1 on failure
*/
int cli_sighandler_set_interrupt(void (*handler)(void));
#endif /* CLI_sighandler_h__ */

36
src/cli/unix/sighandler.c Normal file
View File

@ -0,0 +1,36 @@
/*
* 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 <stdint.h>
#include <signal.h>
#include "git2_util.h"
#include "cli.h"
static void (*interrupt_handler)(void) = NULL;
static void interrupt_proxy(int signal)
{
GIT_UNUSED(signal);
interrupt_handler();
}
int cli_sighandler_set_interrupt(void (*handler)(void))
{
void (*result)(int);
if ((interrupt_handler = handler) != NULL)
result = signal(SIGINT, interrupt_proxy);
else
result = signal(SIGINT, SIG_DFL);
if (result == SIG_ERR) {
git_error_set(GIT_ERROR_OS, "could not set signal handler");
return -1;
}
return 0;
}

View File

@ -0,0 +1,3 @@
#include <git2.h>
#include "cli.h"

View File

@ -0,0 +1,37 @@
/*
* 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 "git2_util.h"
#include <windows.h>
#include "cli.h"
static void (*interrupt_handler)(void) = NULL;
static BOOL WINAPI interrupt_proxy(DWORD signal)
{
GIT_UNUSED(signal);
interrupt_handler();
return TRUE;
}
int cli_sighandler_set_interrupt(void (*handler)(void))
{
BOOL result;
if ((interrupt_handler = handler) != NULL)
result = SetConsoleCtrlHandler(interrupt_proxy, FALSE);
else
result = SetConsoleCtrlHandler(NULL, FALSE);
if (!result) {
git_error_set(GIT_ERROR_OS, "could not set control control handler");
return -1;
}
return 0;
}

View File

@ -46,8 +46,17 @@
#cmakedefine GIT_SHA1_WIN32 1 #cmakedefine GIT_SHA1_WIN32 1
#cmakedefine GIT_SHA1_COMMON_CRYPTO 1 #cmakedefine GIT_SHA1_COMMON_CRYPTO 1
#cmakedefine GIT_SHA1_OPENSSL 1 #cmakedefine GIT_SHA1_OPENSSL 1
#cmakedefine GIT_SHA1_OPENSSL_DYNAMIC 1
#cmakedefine GIT_SHA1_MBEDTLS 1 #cmakedefine GIT_SHA1_MBEDTLS 1
#cmakedefine GIT_SHA256_BUILTIN 1
#cmakedefine GIT_SHA256_WIN32 1
#cmakedefine GIT_SHA256_COMMON_CRYPTO 1
#cmakedefine GIT_SHA256_OPENSSL 1
#cmakedefine GIT_SHA256_OPENSSL_DYNAMIC 1
#cmakedefine GIT_SHA256_MBEDTLS 1
#cmakedefine GIT_RAND_GETENTROPY 1 #cmakedefine GIT_RAND_GETENTROPY 1
#cmakedefine GIT_RAND_GETLOADAVG 1
#endif #endif

View File

@ -1,40 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_hash_sha1_h__
#define INCLUDE_hash_sha1_h__
#include "common.h"
typedef struct git_hash_sha1_ctx git_hash_sha1_ctx;
#if defined(GIT_SHA1_COLLISIONDETECT)
# include "sha1/collisiondetect.h"
#elif defined(GIT_SHA1_COMMON_CRYPTO)
# include "sha1/common_crypto.h"
#elif defined(GIT_SHA1_OPENSSL)
# include "sha1/openssl.h"
#elif defined(GIT_SHA1_WIN32)
# include "sha1/win32.h"
#elif defined(GIT_SHA1_MBEDTLS)
# include "sha1/mbedtls.h"
#else
# include "sha1/generic.h"
#endif
#define GIT_HASH_SHA1_SIZE 20
int git_hash_sha1_global_init(void);
int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx);
void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx);
int git_hash_sha1_init(git_hash_sha1_ctx *c);
int git_hash_sha1_update(git_hash_sha1_ctx *c, const void *data, size_t len);
int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *c);
#endif

View File

@ -1,300 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "generic.h"
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
/*
* Force usage of rol or ror by selecting the one with the smaller constant.
* It _can_ generate slightly smaller code (a constant of 1 is special), but
* perhaps more importantly it's possibly faster on any uarch that does a
* rotate with a loop.
*/
#define SHA_ASM(op, x, n) (__extension__ ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }))
#define SHA_ROL(x,n) SHA_ASM("rol", x, n)
#define SHA_ROR(x,n) SHA_ASM("ror", x, n)
#else
#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n))
#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n)
#endif
/*
* If you have 32 registers or more, the compiler can (and should)
* try to change the array[] accesses into registers. However, on
* machines with less than ~25 registers, that won't really work,
* and at least gcc will make an unholy mess of it.
*
* So to avoid that mess which just slows things down, we force
* the stores to memory to actually happen (we might be better off
* with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
* suggested by Artur Skawina - that will also make gcc unable to
* try to do the silly "optimize away loads" part because it won't
* see what the value will be).
*
* Ben Herrenschmidt reports that on PPC, the C version comes close
* to the optimized asm with this (ie on PPC you don't want that
* 'volatile', since there are lots of registers).
*
* On ARM we get the best code generation by forcing a full memory barrier
* between each SHA_ROUND, otherwise gcc happily get wild with spilling and
* the stack frame size simply explode and performance goes down the drain.
*/
#if defined(__i386__) || defined(__x86_64__)
#define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
#elif defined(__GNUC__) && defined(__arm__)
#define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
#else
#define setW(x, val) (W(x) = (val))
#endif
/*
* Performance might be improved if the CPU architecture is OK with
* unaligned 32-bit loads and a fast ntohl() is available.
* Otherwise fall back to byte loads and shifts which is portable,
* and is faster on architectures with memory alignment issues.
*/
#if defined(__i386__) || defined(__x86_64__) || \
defined(_M_IX86) || defined(_M_X64) || \
defined(__ppc__) || defined(__ppc64__) || \
defined(__powerpc__) || defined(__powerpc64__) || \
defined(__s390__) || defined(__s390x__)
#define get_be32(p) ntohl(*(const unsigned int *)(p))
#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
#else
#define get_be32(p) ( \
(*((const unsigned char *)(p) + 0) << 24) | \
(*((const unsigned char *)(p) + 1) << 16) | \
(*((const unsigned char *)(p) + 2) << 8) | \
(*((const unsigned char *)(p) + 3) << 0) )
#define put_be32(p, v) do { \
unsigned int __v = (v); \
*((unsigned char *)(p) + 0) = __v >> 24; \
*((unsigned char *)(p) + 1) = __v >> 16; \
*((unsigned char *)(p) + 2) = __v >> 8; \
*((unsigned char *)(p) + 3) = __v >> 0; } while (0)
#endif
/* This "rolls" over the 512-bit array */
#define W(x) (array[(x)&15])
/*
* Where do we get the source from? The first 16 iterations get it from
* the input data, the next mix it from the 512-bit array.
*/
#define SHA_SRC(t) get_be32(data + t)
#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
unsigned int TEMP = input(t); setW(t, TEMP); \
E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
B = SHA_ROR(B, 2); } while (0)
#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
static void hash__block(git_hash_sha1_ctx *ctx, const unsigned int *data)
{
unsigned int A,B,C,D,E;
unsigned int array[16];
A = ctx->H[0];
B = ctx->H[1];
C = ctx->H[2];
D = ctx->H[3];
E = ctx->H[4];
/* Round 1 - iterations 0-16 take their input from 'data' */
T_0_15( 0, A, B, C, D, E);
T_0_15( 1, E, A, B, C, D);
T_0_15( 2, D, E, A, B, C);
T_0_15( 3, C, D, E, A, B);
T_0_15( 4, B, C, D, E, A);
T_0_15( 5, A, B, C, D, E);
T_0_15( 6, E, A, B, C, D);
T_0_15( 7, D, E, A, B, C);
T_0_15( 8, C, D, E, A, B);
T_0_15( 9, B, C, D, E, A);
T_0_15(10, A, B, C, D, E);
T_0_15(11, E, A, B, C, D);
T_0_15(12, D, E, A, B, C);
T_0_15(13, C, D, E, A, B);
T_0_15(14, B, C, D, E, A);
T_0_15(15, A, B, C, D, E);
/* Round 1 - tail. Input from 512-bit mixing array */
T_16_19(16, E, A, B, C, D);
T_16_19(17, D, E, A, B, C);
T_16_19(18, C, D, E, A, B);
T_16_19(19, B, C, D, E, A);
/* Round 2 */
T_20_39(20, A, B, C, D, E);
T_20_39(21, E, A, B, C, D);
T_20_39(22, D, E, A, B, C);
T_20_39(23, C, D, E, A, B);
T_20_39(24, B, C, D, E, A);
T_20_39(25, A, B, C, D, E);
T_20_39(26, E, A, B, C, D);
T_20_39(27, D, E, A, B, C);
T_20_39(28, C, D, E, A, B);
T_20_39(29, B, C, D, E, A);
T_20_39(30, A, B, C, D, E);
T_20_39(31, E, A, B, C, D);
T_20_39(32, D, E, A, B, C);
T_20_39(33, C, D, E, A, B);
T_20_39(34, B, C, D, E, A);
T_20_39(35, A, B, C, D, E);
T_20_39(36, E, A, B, C, D);
T_20_39(37, D, E, A, B, C);
T_20_39(38, C, D, E, A, B);
T_20_39(39, B, C, D, E, A);
/* Round 3 */
T_40_59(40, A, B, C, D, E);
T_40_59(41, E, A, B, C, D);
T_40_59(42, D, E, A, B, C);
T_40_59(43, C, D, E, A, B);
T_40_59(44, B, C, D, E, A);
T_40_59(45, A, B, C, D, E);
T_40_59(46, E, A, B, C, D);
T_40_59(47, D, E, A, B, C);
T_40_59(48, C, D, E, A, B);
T_40_59(49, B, C, D, E, A);
T_40_59(50, A, B, C, D, E);
T_40_59(51, E, A, B, C, D);
T_40_59(52, D, E, A, B, C);
T_40_59(53, C, D, E, A, B);
T_40_59(54, B, C, D, E, A);
T_40_59(55, A, B, C, D, E);
T_40_59(56, E, A, B, C, D);
T_40_59(57, D, E, A, B, C);
T_40_59(58, C, D, E, A, B);
T_40_59(59, B, C, D, E, A);
/* Round 4 */
T_60_79(60, A, B, C, D, E);
T_60_79(61, E, A, B, C, D);
T_60_79(62, D, E, A, B, C);
T_60_79(63, C, D, E, A, B);
T_60_79(64, B, C, D, E, A);
T_60_79(65, A, B, C, D, E);
T_60_79(66, E, A, B, C, D);
T_60_79(67, D, E, A, B, C);
T_60_79(68, C, D, E, A, B);
T_60_79(69, B, C, D, E, A);
T_60_79(70, A, B, C, D, E);
T_60_79(71, E, A, B, C, D);
T_60_79(72, D, E, A, B, C);
T_60_79(73, C, D, E, A, B);
T_60_79(74, B, C, D, E, A);
T_60_79(75, A, B, C, D, E);
T_60_79(76, E, A, B, C, D);
T_60_79(77, D, E, A, B, C);
T_60_79(78, C, D, E, A, B);
T_60_79(79, B, C, D, E, A);
ctx->H[0] += A;
ctx->H[1] += B;
ctx->H[2] += C;
ctx->H[3] += D;
ctx->H[4] += E;
}
int git_hash_sha1_global_init(void)
{
return 0;
}
int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
{
return git_hash_sha1_init(ctx);
}
void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
{
GIT_UNUSED(ctx);
}
int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
{
ctx->size = 0;
/* Initialize H with the magic constants (see FIPS180 for constants) */
ctx->H[0] = 0x67452301;
ctx->H[1] = 0xefcdab89;
ctx->H[2] = 0x98badcfe;
ctx->H[3] = 0x10325476;
ctx->H[4] = 0xc3d2e1f0;
return 0;
}
int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
{
unsigned int lenW = ctx->size & 63;
ctx->size += len;
/* Read the data into W and process blocks as they get full */
if (lenW) {
unsigned int left = 64 - lenW;
if (len < left)
left = (unsigned int)len;
memcpy(lenW + (char *)ctx->W, data, left);
lenW = (lenW + left) & 63;
len -= left;
data = ((const char *)data + left);
if (lenW)
return 0;
hash__block(ctx, ctx->W);
}
while (len >= 64) {
hash__block(ctx, data);
data = ((const char *)data + 64);
len -= 64;
}
if (len)
memcpy(ctx->W, data, len);
return 0;
}
int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
{
static const unsigned char pad[64] = { 0x80 };
unsigned int padlen[2];
int i;
/* Pad with a binary 1 (ie 0x80), then zeroes, then length */
padlen[0] = htonl((uint32_t)(ctx->size >> 29));
padlen[1] = htonl((uint32_t)(ctx->size << 3));
i = ctx->size & 63;
git_hash_sha1_update(ctx, pad, 1+ (63 & (55 - i)));
git_hash_sha1_update(ctx, padlen, 8);
/* Output hash */
for (i = 0; i < 5; i++)
put_be32(out + i*4, ctx->H[i]);
return 0;
}

View File

@ -1,59 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "openssl.h"
int git_hash_sha1_global_init(void)
{
return 0;
}
int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
{
return git_hash_sha1_init(ctx);
}
void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
{
GIT_UNUSED(ctx);
}
int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
{
GIT_ASSERT_ARG(ctx);
if (SHA1_Init(&ctx->c) != 1) {
git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to initialize hash context");
return -1;
}
return 0;
}
int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
{
GIT_ASSERT_ARG(ctx);
if (SHA1_Update(&ctx->c, data, len) != 1) {
git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to update hash");
return -1;
}
return 0;
}
int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
{
GIT_ASSERT_ARG(ctx);
if (SHA1_Final(out, &ctx->c) != 1) {
git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to finalize hash");
return -1;
}
return 0;
}

View File

@ -1,333 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "win32.h"
#include "runtime.h"
#include <wincrypt.h>
#include <strsafe.h>
#define GIT_HASH_CNG_DLL_NAME "bcrypt.dll"
/* BCRYPT_SHA1_ALGORITHM */
#define GIT_HASH_CNG_HASH_TYPE L"SHA1"
/* BCRYPT_OBJECT_LENGTH */
#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
/* BCRYPT_HASH_REUSEABLE_FLAGS */
#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
static git_hash_prov hash_prov = {0};
/* Hash initialization */
/* Initialize CNG, if available */
GIT_INLINE(int) hash_cng_prov_init(void)
{
char dll_path[MAX_PATH];
DWORD dll_path_len, size_len;
/* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
if (!git_has_win32_version(6, 0, 1)) {
git_error_set(GIT_ERROR_SHA1, "CryptoNG is not supported on this platform");
return -1;
}
/* Load bcrypt.dll explicitly from the system directory */
if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
dll_path_len > MAX_PATH ||
StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
(hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL) {
git_error_set(GIT_ERROR_SHA1, "CryptoNG library could not be loaded");
return -1;
}
/* Load the function addresses */
if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL ||
(hash_prov.prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptGetProperty")) == NULL ||
(hash_prov.prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCreateHash")) == NULL ||
(hash_prov.prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptFinishHash")) == NULL ||
(hash_prov.prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptHashData")) == NULL ||
(hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL ||
(hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) {
FreeLibrary(hash_prov.prov.cng.dll);
git_error_set(GIT_ERROR_OS, "CryptoNG functions could not be loaded");
return -1;
}
/* Load the SHA1 algorithm */
if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) {
FreeLibrary(hash_prov.prov.cng.dll);
git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
return -1;
}
/* Get storage space for the hash object */
if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) {
hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
FreeLibrary(hash_prov.prov.cng.dll);
git_error_set(GIT_ERROR_OS, "algorithm handle could not be found");
return -1;
}
hash_prov.type = CNG;
return 0;
}
GIT_INLINE(void) hash_cng_prov_shutdown(void)
{
hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
FreeLibrary(hash_prov.prov.cng.dll);
hash_prov.type = INVALID;
}
/* Initialize CryptoAPI */
GIT_INLINE(int) hash_cryptoapi_prov_init()
{
if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
git_error_set(GIT_ERROR_OS, "legacy hash context could not be started");
return -1;
}
hash_prov.type = CRYPTOAPI;
return 0;
}
GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
{
CryptReleaseContext(hash_prov.prov.cryptoapi.handle, 0);
hash_prov.type = INVALID;
}
static void sha1_shutdown(void)
{
if (hash_prov.type == CNG)
hash_cng_prov_shutdown();
else if(hash_prov.type == CRYPTOAPI)
hash_cryptoapi_prov_shutdown();
}
int git_hash_sha1_global_init(void)
{
int error = 0;
if (hash_prov.type != INVALID)
return 0;
if ((error = hash_cng_prov_init()) < 0)
error = hash_cryptoapi_prov_init();
if (!error)
error = git_runtime_shutdown_register(sha1_shutdown);
return error;
}
/* CryptoAPI: available in Windows XP and newer */
GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_sha1_ctx *ctx)
{
ctx->type = CRYPTOAPI;
ctx->prov = &hash_prov;
return git_hash_sha1_init(ctx);
}
GIT_INLINE(int) hash_cryptoapi_init(git_hash_sha1_ctx *ctx)
{
if (ctx->ctx.cryptoapi.valid)
CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
ctx->ctx.cryptoapi.valid = 0;
git_error_set(GIT_ERROR_OS, "legacy hash implementation could not be created");
return -1;
}
ctx->ctx.cryptoapi.valid = 1;
return 0;
}
GIT_INLINE(int) hash_cryptoapi_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len)
{
const BYTE *data = (BYTE *)_data;
GIT_ASSERT(ctx->ctx.cryptoapi.valid);
while (len > 0) {
DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) {
git_error_set(GIT_ERROR_OS, "legacy hash data could not be updated");
return -1;
}
data += chunk;
len -= chunk;
}
return 0;
}
GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out, git_hash_sha1_ctx *ctx)
{
DWORD len = GIT_HASH_SHA1_SIZE;
int error = 0;
GIT_ASSERT(ctx->ctx.cryptoapi.valid);
if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out, &len, 0)) {
git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished");
error = -1;
}
CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
ctx->ctx.cryptoapi.valid = 0;
return error;
}
GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_sha1_ctx *ctx)
{
if (ctx->ctx.cryptoapi.valid)
CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
}
/* CNG: Available in Windows Server 2008 and newer */
GIT_INLINE(int) hash_ctx_cng_init(git_hash_sha1_ctx *ctx)
{
if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL)
return -1;
if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) {
git__free(ctx->ctx.cng.hash_object);
git_error_set(GIT_ERROR_OS, "hash implementation could not be created");
return -1;
}
ctx->type = CNG;
ctx->prov = &hash_prov;
return 0;
}
GIT_INLINE(int) hash_cng_init(git_hash_sha1_ctx *ctx)
{
BYTE hash[GIT_OID_RAWSZ];
if (!ctx->ctx.cng.updated)
return 0;
/* CNG needs to be finished to restart */
if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0) {
git_error_set(GIT_ERROR_OS, "hash implementation could not be finished");
return -1;
}
ctx->ctx.cng.updated = 0;
return 0;
}
GIT_INLINE(int) hash_cng_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len)
{
PBYTE data = (PBYTE)_data;
while (len > 0) {
ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len;
if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) {
git_error_set(GIT_ERROR_OS, "hash could not be updated");
return -1;
}
data += chunk;
len -= chunk;
}
return 0;
}
GIT_INLINE(int) hash_cng_final(unsigned char *out, git_hash_sha1_ctx *ctx)
{
if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out, GIT_HASH_SHA1_SIZE, 0) < 0) {
git_error_set(GIT_ERROR_OS, "hash could not be finished");
return -1;
}
ctx->ctx.cng.updated = 0;
return 0;
}
GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_sha1_ctx *ctx)
{
ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle);
git__free(ctx->ctx.cng.hash_object);
}
/* Indirection between CryptoAPI and CNG */
int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
{
int error = 0;
GIT_ASSERT_ARG(ctx);
/*
* When compiled with GIT_THREADS, the global hash_prov data is
* initialized with git_libgit2_init. Otherwise, it must be initialized
* at first use.
*/
if (hash_prov.type == INVALID && (error = git_hash_sha1_global_init()) < 0)
return error;
memset(ctx, 0x0, sizeof(git_hash_sha1_ctx));
return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx);
}
int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
{
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)
{
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(unsigned char *out, git_hash_sha1_ctx *ctx)
{
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)
{
if (!ctx)
return;
else if (ctx->type == CNG)
hash_ctx_cng_cleanup(ctx);
else if(ctx->type == CRYPTOAPI)
hash_ctx_cryptoapi_cleanup(ctx);
}

View File

@ -1,128 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_hash_sha1_win32_h__
#define INCLUDE_hash_sha1_win32_h__
#include "hash/sha1.h"
#include <wincrypt.h>
#include <strsafe.h>
enum hash_win32_prov_type {
INVALID = 0,
CRYPTOAPI,
CNG
};
/*
* CryptoAPI is available for hashing on Windows XP and newer.
*/
struct hash_cryptoapi_prov {
HCRYPTPROV handle;
};
/*
* CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is
* preferred, however it is only available on Windows 2008 and newer and
* must therefore be dynamically loaded, and we must inline constants that
* would not exist when building in pre-Windows 2008 environments.
*/
/* Function declarations for CNG */
typedef NTSTATUS (WINAPI *hash_win32_cng_open_algorithm_provider_fn)(
HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm,
LPCWSTR pszAlgId,
LPCWSTR pszImplementation,
DWORD dwFlags);
typedef NTSTATUS (WINAPI *hash_win32_cng_get_property_fn)(
HANDLE /* BCRYPT_HANDLE */ hObject,
LPCWSTR pszProperty,
PUCHAR pbOutput,
ULONG cbOutput,
ULONG *pcbResult,
ULONG dwFlags);
typedef NTSTATUS (WINAPI *hash_win32_cng_create_hash_fn)(
HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
HANDLE /* BCRYPT_HASH_HANDLE */ *phHash,
PUCHAR pbHashObject, ULONG cbHashObject,
PUCHAR pbSecret,
ULONG cbSecret,
ULONG dwFlags);
typedef NTSTATUS (WINAPI *hash_win32_cng_finish_hash_fn)(
HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
PUCHAR pbOutput,
ULONG cbOutput,
ULONG dwFlags);
typedef NTSTATUS (WINAPI *hash_win32_cng_hash_data_fn)(
HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
PUCHAR pbInput,
ULONG cbInput,
ULONG dwFlags);
typedef NTSTATUS (WINAPI *hash_win32_cng_destroy_hash_fn)(
HANDLE /* BCRYPT_HASH_HANDLE */ hHash);
typedef NTSTATUS (WINAPI *hash_win32_cng_close_algorithm_provider_fn)(
HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
ULONG dwFlags);
struct hash_cng_prov {
/* DLL for CNG */
HINSTANCE dll;
/* Function pointers for CNG */
hash_win32_cng_open_algorithm_provider_fn open_algorithm_provider;
hash_win32_cng_get_property_fn get_property;
hash_win32_cng_create_hash_fn create_hash;
hash_win32_cng_finish_hash_fn finish_hash;
hash_win32_cng_hash_data_fn hash_data;
hash_win32_cng_destroy_hash_fn destroy_hash;
hash_win32_cng_close_algorithm_provider_fn close_algorithm_provider;
HANDLE /* BCRYPT_ALG_HANDLE */ handle;
DWORD hash_object_size;
};
typedef struct {
enum hash_win32_prov_type type;
union {
struct hash_cryptoapi_prov cryptoapi;
struct hash_cng_prov cng;
} prov;
} git_hash_prov;
/* Hash contexts */
struct hash_cryptoapi_ctx {
bool valid;
HCRYPTHASH hash_handle;
};
struct hash_cng_ctx {
bool updated;
HANDLE /* BCRYPT_HASH_HANDLE */ hash_handle;
PBYTE hash_object;
};
struct git_hash_sha1_ctx {
enum hash_win32_prov_type type;
git_hash_prov *prov;
union {
struct hash_cryptoapi_ctx cryptoapi;
struct hash_cng_ctx cng;
} ctx;
};
#endif

131
src/libgit2/CMakeLists.txt Normal file
View File

@ -0,0 +1,131 @@
# libgit2: the shared library: this CMakeLists.txt compiles the core
# git library functionality.
add_library(libgit2 OBJECT)
set_target_properties(libgit2 PROPERTIES C_STANDARD 90)
set_target_properties(libgit2 PROPERTIES C_EXTENSIONS OFF)
include(PkgBuildConfig)
set(LIBGIT2_INCLUDES
"${PROJECT_BINARY_DIR}/src"
"${PROJECT_SOURCE_DIR}/src/libgit2"
"${PROJECT_SOURCE_DIR}/src/util"
"${PROJECT_SOURCE_DIR}/include")
if(WIN32 AND EMBED_SSH_PATH)
file(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
list(SORT SRC_SSH)
target_sources(libgit2 PRIVATE ${SRC_SSH})
list(APPEND LIBGIT2_SYSTEM_INCLUDES "${EMBED_SSH_PATH}/include")
file(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
set(GIT_SSH 1)
endif()
# Collect sourcefiles
file(GLOB SRC_H
"${PROJECT_SOURCE_DIR}/include/git2.h"
"${PROJECT_SOURCE_DIR}/include/git2/*.h"
"${PROJECT_SOURCE_DIR}/include/git2/sys/*.h")
list(SORT SRC_H)
target_sources(libgit2 PRIVATE ${SRC_H})
file(GLOB SRC_GIT2 *.c *.h
streams/*.c streams/*.h
transports/*.c transports/*.h
xdiff/*.c xdiff/*.h)
list(SORT SRC_GIT2)
target_sources(libgit2 PRIVATE ${SRC_GIT2})
if(WIN32 AND NOT CYGWIN)
# Add resource information on Windows
set(SRC_RC "git2.rc")
endif()
if(APPLE)
# The old Secure Transport API has been deprecated in macOS 10.15.
set_source_files_properties(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated)
endif()
# the xdiff dependency is not (yet) warning-free, disable warnings
# as errors for the xdiff sources until we've sorted them out
if(MSVC)
set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xmerge.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS -WX-)
else()
set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
endif()
ide_split_sources(libgit2)
list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:libgit2> ${LIBGIT2_DEPENDENCY_OBJECTS})
target_include_directories(libgit2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_include_directories(libgit2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
#
# Compile and link libgit2
#
add_library(libgit2package ${SRC_RC} ${LIBGIT2_OBJECTS})
target_link_libraries(libgit2package ${LIBGIT2_SYSTEM_LIBS})
set_target_properties(libgit2package PROPERTIES C_STANDARD 90)
set_target_properties(libgit2package PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(libgit2package PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(libgit2package PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
# Win64+MSVC+static libs = linker error
if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
set_target_properties(libgit2package PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
endif()
ide_split_sources(libgit2package)
if(SONAME)
set_target_properties(libgit2package PROPERTIES VERSION ${libgit2_VERSION})
set_target_properties(libgit2package PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}")
if(LIBGIT2_FILENAME)
target_compile_definitions(libgit2package PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
set_target_properties(libgit2package PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
elseif(DEFINED LIBGIT2_PREFIX)
set_target_properties(libgit2package PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
endif()
endif()
pkg_build_config(NAME libgit2
VERSION ${libgit2_VERSION}
DESCRIPTION "The git library, take 2"
LIBS_SELF git2
PRIVATE_LIBS ${LIBGIT2_PC_LIBS}
REQUIRES ${LIBGIT2_PC_REQUIRES})
if(MSVC_IDE)
# Precompiled headers
set_target_properties(libgit2package PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
endif()
# Install
install(TARGETS libgit2package
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/git2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${PROJECT_SOURCE_DIR}/include/git2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

View File

@ -101,7 +101,7 @@ static int write_file_stream(
git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size) git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size)
{ {
int fd, error; int fd, error;
char buffer[FILEIO_BUFSIZE]; char buffer[GIT_BUFSIZE_FILEIO];
git_odb_stream *stream = NULL; git_odb_stream *stream = NULL;
ssize_t read_len = -1; ssize_t read_len = -1;
git_object_size_t written = 0; git_object_size_t written = 0;

View File

@ -53,6 +53,17 @@ static int not_a_local_branch(const char *reference_name)
return -1; return -1;
} }
static bool branch_name_is_valid(const char *branch_name)
{
/*
* 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
*/
return branch_name[0] != '-' && git__strcmp(branch_name, "HEAD");
}
static int create_branch( static int create_branch(
git_reference **ref_out, git_reference **ref_out,
git_repository *repository, git_repository *repository,
@ -73,8 +84,8 @@ static int create_branch(
GIT_ASSERT_ARG(ref_out); GIT_ASSERT_ARG(ref_out);
GIT_ASSERT_ARG(git_commit_owner(commit) == repository); GIT_ASSERT_ARG(git_commit_owner(commit) == repository);
if (!git__strcmp(branch_name, "HEAD")) { if (!branch_name_is_valid(branch_name)) {
git_error_set(GIT_ERROR_REFERENCE, "'HEAD' is not a valid branch name"); git_error_set(GIT_ERROR_REFERENCE, "'%s' is not a valid branch name", branch_name);
error = -1; error = -1;
goto cleanup; goto cleanup;
} }
@ -797,13 +808,7 @@ int git_branch_name_is_valid(int *valid, const char *name)
*valid = 0; *valid = 0;
/* if (!name || !branch_name_is_valid(name))
* 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; goto done;
if ((error = git_str_puts(&ref_name, GIT_REFS_HEADS_DIR)) < 0 || if ((error = git_str_puts(&ref_name, GIT_REFS_HEADS_DIR)) < 0 ||

Some files were not shown because too many files have changed in this diff Show More