mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-02 04:47:51 +00:00
New upstream version 1.5.0+ds
This commit is contained in:
parent
e579e0f707
commit
ad5611d85b
92
.clang-format
Normal file
92
.clang-format
Normal 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
5
.github/release.yml
vendored
@ -6,10 +6,13 @@ changelog:
|
||||
- title: Bug fixes
|
||||
labels:
|
||||
- bug
|
||||
- title: Security fixes
|
||||
labels:
|
||||
- security
|
||||
- title: Code cleanups
|
||||
labels:
|
||||
- cleanup
|
||||
- title: CI improvements
|
||||
- title: Build and CI improvements
|
||||
labels:
|
||||
- build
|
||||
- title: Documentation improvements
|
||||
|
82
.github/workflows/benchmark.yml
vendored
Normal file
82
.github/workflows/benchmark.yml
vendored
Normal 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()
|
36
.github/workflows/codeql.yml
vendored
36
.github/workflows/codeql.yml
vendored
@ -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
|
54
.github/workflows/main.yml
vendored
54
.github/workflows/main.yml
vendored
@ -10,7 +10,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
docker-registry: docker.pkg.github.com
|
||||
docker-registry: ghcr.io
|
||||
docker-config-path: source/ci/docker
|
||||
|
||||
jobs:
|
||||
@ -69,7 +69,7 @@ jobs:
|
||||
if [ "${{ matrix.container.base }}" != "" ]; then
|
||||
BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
|
||||
fi
|
||||
docker build -t ${{ env.docker-registry-container-sha }} ${BASE_ARG} -f ${{ env.dockerfile }} .
|
||||
docker 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 push ${{ env.docker-registry-container-sha }}
|
||||
docker push ${{ env.docker-registry-container-latest }}
|
||||
@ -85,6 +85,7 @@ jobs:
|
||||
matrix:
|
||||
platform:
|
||||
- name: "Linux (Xenial, GCC, OpenSSL)"
|
||||
id: xenial-gcc-openssl
|
||||
container:
|
||||
name: xenial
|
||||
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
|
||||
os: ubuntu-latest
|
||||
- name: Linux (Xenial, GCC, mbedTLS)
|
||||
id: xenial-gcc-mbedtls
|
||||
container:
|
||||
name: xenial
|
||||
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
|
||||
os: ubuntu-latest
|
||||
- name: "Linux (Xenial, Clang, OpenSSL)"
|
||||
id: xenial-clang-openssl
|
||||
container:
|
||||
name: xenial
|
||||
env:
|
||||
@ -109,6 +112,7 @@ jobs:
|
||||
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
|
||||
os: ubuntu-latest
|
||||
- name: "Linux (Xenial, Clang, mbedTLS)"
|
||||
id: xenial-clang-mbedtls
|
||||
container:
|
||||
name: xenial
|
||||
env:
|
||||
@ -117,6 +121,7 @@ jobs:
|
||||
CMAKE_GENERATOR: Ninja
|
||||
os: ubuntu-latest
|
||||
- name: "Linux (MemorySanitizer)"
|
||||
id: memorysanitizer
|
||||
container:
|
||||
name: focal
|
||||
env:
|
||||
@ -130,6 +135,7 @@ jobs:
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
os: ubuntu-latest
|
||||
- name: "Linux (UndefinedBehaviorSanitizer)"
|
||||
id: ubsanitizer
|
||||
container:
|
||||
name: focal
|
||||
env:
|
||||
@ -143,6 +149,7 @@ jobs:
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
os: ubuntu-latest
|
||||
- name: "Linux (ThreadSanitizer)"
|
||||
id: threadsanitizer
|
||||
container:
|
||||
name: focal
|
||||
env:
|
||||
@ -157,6 +164,7 @@ jobs:
|
||||
TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
|
||||
os: ubuntu-latest
|
||||
- name: "macOS"
|
||||
id: macos
|
||||
os: macos-10.15
|
||||
env:
|
||||
CC: clang
|
||||
@ -166,6 +174,7 @@ jobs:
|
||||
SKIP_NEGOTIATE_TESTS: true
|
||||
setup-script: osx
|
||||
- name: "Windows (amd64, Visual Studio)"
|
||||
id: windows-amd64-vs
|
||||
os: windows-2019
|
||||
env:
|
||||
ARCH: amd64
|
||||
@ -174,6 +183,7 @@ jobs:
|
||||
SKIP_SSH_TESTS: true
|
||||
SKIP_NEGOTIATE_TESTS: true
|
||||
- name: "Windows (x86, Visual Studio)"
|
||||
id: windows-x86-vs
|
||||
os: windows-2019
|
||||
env:
|
||||
ARCH: x86
|
||||
@ -182,6 +192,7 @@ jobs:
|
||||
SKIP_SSH_TESTS: true
|
||||
SKIP_NEGOTIATE_TESTS: true
|
||||
- name: "Windows (amd64, mingw)"
|
||||
id: windows-amd64-mingw
|
||||
os: windows-2019
|
||||
setup-script: mingw
|
||||
env:
|
||||
@ -193,6 +204,7 @@ jobs:
|
||||
SKIP_SSH_TESTS: true
|
||||
SKIP_NEGOTIATE_TESTS: true
|
||||
- name: "Windows (x86, mingw)"
|
||||
id: windows-x86-mingw
|
||||
os: windows-2019
|
||||
setup-script: mingw
|
||||
env:
|
||||
@ -214,7 +226,7 @@ jobs:
|
||||
path: source
|
||||
fetch-depth: 0
|
||||
- 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
|
||||
if: matrix.platform.setup-script != ''
|
||||
- name: Setup QEMU
|
||||
@ -229,7 +241,11 @@ jobs:
|
||||
working-directory: ${{ env.docker-config-path }}
|
||||
if: matrix.platform.container.name != ''
|
||||
- name: Create container
|
||||
run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ env.dockerfile }} .
|
||||
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 }}
|
||||
if: matrix.platform.container.name != '' && env.docker-container-exists != 'true'
|
||||
- name: Build and test
|
||||
@ -237,10 +253,12 @@ jobs:
|
||||
export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
|
||||
|
||||
if [ -n "${{ matrix.platform.container.name }}" ]; then
|
||||
mkdir build
|
||||
docker run \
|
||||
--rm \
|
||||
--user libgit2:libgit2 \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
-v "$(pwd)/source:/home/libgit2/source" \
|
||||
-v "$(pwd)/build:/home/libgit2/build" \
|
||||
-w /home/libgit2 \
|
||||
-e ASAN_SYMBOLIZER_PATH \
|
||||
-e CC \
|
||||
@ -248,19 +266,40 @@ jobs:
|
||||
-e CMAKE_GENERATOR \
|
||||
-e CMAKE_OPTIONS \
|
||||
-e GITTEST_NEGOTIATE_PASSWORD \
|
||||
-e GITTEST_FLAKY_STAT \
|
||||
-e PKG_CONFIG_PATH \
|
||||
-e SKIP_NEGOTIATE_TESTS \
|
||||
-e SKIP_SSH_TESTS \
|
||||
-e TSAN_OPTIONS \
|
||||
-e UBSAN_OPTIONS \
|
||||
${{ env.docker-registry-container-sha }} \
|
||||
/bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh"
|
||||
/bin/bash -c "cd build && ../source/ci/build.sh && ../source/ci/test.sh"
|
||||
else
|
||||
mkdir build && cd build
|
||||
mkdir build
|
||||
cd build
|
||||
../source/ci/build.sh
|
||||
../source/ci/test.sh
|
||||
fi
|
||||
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
|
||||
# as a build artifact so that it can be reviewed as part of a pull
|
||||
@ -270,6 +309,7 @@ jobs:
|
||||
documentation:
|
||||
name: Generate documentation
|
||||
needs: [ containers ]
|
||||
if: success() || failure()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
|
43
.github/workflows/nightly.yml
vendored
43
.github/workflows/nightly.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
- cron: '15 1 * * *'
|
||||
|
||||
env:
|
||||
docker-registry: docker.pkg.github.com
|
||||
docker-registry: ghcr.io
|
||||
docker-config-path: source/ci/docker
|
||||
|
||||
jobs:
|
||||
@ -15,6 +15,10 @@ jobs:
|
||||
# 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:
|
||||
@ -247,6 +251,7 @@ jobs:
|
||||
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
|
||||
RUN_INVASIVE_TESTS: true
|
||||
SKIP_PROXY_TESTS: true
|
||||
GITTEST_FLAKY_STAT: true
|
||||
os: ubuntu-latest
|
||||
- name: "Linux (arm64, Bionic, GCC, OpenSSL)"
|
||||
container:
|
||||
@ -271,7 +276,7 @@ jobs:
|
||||
path: source
|
||||
fetch-depth: 0
|
||||
- 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
|
||||
if: matrix.platform.setup-script != ''
|
||||
- name: Setup QEMU
|
||||
@ -305,6 +310,7 @@ jobs:
|
||||
-e CMAKE_GENERATOR \
|
||||
-e CMAKE_OPTIONS \
|
||||
-e GITTEST_NEGOTIATE_PASSWORD \
|
||||
-e GITTEST_FLAKY_STAT \
|
||||
-e PKG_CONFIG_PATH \
|
||||
-e SKIP_NEGOTIATE_TESTS \
|
||||
-e SKIP_SSH_TESTS \
|
||||
@ -319,6 +325,10 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
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
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -338,3 +348,32 @@ jobs:
|
||||
run: source/ci/coverity.sh
|
||||
env:
|
||||
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
|
||||
|
@ -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.
|
||||
#
|
||||
# This top-level CMakeLists.txt sets up configuration options and
|
||||
# determines which subprojects to build.
|
||||
|
||||
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
|
||||
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
|
||||
option(BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" 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_FUZZERS "Build the fuzz targets" OFF)
|
||||
|
||||
@ -25,7 +29,8 @@ option(USE_NSEC "Support nanosecond precision file mtimes and cti
|
||||
# Backend selection
|
||||
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_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)
|
||||
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.")
|
||||
@ -127,6 +132,14 @@ if(BUILD_FUZZERS)
|
||||
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
|
||||
|
||||
feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
|
||||
|
40
COPYING
40
COPYING
@ -1144,3 +1144,43 @@ worldwide. This software is distributed without any warranty.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -392,6 +392,8 @@ Here are the bindings to libgit2 that are currently available:
|
||||
* parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
|
||||
* Perl
|
||||
* Git-Raw <https://github.com/jacquesg/p5-Git-Raw>
|
||||
* Pharo Smalltalk
|
||||
* libgit2-pharo-bindings <https://github.com/pharo-vcs/libgit2-pharo-bindings>
|
||||
* PHP
|
||||
* php-git <https://github.com/libgit2/php-git>
|
||||
* Python
|
||||
@ -405,6 +407,8 @@ Here are the bindings to libgit2 that are currently available:
|
||||
* git2-rs <https://github.com/rust-lang/git2-rs>
|
||||
* Swift
|
||||
* SwiftGit2 <https://github.com/SwiftGit2/SwiftGit2>
|
||||
* Tcl
|
||||
* lg2 <https://github.com/apnadkarni/tcl-libgit2>
|
||||
* Vala
|
||||
* libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
|
||||
|
||||
|
@ -59,7 +59,7 @@ echo "##########################################################################
|
||||
echo "## Configuring build environment"
|
||||
echo "##############################################################################"
|
||||
|
||||
echo cmake -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\"
|
||||
echo "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\"
|
||||
env PATH="${BUILD_PATH}" "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G "${CMAKE_GENERATOR}" ${CMAKE_OPTIONS} -S "${SOURCE_DIR}"
|
||||
|
||||
echo ""
|
||||
@ -69,10 +69,11 @@ echo "##########################################################################
|
||||
|
||||
# Determine parallelism; newer cmake supports `--build --parallel` but
|
||||
# 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})
|
||||
else
|
||||
BUILDER=("${CMAKE}" --build .)
|
||||
BUILDER=("${CMAKE}" --build . ${CMAKE_BUILD_OPTIONS})
|
||||
fi
|
||||
|
||||
echo "${BUILDER[@]}"
|
||||
env PATH="${BUILD_PATH}" "${BUILDER[@]}"
|
||||
|
@ -28,17 +28,22 @@ RUN apt-get update && \
|
||||
|
||||
FROM apt AS mbedtls
|
||||
RUN cd /tmp && \
|
||||
curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
|
||||
tar -xz && \
|
||||
cd mbedtls-2.16.2 && \
|
||||
curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
|
||||
tar -xz && \
|
||||
cd mbedtls-mbedtls-2.16.2 && \
|
||||
scripts/config.pl set MBEDTLS_MD4_C 1 && \
|
||||
CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
|
||||
ninja install && \
|
||||
cd .. && \
|
||||
rm -rf mbedtls-2.16.2
|
||||
rm -rf mbedtls-mbedtls-2.16.2
|
||||
|
||||
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
|
||||
RUN mkdir /var/run/sshd
|
||||
|
@ -48,7 +48,12 @@ RUN cd /tmp && \
|
||||
rm -rf cmake-3.21.1
|
||||
|
||||
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
|
||||
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
|
||||
|
@ -1,6 +1,10 @@
|
||||
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 \
|
||||
which \
|
||||
bzip2 \
|
||||
@ -40,7 +44,12 @@ RUN cd /tmp && \
|
||||
rm -rf valgrind-3.15.0
|
||||
|
||||
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
|
||||
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
|
||||
|
@ -32,9 +32,9 @@ RUN apt-get update && \
|
||||
|
||||
FROM apt AS mbedtls
|
||||
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 && \
|
||||
cd mbedtls-2.16.2 && \
|
||||
cd mbedtls-mbedtls-2.16.2 && \
|
||||
scripts/config.pl unset MBEDTLS_AESNI_C && \
|
||||
scripts/config.pl set MBEDTLS_MD4_C 1 && \
|
||||
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 .. && \
|
||||
ninja install && \
|
||||
cd .. && \
|
||||
rm -rf mbedtls-2.16.2
|
||||
rm -rf mbedtls-mbedtls-2.16.2
|
||||
|
||||
FROM mbedtls AS libssh2
|
||||
RUN cd /tmp && \
|
||||
@ -73,7 +73,13 @@ RUN cd /tmp && \
|
||||
rm -rf valgrind-3.15.0
|
||||
|
||||
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
|
||||
RUN mkdir /var/run/sshd
|
||||
|
@ -30,14 +30,14 @@ RUN apt-get update && \
|
||||
|
||||
FROM apt AS mbedtls
|
||||
RUN cd /tmp && \
|
||||
curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
|
||||
tar -xz && \
|
||||
cd mbedtls-2.16.2 && \
|
||||
curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
|
||||
tar -xz && \
|
||||
cd mbedtls-mbedtls-2.16.2 && \
|
||||
scripts/config.pl set MBEDTLS_MD4_C 1 && \
|
||||
CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
|
||||
ninja install && \
|
||||
cd .. && \
|
||||
rm -rf mbedtls-2.16.2
|
||||
rm -rf mbedtls-mbedtls-2.16.2
|
||||
|
||||
FROM mbedtls AS libssh2
|
||||
RUN cd /tmp && \
|
||||
@ -60,7 +60,13 @@ RUN cd /tmp && \
|
||||
rm -rf valgrind-3.15.0
|
||||
|
||||
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
|
||||
RUN mkdir /var/run/sshd
|
||||
|
@ -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-latest=${DOCKER_REGISTRY_CONTAINER}:latest" >> $GITHUB_ENV
|
||||
|
||||
echo "::: logging in to ${DOCKER_REGISTRY} as ${GITHUB_ACTOR}"
|
||||
|
||||
exists="true"
|
||||
docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false"
|
||||
|
||||
echo "::: pulling ${DOCKER_REGISTRY_CONTAINER_SHA}"
|
||||
|
||||
if [ "${exists}" != "false" ]; then
|
||||
docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false"
|
||||
fi
|
||||
|
@ -1,4 +1,6 @@
|
||||
#!/bin/sh -e
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
echo "##############################################################################"
|
||||
echo "## Downloading mingw"
|
6
ci/setup-osx-benchmark.sh
Executable file
6
ci/setup-osx-benchmark.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
brew update
|
||||
brew install hyperfine
|
@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
set -ex
|
||||
|
||||
brew update
|
||||
brew install pkgconfig zlib curl openssl libssh2 ninja
|
20
ci/setup-ubuntu-benchmark.sh
Executable file
20
ci/setup-ubuntu-benchmark.sh
Executable 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
9
ci/setup-win32-benchmark.sh
Executable 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}")
|
18
ci/test.sh
18
ci/test.sh
@ -159,10 +159,18 @@ fi
|
||||
if [ -z "$SKIP_OFFLINE_TESTS" ]; then
|
||||
echo ""
|
||||
echo "##############################################################################"
|
||||
echo "## Running (offline) tests"
|
||||
echo "## Running core tests"
|
||||
echo "##############################################################################"
|
||||
|
||||
echo ""
|
||||
echo "Running libgit2 integration (offline) tests"
|
||||
echo ""
|
||||
run_test offline
|
||||
|
||||
echo ""
|
||||
echo "Running utility tests"
|
||||
echo ""
|
||||
run_test util
|
||||
fi
|
||||
|
||||
if [ -n "$RUN_INVASIVE_TESTS" ]; then
|
||||
@ -186,7 +194,7 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
|
||||
|
||||
echo ""
|
||||
echo "##############################################################################"
|
||||
echo "## Running (online) tests"
|
||||
echo "## Running networking (online) tests"
|
||||
echo "##############################################################################"
|
||||
|
||||
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
|
||||
# to avoid polluting the test environment.
|
||||
echo ""
|
||||
echo "##############################################################################"
|
||||
echo "## Running (online_customcert) tests"
|
||||
echo "##############################################################################"
|
||||
echo "Running custom certificate (online_customcert) tests"
|
||||
echo ""
|
||||
|
||||
run_test online_customcert
|
||||
fi
|
||||
|
||||
|
7
cmake/AddClarTest.cmake
Normal file
7
cmake/AddClarTest.cmake
Normal 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)
|
@ -16,19 +16,18 @@
|
||||
# PCRE_FOUND - True if pcre found.
|
||||
|
||||
# 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.
|
||||
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.
|
||||
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.
|
||||
if(PCRE_FOUND)
|
||||
set(PCRE_LIBRARIES ${PCRE_LIBRARY} ${PCRE_POSIX_LIBRARY})
|
||||
set(PCRE_LIBRARIES ${PCRE_LIBRARY})
|
||||
set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
|
||||
else(PCRE_FOUND)
|
||||
set(PCRE_LIBRARIES)
|
||||
|
@ -16,7 +16,7 @@
|
||||
# PCRE2_FOUND - True if pcre found.
|
||||
|
||||
# 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.
|
||||
find_library(PCRE2_LIBRARY NAMES pcre2-8)
|
||||
|
@ -64,7 +64,7 @@ if(USE_HTTPS)
|
||||
|
||||
if(NOT CERT_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
|
||||
set(OPENSSL_CMD "/usr/local/opt/openssl/bin/openssl")
|
||||
else()
|
||||
|
@ -4,6 +4,9 @@ include(SanitizeBool)
|
||||
|
||||
# USE_SHA1=CollisionDetection(ON)/HTTPS/Generic/OFF
|
||||
sanitizebool(USE_SHA1)
|
||||
sanitizebool(USE_SHA256)
|
||||
|
||||
# sha1
|
||||
|
||||
if(USE_SHA1 STREQUAL ON)
|
||||
SET(USE_SHA1 "CollisionDetection")
|
||||
@ -21,32 +24,77 @@ endif()
|
||||
|
||||
if(USE_SHA1 STREQUAL "CollisionDetection")
|
||||
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")
|
||||
# OPENSSL_FOUND should already be set, we're checking USE_HTTPS
|
||||
|
||||
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")
|
||||
list(APPEND LIBGIT2_PC_LIBS "-lssl")
|
||||
else()
|
||||
list(APPEND LIBGIT2_PC_REQUIRES "openssl")
|
||||
endif()
|
||||
elseif(USE_SHA1 STREQUAL "CommonCrypto")
|
||||
set(GIT_SHA1_COMMON_CRYPTO 1)
|
||||
elseif(USE_SHA1 STREQUAL "mbedTLS")
|
||||
set(GIT_SHA1_MBEDTLS 1)
|
||||
endif()
|
||||
|
||||
if(USE_SHA1 STREQUAL "mbedTLS" OR USE_SHA256 STREQUAL "mbedTLS")
|
||||
list(APPEND LIBGIT2_SYSTEM_INCLUDES ${MBEDTLS_INCLUDE_DIR})
|
||||
list(APPEND LIBGIT2_SYSTEM_LIBS ${MBEDTLS_LIBRARIES})
|
||||
# mbedTLS has no pkgconfig file, hence we can't require it
|
||||
# https://github.com/ARMmbed/mbedtls/issues/228
|
||||
# For now, pass its link flags as our own
|
||||
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()
|
||||
|
||||
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}")
|
||||
|
@ -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/).
|
||||
|
||||
**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:
|
||||
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.
|
||||
|
||||
## 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
|
||||
* 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:
|
||||
|
||||
* 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
|
||||
**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.0...v1.5.0
|
||||
|
||||
v1.4
|
||||
----
|
||||
|
@ -1,3 +1,5 @@
|
||||
# examples: code usage examples of libgit2
|
||||
|
||||
file(GLOB SRC_EXAMPLES *.c *.h)
|
||||
|
||||
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})
|
||||
|
||||
if(WIN32 OR ANDROID)
|
||||
target_link_libraries(lg2 git2)
|
||||
target_link_libraries(lg2 libgit2package)
|
||||
else()
|
||||
target_link_libraries(lg2 git2 pthread)
|
||||
target_link_libraries(lg2 libgit2package pthread)
|
||||
endif()
|
||||
|
@ -1,3 +1,5 @@
|
||||
# fuzzers: libFuzzer and standalone fuzzing utilities
|
||||
|
||||
if(BUILD_FUZZERS AND NOT USE_STANDALONE_FUZZERS)
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer-no-link")
|
||||
add_c_flag(-fsanitize=fuzzer)
|
||||
|
@ -94,7 +94,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
fprintf(stderr, "Failed to compute the SHA1 hash\n");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -129,8 +129,8 @@ GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter);
|
||||
* See `git_tag_create()` for rules about valid names.
|
||||
*
|
||||
* Note that if the move succeeds, the old reference object will not
|
||||
+ be valid anymore, and should be freed immediately by the user using
|
||||
+ `git_reference_free()`.
|
||||
* be valid anymore, and should be freed immediately by the user using
|
||||
* `git_reference_free()`.
|
||||
*
|
||||
* @param out New reference object for the updated name.
|
||||
*
|
||||
|
@ -121,6 +121,17 @@ GIT_BEGIN_DECL
|
||||
*/
|
||||
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
|
||||
* was compiled
|
||||
|
@ -122,7 +122,7 @@ typedef struct {
|
||||
* global configuration file.
|
||||
*
|
||||
* 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
|
||||
* @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
|
||||
*
|
||||
* If /etc/gitconfig doesn't exist, it will look for
|
||||
* %PROGRAMFILES%\Git\etc\gitconfig.
|
||||
* If `/etc/gitconfig` doesn't exist, it will look for
|
||||
* `%PROGRAMFILES%\Git\etc\gitconfig`.
|
||||
*
|
||||
* @param out Pointer to a user-allocated git_buf in which to store the path
|
||||
* @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
|
||||
*
|
||||
* 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
|
||||
* @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
|
||||
*
|
||||
* The pointers returned by this function are valid until the iterator
|
||||
* is freed.
|
||||
* The pointers returned by this function are valid until the next call
|
||||
* to `git_config_next` or until the iterator is freed.
|
||||
*
|
||||
* @param entry pointer to store the entry
|
||||
* @param iter the iterator
|
||||
|
@ -436,6 +436,8 @@ GIT_EXTERN(int) git_diff_format_email_options_init(
|
||||
#define GITERR_WORKTREE GIT_ERROR_WORKTREE
|
||||
#define GITERR_SHA1 GIT_ERROR_SHA1
|
||||
|
||||
#define GIT_ERROR_SHA1 GIT_ERROR_SHA
|
||||
|
||||
/**
|
||||
* Return the last `git_error` object that was generated for the
|
||||
* current thread. This is an alias of `git_error_last` and is
|
||||
|
@ -107,7 +107,7 @@ typedef enum {
|
||||
GIT_ERROR_FILESYSTEM,
|
||||
GIT_ERROR_PATCH,
|
||||
GIT_ERROR_WORKTREE,
|
||||
GIT_ERROR_SHA1,
|
||||
GIT_ERROR_SHA,
|
||||
GIT_ERROR_HTTP,
|
||||
GIT_ERROR_INTERNAL
|
||||
} git_error_t;
|
||||
@ -131,7 +131,8 @@ GIT_EXTERN(const git_error *) git_error_last(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
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
GIT_EXTERN(int) git_error_set_str(int error_class, const char *string);
|
||||
|
@ -603,7 +603,7 @@ GIT_EXTERN(int) git_merge_commits(
|
||||
* completes, resolve any conflicts and prepare a commit.
|
||||
*
|
||||
* 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
|
||||
* `git_repository_state_cleanup()`.
|
||||
*
|
||||
|
@ -227,13 +227,16 @@ typedef struct {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
#ifndef INCLUDE_sys_git_remote_h
|
||||
#define INCLUDE_sys_git_remote_h
|
||||
|
||||
#include "git2/remote.h"
|
||||
|
||||
/**
|
||||
* @file git2/sys/remote.h
|
||||
* @brief Low-level remote functionality for custom transports
|
||||
@ -26,6 +28,19 @@ typedef enum {
|
||||
GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1),
|
||||
} 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
|
||||
#endif
|
||||
|
@ -9,10 +9,11 @@
|
||||
#define INCLUDE_sys_git_transport_h
|
||||
|
||||
#include "git2/net.h"
|
||||
#include "git2/proxy.h"
|
||||
#include "git2/remote.h"
|
||||
#include "git2/strarray.h"
|
||||
#include "git2/transport.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/strarray.h"
|
||||
#include "git2/proxy.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);
|
||||
|
||||
/**
|
||||
* 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 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 ***
|
||||
|
@ -7,12 +7,33 @@
|
||||
#ifndef INCLUDE_git_version_h__
|
||||
#define INCLUDE_git_version_h__
|
||||
|
||||
#define LIBGIT2_VERSION "1.4.3"
|
||||
#define LIBGIT2_VER_MAJOR 1
|
||||
#define LIBGIT2_VER_MINOR 4
|
||||
#define LIBGIT2_VER_REVISION 3
|
||||
#define LIBGIT2_VER_PATCH 0
|
||||
/**
|
||||
* The version string for libgit2. This string follows semantic
|
||||
* versioning (v2) guidelines.
|
||||
*/
|
||||
#define LIBGIT2_VERSION "1.5.0"
|
||||
|
||||
#define LIBGIT2_SOVERSION "1.4"
|
||||
/** The major version number for this version of libgit2. */
|
||||
#define LIBGIT2_VER_MAJOR 1
|
||||
|
||||
/** 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
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "libgit2",
|
||||
"version": "1.4.3",
|
||||
"version": "1.5.0",
|
||||
"repo": "https://github.com/libgit2/libgit2",
|
||||
"description": " A cross-platform, linkable library implementation of Git that you can use in your application.",
|
||||
"install": "mkdir build && cd build && cmake .. && cmake --build ."
|
||||
|
@ -1,12 +1,22 @@
|
||||
add_library(git2internal OBJECT)
|
||||
set_target_properties(git2internal PROPERTIES C_STANDARD 90)
|
||||
set_target_properties(git2internal PROPERTIES C_EXTENSIONS OFF)
|
||||
# The main libgit2 source tree: this CMakeLists.txt identifies platform
|
||||
# support and includes the subprojects that make up core libgit2 support.
|
||||
|
||||
#
|
||||
# Optional build configuration settings
|
||||
#
|
||||
|
||||
if(DEPRECATE_HARD)
|
||||
add_definitions(-DGIT_DEPRECATE_HARD)
|
||||
endif()
|
||||
|
||||
if(USE_LEAK_CHECKER STREQUAL "valgrind")
|
||||
add_definitions(-DVALGRIND)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Optional debugging functionality
|
||||
#
|
||||
|
||||
if(DEBUG_POOL)
|
||||
set(GIT_DEBUG_POOL 1)
|
||||
endif()
|
||||
@ -22,29 +32,32 @@ if(DEBUG_STRICT_OPEN)
|
||||
endif()
|
||||
add_feature_info(debugopen GIT_DEBUG_STRICT_OPEN "path validation in open")
|
||||
|
||||
#
|
||||
# Optional feature enablement
|
||||
#
|
||||
|
||||
include(PkgBuildConfig)
|
||||
include(SanitizeBool)
|
||||
include(SelectGSSAPI)
|
||||
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
|
||||
# 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 "")
|
||||
#
|
||||
# Platform support
|
||||
#
|
||||
|
||||
set(LIBGIT2_INCLUDES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${PROJECT_SOURCE_DIR}/src"
|
||||
"${PROJECT_SOURCE_DIR}/include")
|
||||
# futimes/futimens
|
||||
|
||||
if(HAVE_FUTIMENS)
|
||||
set(GIT_USE_FUTIMENS 1)
|
||||
endif ()
|
||||
add_feature_info(futimens GIT_USE_FUTIMENS "futimens support")
|
||||
|
||||
# qsort
|
||||
|
||||
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 *))"
|
||||
"" "stdlib.h" GIT_QSORT_R_BSD)
|
||||
@ -55,69 +68,85 @@ check_prototype_definition(qsort_r
|
||||
|
||||
check_function_exists(qsort_s GIT_QSORT_S)
|
||||
|
||||
# random / entropy data
|
||||
|
||||
check_function_exists(getentropy GIT_RAND_GETENTROPY)
|
||||
check_function_exists(getloadavg GIT_RAND_GETLOADAVG)
|
||||
|
||||
# Find required dependencies
|
||||
# determine architecture of the machine
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
|
||||
list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl)
|
||||
list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku")
|
||||
list(APPEND LIBGIT2_SYSTEM_LIBS network)
|
||||
list(APPEND LIBGIT2_PC_LIBS "-lnetwork")
|
||||
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()
|
||||
|
||||
# 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)
|
||||
if(NEED_LIBRT)
|
||||
list(APPEND LIBGIT2_SYSTEM_LIBS rt)
|
||||
list(APPEND LIBGIT2_PC_LIBS "-lrt")
|
||||
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)
|
||||
list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
if(NOT WIN32)
|
||||
find_package(Threads REQUIRED)
|
||||
list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
set(GIT_THREADS 1)
|
||||
endif()
|
||||
add_feature_info(threadsafe USE_THREADS "threadsafe support")
|
||||
|
||||
#
|
||||
# Optional bundled features
|
||||
#
|
||||
|
||||
if(WIN32 AND EMBED_SSH_PATH)
|
||||
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
|
||||
# ntlmclient
|
||||
if(USE_NTLMCLIENT)
|
||||
set(GIT_NTLM 1)
|
||||
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/ntlmclient" "${PROJECT_BINARY_DIR}/deps/ntlmclient")
|
||||
@ -126,11 +155,10 @@ if(USE_NTLMCLIENT)
|
||||
endif()
|
||||
add_feature_info(ntlmclient GIT_NTLM "NTLM authentication support for Unix")
|
||||
|
||||
# Optional external dependency: GSSAPI
|
||||
#
|
||||
# Optional external dependencies
|
||||
|
||||
include(SelectGSSAPI)
|
||||
|
||||
# Optional external dependency: iconv
|
||||
# iconv
|
||||
if(USE_ICONV)
|
||||
find_package(Iconv)
|
||||
endif()
|
||||
@ -142,166 +170,28 @@ if(ICONV_FOUND)
|
||||
endif()
|
||||
add_feature_info(iconv GIT_USE_ICONV "iconv encoding conversion support")
|
||||
|
||||
|
||||
if(USE_THREADS)
|
||||
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 support
|
||||
#
|
||||
|
||||
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)
|
||||
target_include_directories(git2internal SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
|
||||
add_subdirectory(libgit2)
|
||||
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_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)
|
||||
|
||||
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
12
src/README.md
Normal 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
53
src/cli/CMakeLists.txt
Normal 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
26
src/cli/README.md
Normal 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>`.
|
||||
|
@ -5,15 +5,16 @@
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_hash_sha1_generic_h__
|
||||
#define INCLUDE_hash_sha1_generic_h__
|
||||
#ifndef CLI_cli_h__
|
||||
#define CLI_cli_h__
|
||||
|
||||
#include "hash/sha1.h"
|
||||
#define PROGRAM_NAME "git2"
|
||||
|
||||
struct git_hash_sha1_ctx {
|
||||
uint64_t size;
|
||||
unsigned int H[5];
|
||||
unsigned int W[16];
|
||||
};
|
||||
#include "git2_util.h"
|
||||
|
||||
#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
21
src/cli/cmd.c
Normal 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
33
src/cli/cmd.h
Normal 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
204
src/cli/cmd_cat_file.c
Normal 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
176
src/cli/cmd_clone.c
Normal 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
135
src/cli/cmd_hash_object.c
Normal 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
86
src/cli/cmd_help.c
Normal 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
51
src/cli/error.h
Normal 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
106
src/cli/main.c
Normal 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
669
src/cli/opt.c
Normal 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
349
src/cli/opt.h
Normal 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
194
src/cli/opt_usage.c
Normal 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
35
src/cli/opt_usage.h
Normal 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
345
src/cli/progress.c
Normal 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
117
src/cli/progress.h
Normal 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
20
src/cli/sighandler.h
Normal 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
36
src/cli/unix/sighandler.c
Normal 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;
|
||||
}
|
3
src/cli/win32/precompiled.h
Normal file
3
src/cli/win32/precompiled.h
Normal file
@ -0,0 +1,3 @@
|
||||
#include <git2.h>
|
||||
|
||||
#include "cli.h"
|
37
src/cli/win32/sighandler.c
Normal file
37
src/cli/win32/sighandler.c
Normal 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;
|
||||
}
|
@ -46,8 +46,17 @@
|
||||
#cmakedefine GIT_SHA1_WIN32 1
|
||||
#cmakedefine GIT_SHA1_COMMON_CRYPTO 1
|
||||
#cmakedefine GIT_SHA1_OPENSSL 1
|
||||
#cmakedefine GIT_SHA1_OPENSSL_DYNAMIC 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_GETLOADAVG 1
|
||||
|
||||
#endif
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
131
src/libgit2/CMakeLists.txt
Normal 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})
|
@ -101,7 +101,7 @@ static int write_file_stream(
|
||||
git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size)
|
||||
{
|
||||
int fd, error;
|
||||
char buffer[FILEIO_BUFSIZE];
|
||||
char buffer[GIT_BUFSIZE_FILEIO];
|
||||
git_odb_stream *stream = NULL;
|
||||
ssize_t read_len = -1;
|
||||
git_object_size_t written = 0;
|
@ -53,6 +53,17 @@ static int not_a_local_branch(const char *reference_name)
|
||||
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(
|
||||
git_reference **ref_out,
|
||||
git_repository *repository,
|
||||
@ -73,8 +84,8 @@ static int create_branch(
|
||||
GIT_ASSERT_ARG(ref_out);
|
||||
GIT_ASSERT_ARG(git_commit_owner(commit) == repository);
|
||||
|
||||
if (!git__strcmp(branch_name, "HEAD")) {
|
||||
git_error_set(GIT_ERROR_REFERENCE, "'HEAD' is not a valid branch name");
|
||||
if (!branch_name_is_valid(branch_name)) {
|
||||
git_error_set(GIT_ERROR_REFERENCE, "'%s' is not a valid branch name", branch_name);
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
@ -797,13 +808,7 @@ int git_branch_name_is_valid(int *valid, const char *name)
|
||||
|
||||
*valid = 0;
|
||||
|
||||
/*
|
||||
* Discourage branch name starting with dash,
|
||||
* https://github.com/git/git/commit/6348624010888b
|
||||
* and discourage HEAD as branch name,
|
||||
* https://github.com/git/git/commit/a625b092cc5994
|
||||
*/
|
||||
if (!name || name[0] == '-' || !git__strcmp(name, "HEAD"))
|
||||
if (!name || !branch_name_is_valid(name))
|
||||
goto done;
|
||||
|
||||
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
Loading…
Reference in New Issue
Block a user