Update upstream source from tag 'upstream/0.27.7+dfsg.1'

Update to upstream version '0.27.7+dfsg.1'
with Debian dir 3a55b6017d
This commit is contained in:
Raju Devidas 2018-12-27 01:36:35 +05:30
commit 7838d95547
80 changed files with 2396 additions and 723 deletions

View File

@ -1,92 +0,0 @@
# Travis-CI Build for libgit2
# see travis-ci.org for details
language: c
os:
- linux
- osx
compiler:
- gcc
- clang
# Settings to try
env:
global:
- secure: "YnhS+8n6B+uoyaYfaJ3Lei7cSJqHDPiKJCKFIF2c87YDfmCvAJke8QtE7IzjYDs7UFkTCM4ox+ph2bERUrxZbSCyEkHdjIZpKuMJfYWja/jgMqTMxdyOH9y8JLFbZsSXDIXDwqBlC6vVyl1fP90M35wuWcNTs6tctfVWVofEFbs="
- GITTEST_INVASIVE_FS_SIZE=1
matrix:
- OPTIONS="-DTHREADSAFE=ON -DENABLE_TRACE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_WERROR=ON"
- OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON -DENABLE_WERROR=ON"
dist: trusty
osx_image: xcode8.3
sudo: false
addons:
apt:
sources:
- sourceline: 'deb https://dl.bintray.com/libgit2/ci-dependencies trusty libgit2deps'
key_url: 'https://bintray.com/user/downloadSubjectPublicKey?username=bintray'
packages:
cmake
curl
libcurl3
libcurl3-gnutls
libcurl4-gnutls-dev
libssh2-1-dev
openssh-client
openssh-server
valgrind
matrix:
fast_finish: true
exclude:
- os: osx
compiler: gcc
include:
- compiler: gcc
env: COVERITY=1
os: linux
dist: trusty
- compiler: gcc
env:
- VALGRIND=1
OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
os: linux
dist: trusty
allow_failures:
- env: COVERITY=1
install:
- if [ -f ./script/install-deps-${TRAVIS_OS_NAME}.sh ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
# Run the Build script and tests
script:
- script/cibuild.sh
# Run Tests
after_success:
- if [ "$TRAVIS_OS_NAME" = "linux" -a -n "$VALGRIND" ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline; fi
# Only watch the development and master branches
branches:
only:
- master
- /^maint.*/
# Notify development list when needed
notifications:
irc:
channels:
- irc.freenode.net#libgit2
on_success: change
on_failure: always
use_notice: true
skip_join: true
campfire:
on_success: always
on_failure: always
rooms:
- secure: "sH0dpPWMirbEe7AvLddZ2yOp8rzHalGmv0bYL/LIhVw3JDI589HCYckeLMSB\n3e/FeXw4bn0EqXWEXijVa4ijbilVY6d8oprdqMdWHEodng4KvY5vID3iZSGT\nxylhahO1XHmRynKQLOAvxlc93IlpVW38vQfby8giIY1nkpspb2w="

View File

@ -1,3 +1,143 @@
v0.27.7
-------
This is a bugfix release with the following changes or improvements:
- Our continuous integration environment has switched from Travis and
AppVeyor to Azure Pipelines CI.
- Fix adding worktrees for bare repositories.
- Fix parsed patches not computing the old respectively new line
numbers correctly.
- Fix parsing configuration variables which do not have a section.
- Fix a zero-byte allocation when trying to detect file renames and
copies of a diff without any hunks.
- Fix a zero-byte allocation when trying to resize or duplicate
vectors.
- Fix return value when trying to unlock worktrees which aren't
locked.
- Fix returning an unitialized error code when preparing a revision
walk without any pushed commits.
- Fix return value of `git_remote_lookup` when lookup of
"remote.$remote.tagopt" fails.
- Fix the revision walk always labelling commits as interesting due
to a mishandling of the commit date.
- Fix the packbuilder inserting uninteresting blobs when adding a
tree containing references to such blobs.
- Ignore unsupported authentication schemes in HTTP transport.
- Improve performane of `git_remote_prune`.
- Fix detection of whether `qsort_r` has a BSD or GNU function
signature.
- Fix detection of iconv if it is provided by libc.
v0.27.6
-------
This as a security release fixing the following list of issues:
- The function family `git__strtol` is used to parse integers
from a buffer. As the functions do not take a buffer length as
argument, they will scan either until the end of the current
number or until a NUL byte is encountered. Many callers have
been misusing the function and called it on potentially
non-NUL-terminated buffers, resulting in possible out-of-bounds
reads. Callers have been fixed to use `git__strntol` functions
instead and `git__strtol` functions were removed.
- The function `git__strntol64` relied on the undefined behavior
of signed integer overflows. While the code tried to detect
such overflows after they have happened, this is unspecified
behavior and may lead to weird behavior on uncommon platforms.
- In the case where `git__strntol32` was unable to parse an
integer because it doesn't fit into an `int32_t`, it printed an
error message containing the string that is currently being
parsed. The code didn't truncate the string though, which
caused it to print the complete string until a NUL byte is
encountered and not only the currently parsed number. In case
where the string was not NUL terminated, this could have lead
to an out-of-bounds read.
- When parsing tags, all unknown fields that appear before the
tag message are skipped. This skipping is done by using a plain
`strstr(buffer, "\n\n")` to search for the two newlines that
separate tag fields from tag message. As it is not possible to
supply a buffer length to `strstr`, this call may skip over the
buffer's end and thus result in an out of bounds read. As
`strstr` may return a pointer that is out of bounds, the
following computation of `buffer_end - buffer` will overflow
and result in an allocation of an invalid length. Note that
when reading objects from the object database, we make sure to
always NUL terminate them, making the use of `strstr` safe.
- When parsing the "encoding" field of a commit, we may perform
an out of bounds read due to using `git__prefixcmp` instead of
`git__prefixncmp`. This can result in the parsed commit object
containing uninitialized data in both its message encoding and
message fields. Note that when reading objects from the object
database, we make sure to always NUL terminate them, making the
use of `strstr` safe.
v0.27.5
-------
This is a security release fixing the following list of issues:
- Submodule URLs and paths with a leading "-" are now ignored.
This is due to the recently discovered CVE-2018-17456, which
can lead to arbitrary code execution in upstream git. While
libgit2 itself is not vulnerable, it can be used to inject
options in an implementation which performs a recursive clone
by executing an external command.
- When running repack while doing repo writes,
`packfile_load__cb()` could see some temporary files in the
directory that were bigger than the usual, and makes `memcmp`
overflow on the `p->pack_name` string. This issue was reported
and fixed by bisho.
- The configuration file parser used unbounded recursion to parse
multiline variables, which could lead to a stack overflow. The
issue was reported by the oss-fuzz project, issue 10048 and
fixed by Nelson Elhage.
- The fix to the unbounded recursion introduced a memory leak in
the config parser. While this leak was never in a public
release, the oss-fuzz project reported this as issue 10127. The
fix was implemented by Nelson Elhage and Patrick Steinhardt.
- When parsing "ok" packets received via the smart protocol, our
parsing code did not correctly verify the bounds of the
packets, which could result in a heap-buffer overflow. The
issue was reported by the oss-fuzz project, issue 9749 and
fixed by Patrick Steinhardt.
- The parsing code for the smart protocol has been tightened in
general, fixing heap-buffer overflows when parsing the packet
type as well as for "ACK" and "unpack" packets. The issue was
discovered and fixed by Patrick Steinhardt.
- Fixed potential integer overflows on platforms with 16 bit
integers when parsing packets for the smart protocol. The issue
was discovered and fixed by Patrick Steinhardt.
- Fixed potential NULL pointer dereference when parsing
configuration files which have "include.path" or
"includeIf..path" statements without a value.
v0.27.4 v0.27.4
------- -------

View File

@ -28,6 +28,7 @@ INCLUDE(CheckLibraryExists)
INCLUDE(CheckFunctionExists) INCLUDE(CheckFunctionExists)
INCLUDE(CheckSymbolExists) INCLUDE(CheckSymbolExists)
INCLUDE(CheckStructHasMember) INCLUDE(CheckStructHasMember)
INCLUDE(CheckPrototypeDefinition) # Added in CMake 3.0
INCLUDE(AddCFlagIfSupported) INCLUDE(AddCFlagIfSupported)
INCLUDE(FindPkgLibraries) INCLUDE(FindPkgLibraries)
INCLUDE(FindThreads) INCLUDE(FindThreads)
@ -224,6 +225,9 @@ ELSE ()
ENABLE_WARNINGS(shift-count-overflow) ENABLE_WARNINGS(shift-count-overflow)
DISABLE_WARNINGS(unused-const-variable) DISABLE_WARNINGS(unused-const-variable)
DISABLE_WARNINGS(unused-function) DISABLE_WARNINGS(unused-function)
ENABLE_WARNINGS(format)
ENABLE_WARNINGS(format-security)
ENABLE_WARNINGS(int-conversion)
IF (APPLE) # Apple deprecated OpenSSL IF (APPLE) # Apple deprecated OpenSSL
DISABLE_WARNINGS(deprecated-declarations) DISABLE_WARNINGS(deprecated-declarations)

View File

@ -1,8 +1,7 @@
libgit2 - the Git linkable library libgit2 - the Git linkable library
================================== ==================================
[![Travis Build Status](https://secure.travis-ci.org/libgit2/libgit2.svg?branch=master)](http://travis-ci.org/libgit2/libgit2) [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/xvof5b4t5480a2q3/branch/master?svg=true)](https://ci.appveyor.com/project/libgit2/libgit2/branch/master)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639) [![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639)
`libgit2` is a portable, pure C implementation of the Git core methods `libgit2` is a portable, pure C implementation of the Git core methods
@ -18,7 +17,7 @@ in your favorite language.
[GitKraken](https://gitkraken.com/) and [gmaster](https://gmaster.io/) [GitKraken](https://gitkraken.com/) and [gmaster](https://gmaster.io/)
and on Git hosting providers like [GitHub](https://github.com/), and on Git hosting providers like [GitHub](https://github.com/),
[GitLab](https://gitlab.com/) and [GitLab](https://gitlab.com/) and
[Visual Studio Team Services](https://visualstudio.com/team-services/). [Azure DevOps](https://azure.com/devops).
We perform the merge every time you click "merge pull request". We perform the merge every time you click "merge pull request".
`libgit2` is licensed under a **very permissive license** (GPLv2 with a special `libgit2` is licensed under a **very permissive license** (GPLv2 with a special
@ -87,7 +86,7 @@ What It Can Do
libgit2 provides you with the ability to manage Git repositories in the libgit2 provides you with the ability to manage Git repositories in the
programming language of your choice. It's used in production to power many programming language of your choice. It's used in production to power many
applications including GitHub.com, Plastic SCM and Visual Studio Team Services. applications including GitHub.com, Plastic SCM and Azure DevOps.
It does not aim to replace the git tool or its user-facing commands. Some APIs It does not aim to replace the git tool or its user-facing commands. Some APIs
resemble the plumbing commands as those align closely with the concepts of the resemble the plumbing commands as those align closely with the concepts of the

View File

@ -1,60 +0,0 @@
version: '{build}'
branches:
only:
- master
- appveyor
- /^maint.*/
environment:
GITTEST_INVASIVE_FS_STRUCTURE: 1
GITTEST_INVASIVE_FS_SIZE: 1
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
GENERATOR: "Visual Studio 10 2010"
ARCH: 32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
GENERATOR: "Visual Studio 10 2010 Win64"
ARCH: 64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
GENERATOR: "Visual Studio 14 2015"
ARCH: 32
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
GENERATOR: "Visual Studio 14 2015 Win64"
ARCH: 64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
GENERATOR: "MSYS Makefiles"
ARCH: i686 # this is for 32-bit MinGW-w64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
GENERATOR: "MSYS Makefiles"
ARCH: 64
cache:
- i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
- x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z
build_script:
- ps: |
mkdir build
cd build
if ($env:GENERATOR -ne "MSYS Makefiles") {
cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D BUILD_EXAMPLES=ON -D MSVC_CRTDBG=ON .. -G"$env:GENERATOR"
cmake --build . --config Debug
}
- cmd: |
if "%GENERATOR%"=="MSYS Makefiles" (C:\MinGW\msys\1.0\bin\sh --login /c/projects/libgit2/script/appveyor-mingw.sh)
test_script:
- ps: |
# Disable DHE key exchange to fix intermittent build failures ("A buffer
# provided was too small") due to SChannel bug. See e.g.
# - https://github.com/aws/aws-sdk-cpp/issues/671
# - https://github.com/dotnet/corefx/issues/7812
New-Item HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\KeyExchangeAlgorithms\Diffie-Hellman -Force | New-ItemProperty -Name Enabled -Value 0 -Force
$ErrorActionPreference="Stop"
Start-FileDownload https://github.com/ethomson/poxyproxy/releases/download/v0.1.0/poxyproxy-0.1.0.jar -FileName poxyproxy.jar
# Run this early so we know it's ready by the time we need it
$proxyJob = Start-Job { java -jar $Env:APPVEYOR_BUILD_FOLDER\build\poxyproxy.jar -d --port 8080 --credentials foo:bar }
ctest -V -R libgit2_clar
Receive-Job -Job $proxyJob
$env:GITTEST_REMOTE_PROXY_URL = "localhost:8080"
$env:GITTEST_REMOTE_PROXY_USER = "foo"
$env:GITTEST_REMOTE_PROXY_PASS = "bar"
ctest -V -R libgit2_clar-proxy_credentials

94
azure-pipelines.yml Normal file
View File

@ -0,0 +1,94 @@
resources:
- repo: self
trigger:
- master
- maint/*
jobs:
- job: linux_trusty_gcc_openssl
displayName: 'Linux (Trusty; GCC; OpenSSL)'
pool:
vmImage: 'Ubuntu 16.04'
steps:
- template: ci/docker.yml
parameters:
imageName: 'libgit2/trusty-openssl:latest'
environmentVariables: |
CC=gcc
LEAK_CHECK=valgrind
- job: linux_trusty_clang_openssl
displayName: 'Linux (Trusty; Clang; OpenSSL)'
pool:
vmImage: 'Ubuntu 16.04'
steps:
- template: ci/docker.yml
parameters:
imageName: 'libgit2/trusty-openssl:latest'
environmentVariables: |
CC=clang
LEAK_CHECK=valgrind
- job: macos
displayName: 'macOS'
pool:
vmImage: 'macOS 10.13'
steps:
- bash: . '$(Build.SourcesDirectory)/ci/setup-osx.sh'
displayName: Setup
- template: ci/bash.yml
parameters:
environmentVariables:
TMPDIR: $(Agent.TempDirectory)
PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
LEAK_CHECK: leaks
- job: windows_vs_amd64
displayName: 'Windows (Visual Studio; amd64)'
pool: Hosted
steps:
- template: ci/powershell.yml
parameters:
environmentVariables:
CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -G"Visual Studio 12 2013 Win64"
- job: windows_vs_x86
displayName: 'Windows (Visual Studio; x86)'
pool: Hosted
steps:
- template: ci/powershell.yml
parameters:
environmentVariables:
CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -G"Visual Studio 12 2013"
- job: windows_mingw_amd64
displayName: 'Windows (MinGW; amd64)'
pool: Hosted
steps:
- powershell: . '$(Build.SourcesDirectory)\ci\setup-mingw.ps1'
displayName: Setup
env:
TEMP: $(Agent.TempDirectory)
ARCH: amd64
- template: ci/powershell.yml
parameters:
environmentVariables:
CMAKE_OPTIONS: -G"MinGW Makefiles"
PATH: $(Agent.TempDirectory)\mingw64\bin;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\CMake\bin
- job: windows_mingw_x86
displayName: 'Windows (MinGW; x86)'
pool: Hosted
steps:
- powershell: . '$(Build.SourcesDirectory)\ci\setup-mingw.ps1'
displayName: Setup
workingDirectory: '$(Build.BinariesDirectory)'
env:
TEMP: $(Agent.TempDirectory)
ARCH: x86
- template: ci/powershell.yml
parameters:
environmentVariables:
CMAKE_OPTIONS: -G"MinGW Makefiles"
PATH: $(Agent.TempDirectory)\mingw32\bin;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\CMake\bin

17
ci/bash.yml Normal file
View File

@ -0,0 +1,17 @@
# These are the steps used for building on machines with bash.
steps:
- bash: . '$(Build.SourcesDirectory)/ci/build.sh'
displayName: Build
workingDirectory: '$(Build.BinariesDirectory)'
env: ${{ parameters.environmentVariables }}
- bash: . '$(Build.SourcesDirectory)/ci/test.sh'
displayName: Test
workingDirectory: '$(Build.BinariesDirectory)'
env: ${{ parameters.environmentVariables }}
- task: PublishTestResults@2
displayName: Publish Test Results
condition: succeededOrFailed()
inputs:
testResultsFiles: 'results_*.xml'
searchFolder: '$(Build.BinariesDirectory)'
mergeTestResults: true

30
ci/build.ps1 Normal file
View File

@ -0,0 +1,30 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction'] = 'Stop'
if ($Env:SOURCE_DIR) { $SourceDirectory = $Env:SOURCE_DIR } else { $SourceDirectory = Split-Path (Split-Path $MyInvocation.MyCommand.Path -Parent) -Parent }
$BuildDirectory = $(Get-Location).Path
Write-Host "Source directory: ${SourceDirectory}"
Write-Host "Build directory: ${BuildDirectory}"
Write-Host ""
Write-Host "Operating system version:"
Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, ServicePackMajorVersion, BuildNumber, OSArchitecture | Format-List
Write-Host "PATH: ${Env:PATH}"
Write-Host ""
Write-Host "##############################################################################"
Write-Host "## Configuring build environment"
Write-Host "##############################################################################"
Invoke-Expression "cmake ${SourceDirectory} -DBUILD_EXAMPLES=ON ${Env:CMAKE_OPTIONS}"
if ($LastExitCode -ne 0) { [Environment]::Exit($LastExitCode) }
Write-Host ""
Write-Host "##############################################################################"
Write-Host "## Building libgit2"
Write-Host "##############################################################################"
cmake --build .
if ($LastExitCode -ne 0) { [Environment]::Exit($LastExitCode) }

39
ci/build.sh Executable file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env bash
#
# Environment variables:
#
# SOURCE_DIR: Set to the directory of the libgit2 source (optional)
# If not set, it will be derived relative to this script.
set -e
SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd)
CC=${CC:-cc}
indent() { sed "s/^/ /"; }
echo "Source directory: ${SOURCE_DIR}"
echo "Build directory: ${BUILD_DIR}"
echo ""
echo "Operating system version:"
uname -a 2>&1 | indent
echo "CMake version:"
cmake --version 2>&1 | indent
echo "Compiler version:"
$CC --version 2>&1 | indent
echo ""
echo "##############################################################################"
echo "## Configuring build environment"
echo "##############################################################################"
echo cmake ${SOURCE_DIR} -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON ${CMAKE_OPTIONS}
cmake ${SOURCE_DIR} -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON ${CMAKE_OPTIONS}
echo ""
echo "##############################################################################"
echo "## Building libgit2"
echo "##############################################################################"
cmake --build .

View File

@ -1,17 +1,13 @@
#!/bin/bash #!/bin/bash
set -e
# Only run this on our branches set -e
echo "Branch: $TRAVIS_BRANCH | Pull request: $TRAVIS_PULL_REQUEST | Slug: $TRAVIS_REPO_SLUG"
if [ "$TRAVIS_BRANCH" != "master" -o "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "libgit2/libgit2" ];
then
echo "Only analyzing the 'master' brach of the main repository."
exit 0
fi
# Environment check # Environment check
[ -z "$COVERITY_TOKEN" ] && echo "Need to set a coverity token" && exit 1 [ -z "$COVERITY_TOKEN" ] && echo "Need to set a coverity token" && exit 1
SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd)
case $(uname -m) in case $(uname -m) in
i?86) BITS=32 ;; i?86) BITS=32 ;;
amd64|x86_64) BITS=64 ;; amd64|x86_64) BITS=64 ;;
@ -32,31 +28,29 @@ if [ ! -d "$TOOL_BASE" ]; then
ln -s "$TOOL_DIR" "$TOOL_BASE"/cov-analysis ln -s "$TOOL_DIR" "$TOOL_BASE"/cov-analysis
fi fi
cp script/user_nodefs.h "$TOOL_BASE"/cov-analysis/config/user_nodefs.h cp "${SOURCE_DIR}/script/user_nodefs.h" "$TOOL_BASE"/cov-analysis/config/user_nodefs.h
COV_BUILD="$TOOL_BASE/cov-analysis/bin/cov-build" COV_BUILD="$TOOL_BASE/cov-analysis/bin/cov-build"
# Configure and build # Configure and build
rm -rf _build cmake ${SOURCE_DIR}
mkdir _build
cd _build
cmake .. -DTHREADSAFE=ON
COVERITY_UNSUPPORTED=1 \ COVERITY_UNSUPPORTED=1 \
$COV_BUILD --dir cov-int \ $COV_BUILD --dir cov-int \
cmake --build . cmake --build .
# Upload results # Upload results
tar czf libgit2.tgz cov-int tar czf libgit2.tgz cov-int
SHA=$(git rev-parse --short HEAD) SHA=$(cd ${SOURCE_DIR} && git rev-parse --short HEAD)
HTML="$(curl \ HTML="$(curl \
--silent \ --silent \
--write-out "\n%{http_code}" \ --write-out "\n%{http_code}" \
--form token="$COVERITY_TOKEN" \ --form token="$COVERITY_TOKEN" \
--form email=bs@github.com \ --form email=libgit2@gmail.com \
--form file=@libgit2.tgz \ --form file=@libgit2.tgz \
--form version="$SHA" \ --form version="$SHA" \
--form description="Travis build" \ --form description="libgit2 build" \
https://scan.coverity.com/builds?project=libgit2)" https://scan.coverity.com/builds?project=libgit2)"
# Body is everything up to the last line # Body is everything up to the last line
BODY="$(echo "$HTML" | head -n-1)" BODY="$(echo "$HTML" | head -n-1)"
@ -65,7 +59,7 @@ STATUS_CODE="$(echo "$HTML" | tail -n1)"
echo "${BODY}" echo "${BODY}"
if [ "${STATUS_CODE}" != "201" ]; then if [ "${STATUS_CODE}" != "200" -a "${STATUS_CODE}" != "201" ]; then
echo "Received error code ${STATUS_CODE} from Coverity" echo "Received error code ${STATUS_CODE} from Coverity"
exit 1 exit 1
fi fi

33
ci/docker.yml Normal file
View File

@ -0,0 +1,33 @@
# These are the steps used in a container-based build in VSTS.
steps:
- task: docker@0
displayName: Build
inputs:
action: 'Run an image'
imageName: ${{ parameters.imageName }}
volumes: |
$(Build.SourcesDirectory):/src
$(Build.BinariesDirectory):/build
envVars: ${{ parameters.environmentVariables }}
workDir: '/build'
containerCommand: '/src/ci/build.sh'
detached: false
- task: docker@0
displayName: Test
inputs:
action: 'Run an image'
imageName: ${{ parameters.imageName }}
volumes: |
$(Build.SourcesDirectory):/src
$(Build.BinariesDirectory):/build
envVars: ${{ parameters.environmentVariables }}
workDir: '/build'
containerCommand: '/src/ci/test.sh'
detached: false
- task: publishtestresults@2
displayName: Publish Test Results
condition: succeededOrFailed()
inputs:
testResultsFiles: 'results_*.xml'
searchFolder: '$(Build.BinariesDirectory)'
mergeTestResults: true

22
ci/nightly.yml Normal file
View File

@ -0,0 +1,22 @@
resources:
- repo: self
jobs:
- job: coverity
displayName: 'Coverity'
pool:
vmImage: 'Ubuntu 16.04'
steps:
- task: Docker@0
displayName: Build
inputs:
action: 'Run an image'
imageName: 'libgit2/trusty-openssl:latest'
volumes: |
$(Build.SourcesDirectory):/src
$(Build.BinariesDirectory):/build
envVars: |
COVERITY_TOKEN=$(COVERITY_TOKEN)
workDir: '/build'
containerCommand: '/src/ci/coverity.sh'
detached: false

17
ci/powershell.yml Normal file
View File

@ -0,0 +1,17 @@
# These are the steps used for building on machines with PowerShell.
steps:
- powershell: . '$(Build.SourcesDirectory)\ci\build.ps1'
displayName: Build
workingDirectory: '$(Build.BinariesDirectory)'
env: ${{ parameters.environmentVariables }}
- powershell: . '$(Build.SourcesDirectory)\ci\test.ps1'
displayName: Test
workingDirectory: '$(Build.BinariesDirectory)'
env: ${{ parameters.environmentVariables }}
- task: PublishTestResults@2
displayName: Publish Test Results
condition: succeededOrFailed()
inputs:
testResultsFiles: 'results_*.xml'
searchFolder: '$(Build.BinariesDirectory)'
mergeTestResults: true

13
ci/setup-linux.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
set -e
set -x
TMPDIR=${TMPDIR:-/tmp}
if [ -z "$SKIP_APT" ]; then
apt-get update
apt-get -y install build-essential pkg-config clang cmake openssl libssl-dev libssh2-1-dev libcurl4-gnutls-dev openssh-server
fi
mkdir -p /var/run/sshd

25
ci/setup-mingw.ps1 Normal file
View File

@ -0,0 +1,25 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction'] = 'Stop'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
[Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem");
Write-Host "##############################################################################"
Write-Host "## Downloading mingw"
Write-Host "##############################################################################"
if ($env:ARCH -eq "amd64") {
$mingw_uri = "https://bintray.com/libgit2/build-dependencies/download_file?file_path=mingw-w64-x86_64-8.1.0-release-win32-seh-rt_v6-rev0.zip"
$platform = "x86_64"
} else {
$mingw_uri = "https://bintray.com/libgit2/build-dependencies/download_file?file_path=mingw-w64-i686-8.1.0-release-win32-sjlj-rt_v6-rev0.zip"
$platform = "x86"
}
$wc = New-Object net.webclient
$wc.Downloadfile($mingw_uri, "${Env:TEMP}/mingw-${Env:ARCH}.zip")
[System.IO.Compression.ZipFile]::ExtractToDirectory("${Env:TEMP}/mingw-${Env:ARCH}.zip", $Env:TEMP)

8
ci/setup-osx.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
set -x
brew update
brew install pkgconfig zlib curl openssl libssh2
ln -s /Applications/Xcode.app/Contents/Developer/usr/lib/libLeaksAtExit.dylib /usr/local/lib

72
ci/test.ps1 Normal file
View File

@ -0,0 +1,72 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction'] = 'Stop'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$SourceDir = Split-Path (Split-Path (Get-Variable MyInvocation).Value.MyCommand.Path)
$BuildDir = Get-Location
$global:Success = $true
if ($Env:SKIP_TESTS) { exit }
# Ask ctest what it would run if we were to invoke it directly. This lets
# us manage the test configuration in a single place (tests/CMakeLists.txt)
# instead of running clar here as well. But it allows us to wrap our test
# harness with a leak checker like valgrind. Append the option to write
# JUnit-style XML files.
function run_test {
$TestName = $args[0]
$TestCommand = (ctest -N -V -R "^$TestName$") -join "`n" -replace "(?ms).*\n^[0-9]*: Test command: ","" -replace "\n.*",""
$TestCommand += " -r${BuildDir}\results_${TestName}.xml"
Write-Host $TestCommand
Invoke-Expression $TestCommand
if ($LastExitCode -ne 0) { $global:Success = $false }
}
Write-Host "##############################################################################"
Write-Host "## Configuring test environment"
Write-Host "##############################################################################"
if (-not $Env:SKIP_PROXY_TESTS) {
Write-Host ""
Write-Host "Starting HTTP proxy..."
Invoke-WebRequest -Method GET -Uri https://github.com/ethomson/poxyproxy/releases/download/v0.1.0/poxyproxy-0.1.0.jar -OutFile poxyproxy.jar
javaw -jar poxyproxy.jar -d --port 8080 --credentials foo:bar
}
Write-Host ""
Write-Host "##############################################################################"
Write-Host "## Running (offline) tests"
Write-Host "##############################################################################"
run_test offline
if (-not $Env:SKIP_ONLINE_TESTS) {
Write-Host ""
Write-Host "##############################################################################"
Write-Host "## Running (online) tests"
Write-Host "##############################################################################"
run_test online
}
if (-not $Env:SKIP_PROXY_TESTS) {
Write-Host ""
Write-Host "Running proxy tests"
Write-Host ""
$Env:GITTEST_REMOTE_PROXY_URL="localhost:8080"
$Env:GITTEST_REMOTE_PROXY_USER="foo"
$Env:GITTEST_REMOTE_PROXY_PASS="bar"
run_test proxy
taskkill /F /IM javaw.exe
}
if (-Not $global:Success) { exit 1 }

198
ci/test.sh Executable file
View File

@ -0,0 +1,198 @@
#!/usr/bin/env bash
set -e
if [ -n "$SKIP_TESTS" ]; then
exit 0
fi
SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd)
TMPDIR=${TMPDIR:-/tmp}
USER=${USER:-$(whoami)}
SUCCESS=1
VALGRIND="valgrind --leak-check=full --show-reachable=yes --error-exitcode=125 --num-callers=50 --suppressions=\"$SOURCE_DIR/libgit2_clar.supp\""
LEAKS="MallocStackLogging=1 MallocScribble=1 leaks -quiet -atExit -- nohup"
cleanup() {
echo "Cleaning up..."
if [ ! -z "$GITDAEMON_DIR" -a -f "${GITDAEMON_DIR}/pid" ]; then
echo "Stopping git daemon..."
kill $(cat "${GITDAEMON_DIR}/pid")
fi
if [ ! -z "$SSHD_DIR" -a -f "${SSHD_DIR}/pid" ]; then
echo "Stopping SSH..."
kill $(cat "${SSHD_DIR}/pid")
fi
echo "Done."
}
failure() {
echo "Test exited with code: $1"
SUCCESS=0
}
# Ask ctest what it would run if we were to invoke it directly. This lets
# us manage the test configuration in a single place (tests/CMakeLists.txt)
# instead of running clar here as well. But it allows us to wrap our test
# harness with a leak checker like valgrind. Append the option to write
# JUnit-style XML files.
run_test() {
TEST_CMD=$(ctest -N -V -R "^${1}$" | sed -n 's/^[0-9]*: Test command: //p')
TEST_CMD="${TEST_CMD} -r${BUILD_DIR}/results_${1}.xml"
if [ "$LEAK_CHECK" = "valgrind" ]; then
RUNNER="$VALGRIND $TEST_CMD"
elif [ "$LEAK_CHECK" = "leaks" ]; then
RUNNER="$LEAKS $TEST_CMD"
else
RUNNER="$TEST_CMD"
fi
eval $RUNNER || failure
}
# Configure the test environment; run them early so that we're certain
# that they're started by the time we need them.
echo "##############################################################################"
echo "## Configuring test environment"
echo "##############################################################################"
if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
echo "Starting git daemon..."
GITDAEMON_DIR=`mktemp -d ${TMPDIR}/gitdaemon.XXXXXXXX`
git init --bare "${GITDAEMON_DIR}/test.git"
git daemon --listen=localhost --export-all --enable=receive-pack --pid-file="${GITDAEMON_DIR}/pid" --base-path="${GITDAEMON_DIR}" "${GITDAEMON_DIR}" 2>/dev/null &
fi
if [ -z "$SKIP_PROXY_TESTS" ]; then
echo "Starting HTTP proxy..."
curl -L https://github.com/ethomson/poxyproxy/releases/download/v0.1.0/poxyproxy-0.1.0.jar >poxyproxy.jar
java -jar poxyproxy.jar -d --port 8080 --credentials foo:bar >/dev/null 2>&1 &
fi
if [ -z "$SKIP_SSH_TESTS" ]; then
echo "Starting ssh daemon..."
HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
SSHD_DIR=`mktemp -d ${TMPDIR}/sshd.XXXXXXXX`
git init --bare "${SSHD_DIR}/test.git"
cat >"${SSHD_DIR}/sshd_config" <<-EOF
Port 2222
ListenAddress 0.0.0.0
Protocol 2
HostKey ${SSHD_DIR}/id_rsa
PidFile ${SSHD_DIR}/pid
AuthorizedKeysFile ${HOME}/.ssh/authorized_keys
LogLevel DEBUG
RSAAuthentication yes
PasswordAuthentication yes
PubkeyAuthentication yes
ChallengeResponseAuthentication no
StrictModes no
# Required here as sshd will simply close connection otherwise
UsePAM no
EOF
ssh-keygen -t rsa -f "${SSHD_DIR}/id_rsa" -N "" -q
/usr/sbin/sshd -f "${SSHD_DIR}/sshd_config" -E "${SSHD_DIR}/log"
# Set up keys
mkdir "${HOME}/.ssh"
ssh-keygen -t rsa -f "${HOME}/.ssh/id_rsa" -N "" -q
cat "${HOME}/.ssh/id_rsa.pub" >>"${HOME}/.ssh/authorized_keys"
while read algorithm key comment; do
echo "[localhost]:2222 $algorithm $key" >>"${HOME}/.ssh/known_hosts"
done <"${SSHD_DIR}/id_rsa.pub"
# Get the fingerprint for localhost and remove the colons so we can
# parse it as a hex number. Older versions have a different output
# format.
if [[ $(ssh -V 2>&1) == OpenSSH_6* ]]; then
SSH_FINGERPRINT=$(ssh-keygen -F '[localhost]:2222' -f "${HOME}/.ssh/known_hosts" -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
else
SSH_FINGERPRINT=$(ssh-keygen -E md5 -F '[localhost]:2222' -f "${HOME}/.ssh/known_hosts" -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
fi
fi
# Run the tests that do not require network connectivity.
if [ -z "$SKIP_OFFLINE_TESTS" ]; then
echo ""
echo "##############################################################################"
echo "## Running (offline) tests"
echo "##############################################################################"
run_test offline
fi
if [ -z "$SKIP_ONLINE_TESTS" ]; then
# Run the various online tests. The "online" test suite only includes the
# default online tests that do not require additional configuration. The
# "proxy" and "ssh" test suites require further setup.
echo ""
echo "##############################################################################"
echo "## Running (online) tests"
echo "##############################################################################"
run_test online
fi
if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
echo ""
echo "Running gitdaemon tests"
echo ""
export GITTEST_REMOTE_URL="git://localhost/test.git"
run_test gitdaemon
unset GITTEST_REMOTE_URL
fi
if [ -z "$SKIP_PROXY_TESTS" ]; then
echo ""
echo "Running proxy tests"
echo ""
export GITTEST_REMOTE_PROXY_URL="localhost:8080"
export GITTEST_REMOTE_PROXY_USER="foo"
export GITTEST_REMOTE_PROXY_PASS="bar"
run_test proxy
unset GITTEST_REMOTE_PROXY_URL
unset GITTEST_REMOTE_PROXY_USER
unset GITTEST_REMOTE_PROXY_PASS
fi
if [ -z "$SKIP_SSH_TESTS" ]; then
echo ""
echo "Running ssh tests"
echo ""
export GITTEST_REMOTE_URL="ssh://localhost:2222/$SSHD_DIR/test.git"
export GITTEST_REMOTE_USER=$USER
export GITTEST_REMOTE_SSH_KEY="${HOME}/.ssh/id_rsa"
export GITTEST_REMOTE_SSH_PUBKEY="${HOME}/.ssh/id_rsa.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE=""
export GITTEST_REMOTE_SSH_FINGERPRINT="${SSH_FINGERPRINT}"
run_test ssh
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_USER
unset GITTEST_REMOTE_SSH_KEY
unset GITTEST_REMOTE_SSH_PUBKEY
unset GITTEST_REMOTE_SSH_PASSPHRASE
unset GITTEST_REMOTE_SSH_FINGERPRINT
fi
cleanup
if [ "$SUCCESS" -ne "1" ]; then
echo "Some tests failed."
exit 1
fi
echo "Success."
exit 0

View File

@ -0,0 +1,29 @@
@CHECK_PROTOTYPE_DEFINITION_HEADER@
static void cmakeRequireSymbol(int dummy, ...) {
(void) dummy;
}
static void checkSymbol(void) {
#ifndef @CHECK_PROTOTYPE_DEFINITION_SYMBOL@
cmakeRequireSymbol(0, &@CHECK_PROTOTYPE_DEFINITION_SYMBOL@);
#endif
}
@CHECK_PROTOTYPE_DEFINITION_PROTO@ {
return @CHECK_PROTOTYPE_DEFINITION_RETURN@;
}
#ifdef __CLASSIC_C__
int main() {
int ac;
char*av[];
#else
int main(int ac, char *av[]) {
#endif
checkSymbol();
if (ac > 1000) {
return *av[0];
}
return 0;
}

View File

@ -0,0 +1,96 @@
# - Check if the protoype we expect is correct.
# check_prototype_definition(FUNCTION PROTOTYPE RETURN HEADER VARIABLE)
# FUNCTION - The name of the function (used to check if prototype exists)
# PROTOTYPE- The prototype to check.
# RETURN - The return value of the function.
# HEADER - The header files required.
# VARIABLE - The variable to store the result.
# Example:
# check_prototype_definition(getpwent_r
# "struct passwd *getpwent_r(struct passwd *src, char *buf, int buflen)"
# "NULL"
# "unistd.h;pwd.h"
# SOLARIS_GETPWENT_R)
# The following variables may be set before calling this macro to
# modify the way the check is run:
#
# CMAKE_REQUIRED_FLAGS = string of compile command line flags
# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
# CMAKE_REQUIRED_INCLUDES = list of include directories
# CMAKE_REQUIRED_LIBRARIES = list of libraries to link
#=============================================================================
# Copyright 2005-2009 Kitware, Inc.
# Copyright 2010-2011 Andreas Schneider <asn@cryptomilk.org>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
#
get_filename_component(__check_proto_def_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
function(CHECK_PROTOTYPE_DEFINITION _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIABLE)
if ("${_VARIABLE}" MATCHES "^${_VARIABLE}$")
set(CHECK_PROTOTYPE_DEFINITION_CONTENT "/* */\n")
set(CHECK_PROTOTYPE_DEFINITION_FLAGS ${CMAKE_REQUIRED_FLAGS})
if (CMAKE_REQUIRED_LIBRARIES)
set(CHECK_PROTOTYPE_DEFINITION_LIBS
"-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
else(CMAKE_REQUIRED_LIBRARIES)
set(CHECK_PROTOTYPE_DEFINITION_LIBS)
endif(CMAKE_REQUIRED_LIBRARIES)
if (CMAKE_REQUIRED_INCLUDES)
set(CMAKE_SYMBOL_EXISTS_INCLUDES
"-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
else(CMAKE_REQUIRED_INCLUDES)
set(CMAKE_SYMBOL_EXISTS_INCLUDES)
endif(CMAKE_REQUIRED_INCLUDES)
foreach(_FILE ${_HEADER})
set(CHECK_PROTOTYPE_DEFINITION_HEADER
"${CHECK_PROTOTYPE_DEFINITION_HEADER}#include <${_FILE}>\n")
endforeach(_FILE)
set(CHECK_PROTOTYPE_DEFINITION_SYMBOL ${_FUNCTION})
set(CHECK_PROTOTYPE_DEFINITION_PROTO ${_PROTOTYPE})
set(CHECK_PROTOTYPE_DEFINITION_RETURN ${_RETURN})
configure_file("${__check_proto_def_dir}/CheckPrototypeDefinition.c.in"
"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c" @ONLY)
file(READ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c _SOURCE)
try_compile(${_VARIABLE}
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c
COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CHECK_PROTOTYPE_DEFINITION_FLAGS}
"${CHECK_PROTOTYPE_DEFINITION_LIBS}"
"${CMAKE_SYMBOL_EXISTS_INCLUDES}"
OUTPUT_VARIABLE OUTPUT)
if (${_VARIABLE})
set(${_VARIABLE} 1 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}")
message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - True")
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
"Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} passed with the following output:\n"
"${OUTPUT}\n\n")
else (${_VARIABLE})
message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - False")
set(${_VARIABLE} 0 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}")
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
"Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} failed with the following output:\n"
"${OUTPUT}\n\n${_SOURCE}\n\n")
endif (${_VARIABLE})
endif("${_VARIABLE}" MATCHES "^${_VARIABLE}$")
endfunction(CHECK_PROTOTYPE_DEFINITION)

View File

@ -12,14 +12,19 @@ IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
ENDIF() ENDIF()
FIND_PATH(ICONV_INCLUDE_DIR iconv.h) FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
CHECK_FUNCTION_EXISTS(iconv_open libc_has_iconv)
FIND_LIBRARY(iconv_lib NAMES iconv libiconv libiconv-2 c) FIND_LIBRARY(iconv_lib NAMES iconv libiconv libiconv-2 c)
IF(ICONV_INCLUDE_DIR AND iconv_lib) IF(ICONV_INCLUDE_DIR AND libc_has_iconv)
SET(ICONV_FOUND TRUE) SET(ICONV_FOUND TRUE)
ENDIF() SET(ICONV_LIBRARIES "")
IF(NOT ICONV_FIND_QUIETLY)
IF(ICONV_FOUND) MESSAGE(STATUS "Found Iconv: provided by libc")
# split iconv into -L and -l linker options, so we can set them for pkg-config ENDIF(NOT ICONV_FIND_QUIETLY)
ELSEIF(ICONV_INCLUDE_DIR AND iconv_lib)
SET(ICONV_FOUND TRUE)
# split iconv into -L and -l linker options, so we can
# set them for pkg-config
GET_FILENAME_COMPONENT(iconv_path ${iconv_lib} PATH) GET_FILENAME_COMPONENT(iconv_path ${iconv_lib} PATH)
GET_FILENAME_COMPONENT(iconv_name ${iconv_lib} NAME_WE) GET_FILENAME_COMPONENT(iconv_name ${iconv_lib} NAME_WE)
STRING(REGEX REPLACE "^lib" "" iconv_name ${iconv_name}) STRING(REGEX REPLACE "^lib" "" iconv_name ${iconv_name})

View File

@ -7,10 +7,10 @@
#ifndef INCLUDE_git_version_h__ #ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__ #define INCLUDE_git_version_h__
#define LIBGIT2_VERSION "0.27.4" #define LIBGIT2_VERSION "0.27.7"
#define LIBGIT2_VER_MAJOR 0 #define LIBGIT2_VER_MAJOR 0
#define LIBGIT2_VER_MINOR 27 #define LIBGIT2_VER_MINOR 27
#define LIBGIT2_VER_REVISION 4 #define LIBGIT2_VER_REVISION 7
#define LIBGIT2_VER_PATCH 0 #define LIBGIT2_VER_PATCH 0
#define LIBGIT2_SOVERSION 27 #define LIBGIT2_SOVERSION 27

View File

@ -47,3 +47,28 @@
... ...
fun:__check_pf fun:__check_pf
} }
{
ignore-curl-global-init
Memcheck:Leak
...
fun:curl_global_init
}
{
ignore-libssh2-gcrypt-leak
Memcheck:Leak
...
fun:gcry_control
obj:*libssh2.so*
}
{
ignore-noai6ai_cached-double-free
Memcheck:Free
fun:free
fun:__libc_freeres
...
fun:exit
...
}

View File

@ -1,23 +0,0 @@
#!/bin/sh
set -e
cd `dirname "$0"`/..
if [ "$ARCH" = "i686" ]; then
f=i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
if ! [ -e $f ]; then
curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/sjlj/$f
fi
7z x $f > /dev/null
export PATH=`pwd`/mingw32/bin:$PATH
else
f=x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z
if ! [ -e $f ]; then
curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/$f
fi
7z x $f > /dev/null
export PATH=`pwd`/mingw64/bin:$PATH
fi
cd build
gcc --version
cmake --version
cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D BUILD_EXAMPLES=ON .. -G"$GENERATOR"
cmake --build . --config RelWithDebInfo

View File

@ -1,102 +0,0 @@
#!/bin/sh
set -x
if [ -n "$COVERITY" ];
then
./script/coverity.sh;
exit $?;
fi
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
export PKG_CONFIG_PATH=$(ls -d /usr/local/Cellar/{curl,zlib}/*/lib/pkgconfig | paste -s -d':' -)
# Set up a ramdisk for us to put our test data on to speed up tests on macOS
export CLAR_TMP="$HOME"/_clar_tmp
mkdir -p $CLAR_TMP
# 5*2M sectors aka ~5GB of space
device=$(hdiutil attach -nomount ram://$((5 * 2 * 1024 * 1024)))
newfs_hfs $device
mount -t hfs $device $CLAR_TMP
fi
# Should we ask Travis to cache this file?
curl -L https://github.com/ethomson/poxyproxy/releases/download/v0.1.0/poxyproxy-0.1.0.jar >poxyproxy.jar || exit $?
# Run this early so we know it's ready by the time we need it
java -jar poxyproxy.jar -d --port 8080 --credentials foo:bar &
mkdir _build
cd _build
# shellcheck disable=SC2086
cmake .. -DBUILD_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
make -j2 install || exit $?
# If this platform doesn't support test execution, bail out now
if [ -n "$SKIP_TESTS" ];
then
exit $?;
fi
# Create a test repo which we can use for the online::push tests
mkdir "$HOME"/_temp
git init --bare "$HOME"/_temp/test.git
git daemon --listen=localhost --export-all --enable=receive-pack --base-path="$HOME"/_temp "$HOME"/_temp 2>/dev/null &
export GITTEST_REMOTE_URL="git://localhost/test.git"
# Run the test suite
ctest -V -R libgit2_clar || exit $?
# Now that we've tested the raw git protocol, let's set up ssh to we
# can do the push tests over it
killall git-daemon
# Set up sshd
mkdir ~/sshd/
cat >~/sshd/sshd_config<<-EOF
Port 2222
ListenAddress 0.0.0.0
Protocol 2
HostKey ${HOME}/sshd/id_rsa
PidFile ${HOME}/sshd/pid
RSAAuthentication yes
PasswordAuthentication yes
PubkeyAuthentication yes
ChallengeResponseAuthentication no
# Required here as sshd will simply close connection otherwise
UsePAM no
EOF
ssh-keygen -t rsa -f ~/sshd/id_rsa -N "" -q
/usr/sbin/sshd -f ~/sshd/sshd_config
# Set up keys
ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
while read algorithm key comment; do
echo "[localhost]:2222 $algorithm $key" >>~/.ssh/known_hosts
done <~/sshd/id_rsa.pub
# Get the fingerprint for localhost and remove the colons so we can parse it as
# a hex number. The Mac version is newer so it has a different output format.
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -E md5 -F '[localhost]:2222' -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
else
export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -F '[localhost]:2222' -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
fi
# Use the SSH server
export GITTEST_REMOTE_URL="ssh://localhost:2222/$HOME/_temp/test.git"
export GITTEST_REMOTE_USER=$USER
export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa"
export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE=""
ctest -V -R libgit2_clar-ssh || exit $?
# Use the proxy we started at the beginning
export GITTEST_REMOTE_PROXY_URL="localhost:8080"
export GITTEST_REMOTE_PROXY_USER="foo"
export GITTEST_REMOTE_PROXY_PASS="bar"
ctest -V -R libgit2_clar-proxy_credentials || exit $?
kill $(cat "$HOME/sshd/pid")

View File

@ -1,9 +0,0 @@
#!/bin/sh
set -x
brew update
brew install zlib
brew install curl
brew install openssl
brew install libssh2

View File

@ -57,10 +57,19 @@ IF (HAVE_FUTIMENS)
SET(GIT_USE_FUTIMENS 1) SET(GIT_USE_FUTIMENS 1)
ENDIF () ENDIF ()
CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R) CHECK_PROTOTYPE_DEFINITION(qsort_r
IF (HAVE_QSORT_R) "void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))"
ADD_DEFINITIONS(-DHAVE_QSORT_R) "" "stdlib.h" HAVE_QSORT_R_BSD)
ENDIF () IF (HAVE_QSORT_R_BSD)
ADD_DEFINITIONS(-DHAVE_QSORT_R_BSD)
ENDIF()
CHECK_PROTOTYPE_DEFINITION(qsort_r
"void qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg)"
"" "stdlib.h" HAVE_QSORT_R_GNU)
IF (HAVE_QSORT_R_GNU)
ADD_DEFINITIONS(-DHAVE_QSORT_R_GNU)
ENDIF()
CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S) CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S)
IF (HAVE_QSORT_S) IF (HAVE_QSORT_S)

View File

@ -443,7 +443,7 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
while (eoln < buffer_end && *eoln != '\n') while (eoln < buffer_end && *eoln != '\n')
++eoln; ++eoln;
if (git__prefixcmp(buffer, "encoding ") == 0) { if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
buffer += strlen("encoding "); buffer += strlen("encoding ");
commit->message_encoding = git__strndup(buffer, eoln - buffer); commit->message_encoding = git__strndup(buffer, eoln - buffer);

View File

@ -171,7 +171,9 @@ static int commit_quick_parse(
buffer--; buffer--;
} }
if ((buffer == committer_start) || (git__strtol64(&commit_time, (char *)(buffer + 1), NULL, 10) < 0)) if ((buffer == committer_start) ||
(git__strntol64(&commit_time, (char *)(buffer + 1),
buffer_end - buffer + 1, NULL, 10) < 0))
return commit_error(commit, "cannot parse commit time"); return commit_error(commit, "cannot parse commit time");
commit->time = commit_time; commit->time = commit_time;

View File

@ -1300,7 +1300,7 @@ int git_config_parse_int64(int64_t *out, const char *value)
const char *num_end; const char *num_end;
int64_t num; int64_t num;
if (!value || git__strtol64(&num, value, &num_end, 0) < 0) if (!value || git__strntol64(&num, value, strlen(value), &num_end, 0) < 0)
goto fail_parse; goto fail_parse;
switch (*num_end) { switch (*num_end) {

View File

@ -928,6 +928,9 @@ static int parse_include(git_config_parser *reader,
char *dir; char *dir;
int result; int result;
if (!file)
return 0;
if ((result = git_path_dirname_r(&path, reader->file->path)) < 0) if ((result = git_path_dirname_r(&path, reader->file->path)) < 0)
return result; return result;
@ -1029,7 +1032,7 @@ static int parse_conditional_include(git_config_parser *reader,
size_t i; size_t i;
int error = 0, matches; int error = 0, matches;
if (!parse_data->repo) if (!parse_data->repo || !file)
return 0; return 0;
condition = git__substrdup(section + strlen("includeIf."), condition = git__substrdup(section + strlen("includeIf."),
@ -1072,8 +1075,16 @@ static int read_on_variable(
GIT_UNUSED(line); GIT_UNUSED(line);
GIT_UNUSED(line_len); GIT_UNUSED(line_len);
if (current_section) {
/* TODO: Once warnings lang, we should likely warn
* here. Git appears to warn in most cases if it sees
* un-namespaced config options.
*/
git_buf_puts(&buf, current_section);
git_buf_putc(&buf, '.');
}
git__strtolower(var_name); git__strtolower(var_name);
git_buf_printf(&buf, "%s.%s", current_section, var_name); git_buf_puts(&buf, var_name);
git__free(var_name); git__free(var_name);
if (git_buf_oom(&buf)) { if (git_buf_oom(&buf)) {

View File

@ -315,50 +315,52 @@ done:
static int parse_multiline_variable(git_config_parser *reader, git_buf *value, int in_quotes) static int parse_multiline_variable(git_config_parser *reader, git_buf *value, int in_quotes)
{ {
char *line = NULL, *proc_line = NULL;
int quote_count; int quote_count;
bool multiline; bool multiline = true;
/* Check that the next line exists */ while (multiline) {
git_parse_advance_line(&reader->ctx); char *line = NULL, *proc_line = NULL;
line = git__strndup(reader->ctx.line, reader->ctx.line_len); int error;
if (line == NULL)
return -1;
/* We've reached the end of the file, there is no continuation. /* Check that the next line exists */
* (this is not an error). git_parse_advance_line(&reader->ctx);
*/ line = git__strndup(reader->ctx.line, reader->ctx.line_len);
if (line[0] == '\0') { GITERR_CHECK_ALLOC(line);
/*
* We've reached the end of the file, there is no continuation.
* (this is not an error).
*/
if (line[0] == '\0') {
error = 0;
goto out;
}
/* If it was just a comment, pretend it didn't exist */
quote_count = strip_comments(line, !!in_quotes);
if (line[0] == '\0')
goto next;
if ((error = unescape_line(&proc_line, &multiline,
line, in_quotes)) < 0)
goto out;
/* Add this line to the multiline var */
if ((error = git_buf_puts(value, proc_line)) < 0)
goto out;
next:
git__free(line); git__free(line);
return 0; git__free(proc_line);
} in_quotes = quote_count;
continue;
quote_count = strip_comments(line, !!in_quotes); out:
/* If it was just a comment, pretend it didn't exist */
if (line[0] == '\0') {
git__free(line); git__free(line);
return parse_multiline_variable(reader, value, quote_count); git__free(proc_line);
/* TODO: unbounded recursion. This **could** be exploitable */ return error;
} }
if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
git__free(line);
return -1;
}
/* add this line to the multiline var */
git_buf_puts(value, proc_line);
git__free(line);
git__free(proc_line);
/*
* If we need to continue reading the next line, let's just
* keep putting stuff in the buffer
*/
if (multiline)
return parse_multiline_variable(reader, value, quote_count);
return 0; return 0;
} }

View File

@ -822,7 +822,7 @@ int git_diff_find_similar(
num_deltas = diff->deltas.length; num_deltas = diff->deltas.length;
/* TODO: maybe abort if deltas.length > rename_limit ??? */ /* TODO: maybe abort if deltas.length > rename_limit ??? */
if (!git__is_uint32(num_deltas)) if (!num_deltas || !git__is_uint32(num_deltas))
goto cleanup; goto cleanup;
/* No flags set; nothing to do */ /* No flags set; nothing to do */

View File

@ -1774,7 +1774,8 @@ int git_index_conflict_add(git_index *index,
if (entries[i] && !valid_filemode(entries[i]->mode)) { if (entries[i] && !valid_filemode(entries[i]->mode)) {
giterr_set(GITERR_INDEX, "invalid filemode for stage %d entry", giterr_set(GITERR_INDEX, "invalid filemode for stage %d entry",
i + 1); i + 1);
return -1; ret = -1;
goto on_error;
} }
} }
@ -2206,7 +2207,7 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
int64_t tmp; int64_t tmp;
if (git__strtol64(&tmp, buffer, &endptr, 8) < 0 || if (git__strntol64(&tmp, buffer, size, &endptr, 8) < 0 ||
!endptr || endptr == buffer || *endptr || !endptr || endptr == buffer || *endptr ||
tmp < 0 || tmp > UINT32_MAX) { tmp < 0 || tmp > UINT32_MAX) {
index_entry_reuc_free(lost); index_entry_reuc_free(lost);
@ -3509,7 +3510,7 @@ int git_index_snapshot_new(git_vector *snap, git_index *index)
error = git_vector_dup(snap, &index->entries, index->entries._cmp); error = git_vector_dup(snap, &index->entries, index->entries._cmp);
if (error < 0) if (error < 0)
git_index_free(index); git_index_snapshot_release(snap, index);
return error; return error;
} }

View File

@ -1384,8 +1384,8 @@ static int git_odb_stream__invalid_length(
{ {
giterr_set(GITERR_ODB, giterr_set(GITERR_ODB,
"cannot %s - " "cannot %s - "
"Invalid length. %"PRIdZ" was expected. The " "Invalid length. %"PRId64" was expected. The "
"total size of the received chunks amounts to %"PRIdZ".", "total size of the received chunks amounts to %"PRId64".",
action, stream->declared_size, stream->received_bytes); action, stream->declared_size, stream->received_bytes);
return -1; return -1;

View File

@ -210,7 +210,7 @@ static int packfile_load__cb(void *data, git_buf *path)
for (i = 0; i < backend->packs.length; ++i) { for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p = git_vector_get(&backend->packs, i); struct git_pack_file *p = git_vector_get(&backend->packs, i);
if (memcmp(p->pack_name, path_str, cmp_len) == 0) if (strncmp(p->pack_name, path_str, cmp_len) == 0)
return 0; return 0;
} }

View File

@ -1666,7 +1666,7 @@ int insert_tree(git_packbuilder *pb, git_tree *tree)
break; break;
case GIT_OBJ_BLOB: case GIT_OBJ_BLOB:
if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0) if ((error = retrieve_object(&obj, pb, entry_id)) < 0)
return error; return error;
if (obj->uninteresting) if (obj->uninteresting)
continue; continue;

View File

@ -8,12 +8,14 @@
int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len) int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len)
{ {
if (content_len) if (content && content_len) {
ctx->content = content; ctx->content = content;
else ctx->content_len = content_len;
ctx->content = NULL; } else {
ctx->content = "";
ctx->content_len = 0;
}
ctx->content_len = content_len;
ctx->remain = ctx->content; ctx->remain = ctx->content;
ctx->remain_len = ctx->content_len; ctx->remain_len = ctx->content_len;
ctx->line = ctx->remain; ctx->line = ctx->remain;
@ -26,6 +28,7 @@ int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_l
void git_parse_ctx_clear(git_parse_ctx *ctx) void git_parse_ctx_clear(git_parse_ctx *ctx)
{ {
memset(ctx, 0, sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx));
ctx->content = "";
} }
void git_parse_advance_line(git_parse_ctx *ctx) void git_parse_advance_line(git_parse_ctx *ctx)

View File

@ -563,6 +563,8 @@ static int parse_hunk_body(
char c; char c;
int origin; int origin;
int prefix = 1; int prefix = 1;
int old_lineno = hunk->hunk.old_start + (hunk->hunk.old_lines - oldlines);
int new_lineno = hunk->hunk.new_start + (hunk->hunk.new_lines - newlines);
if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') { if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') {
error = git_parse_err("invalid patch instruction at line %"PRIuZ, error = git_parse_err("invalid patch instruction at line %"PRIuZ,
@ -586,11 +588,13 @@ static int parse_hunk_body(
case '-': case '-':
origin = GIT_DIFF_LINE_DELETION; origin = GIT_DIFF_LINE_DELETION;
oldlines--; oldlines--;
new_lineno = -1;
break; break;
case '+': case '+':
origin = GIT_DIFF_LINE_ADDITION; origin = GIT_DIFF_LINE_ADDITION;
newlines--; newlines--;
old_lineno = -1;
break; break;
default: default:
@ -607,6 +611,9 @@ static int parse_hunk_body(
line->content_len = ctx->parse_ctx.line_len - prefix; line->content_len = ctx->parse_ctx.line_len - prefix;
line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len; line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
line->origin = origin; line->origin = origin;
line->num_lines = 1;
line->old_lineno = old_lineno;
line->new_lineno = new_lineno;
hunk->line_count++; hunk->line_count++;
} }

View File

@ -152,7 +152,7 @@ GIT_INLINE(int) rebase_readint(
if ((error = rebase_readfile(asc_out, state_path, filename)) < 0) if ((error = rebase_readfile(asc_out, state_path, filename)) < 0)
return error; return error;
if (git__strtol32(&num, asc_out->ptr, &eol, 10) < 0 || num < 0 || *eol) { if (git__strntol32(&num, asc_out->ptr, asc_out->size, &eol, 10) < 0 || num < 0 || *eol) {
giterr_set(GITERR_REBASE, "the file '%s' contains an invalid numeric value", filename); giterr_set(GITERR_REBASE, "the file '%s' contains an invalid numeric value", filename);
return -1; return -1;
} }

View File

@ -428,7 +428,7 @@ static int get_optional_config(
int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
{ {
git_remote *remote; git_remote *remote = NULL;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
const char *val; const char *val;
int error = 0; int error = 0;
@ -510,7 +510,7 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
goto cleanup; goto cleanup;
if (download_tags_value(remote, config) < 0) if ((error = download_tags_value(remote, config)) < 0)
goto cleanup; goto cleanup;
if ((error = lookup_remote_prune_config(remote, config, name)) < 0) if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
@ -1234,7 +1234,7 @@ int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks)
goto cleanup; goto cleanup;
key.name = (char *) git_buf_cstr(&buf); key.name = (char *) git_buf_cstr(&buf);
error = git_vector_search(&pos, &remote_refs, &key); error = git_vector_bsearch(&pos, &remote_refs, &key);
git_buf_free(&buf); git_buf_free(&buf);
if (error < 0 && error != GIT_ENOTFOUND) if (error < 0 && error != GIT_ENOTFOUND)

View File

@ -268,11 +268,15 @@ static int load_config_data(git_repository *repo, const git_config *config)
{ {
int is_bare; int is_bare;
int err = git_config_get_bool(&is_bare, config, "core.bare");
if (err < 0 && err != GIT_ENOTFOUND)
return err;
/* Try to figure out if it's bare, default to non-bare if it's not set */ /* Try to figure out if it's bare, default to non-bare if it's not set */
if (git_config_get_bool(&is_bare, config, "core.bare") < 0) if (err != GIT_ENOTFOUND)
repo->is_bare = 0; repo->is_bare = is_bare && !repo->is_worktree;
else else
repo->is_bare = is_bare; repo->is_bare = 0;
return 0; return 0;
} }

View File

@ -128,7 +128,8 @@ static int try_parse_numeric(int *n, const char *curly_braces_content)
int32_t content; int32_t content;
const char *end_ptr; const char *end_ptr;
if (git__strtol32(&content, curly_braces_content, &end_ptr, 10) < 0) if (git__strntol32(&content, curly_braces_content, strlen(curly_braces_content),
&end_ptr, 10) < 0)
return -1; return -1;
if (*end_ptr != '\0') if (*end_ptr != '\0')
@ -578,7 +579,7 @@ static int extract_how_many(int *n, const char *spec, size_t *pos)
} while (spec[(*pos)] == kind && kind == '~'); } while (spec[(*pos)] == kind && kind == '~');
if (git__isdigit(spec[*pos])) { if (git__isdigit(spec[*pos])) {
if (git__strtol32(&parsed, spec + *pos, &end_ptr, 10) < 0) if (git__strntol32(&parsed, spec + *pos, strlen(spec + *pos), &end_ptr, 10) < 0)
return GIT_EINVALIDSPEC; return GIT_EINVALIDSPEC;
accumulated += (parsed - 1); accumulated += (parsed - 1);

View File

@ -384,10 +384,16 @@ static int still_interesting(git_commit_list *list, int64_t time, int slop)
if (!list) if (!list)
return 0; return 0;
/*
* If the destination list has commits with an earlier date than our
* source, we want to reset the slop counter as we're not done.
*/
if (time <= list->item->time)
return SLOP;
for (; list; list = list->next) { for (; list; list = list->next) {
/* /*
* If the destination list has commits with an earlier date than * If the destination list still contains interesting commits we
* our source or if it still contains interesting commits we
* want to continue looking. * want to continue looking.
*/ */
if (!list->item->uninteresting || list->item->time > time) if (!list->item->uninteresting || list->item->time > time)
@ -401,7 +407,7 @@ static int still_interesting(git_commit_list *list, int64_t time, int slop)
static int limit_list(git_commit_list **out, git_revwalk *walk, git_commit_list *commits) static int limit_list(git_commit_list **out, git_revwalk *walk, git_commit_list *commits)
{ {
int error, slop = SLOP; int error, slop = SLOP;
int64_t time = ~0ll; int64_t time = INT64_MAX;
git_commit_list *list = commits; git_commit_list *list = commits;
git_commit_list *newlist = NULL; git_commit_list *newlist = NULL;
git_commit_list **p = &newlist; git_commit_list **p = &newlist;
@ -522,7 +528,7 @@ cleanup:
static int prepare_walk(git_revwalk *walk) static int prepare_walk(git_revwalk *walk)
{ {
int error; int error = 0;
git_commit_list *list, *commits = NULL; git_commit_list *list, *commits = NULL;
git_commit_list_node *next; git_commit_list_node *next;

View File

@ -231,7 +231,8 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
const char *time_start = email_end + 2; const char *time_start = email_end + 2;
const char *time_end; const char *time_end;
if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) { if (git__strntol64(&sig->when.time, time_start,
buffer_end - time_start, &time_end, 10) < 0) {
git__free(sig->name); git__free(sig->name);
git__free(sig->email); git__free(sig->email);
sig->name = sig->email = NULL; sig->name = sig->email = NULL;
@ -246,8 +247,9 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
tz_start = time_end + 1; tz_start = time_end + 1;
if ((tz_start[0] != '-' && tz_start[0] != '+') || if ((tz_start[0] != '-' && tz_start[0] != '+') ||
git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0) { git__strntol32(&offset, tz_start + 1,
//malformed timezone, just assume it's zero buffer_end - tz_start + 1, &tz_end, 10) < 0) {
/* malformed timezone, just assume it's zero */
offset = 0; offset = 0;
} }

View File

@ -330,7 +330,7 @@ int git_curl_stream_new(git_stream **out, const char *host, const char *port)
return -1; return -1;
} }
if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) { if ((error = git__strntol32(&iport, port, strlen(port), NULL, 10)) < 0) {
git__free(st); git__free(st);
return error; return error;
} }

View File

@ -1813,6 +1813,14 @@ static int get_value(const char **out, git_config *cfg, git_buf *buf, const char
return error; return error;
} }
static bool looks_like_command_line_option(const char *s)
{
if (s && s[0] == '-')
return true;
return false;
}
static int submodule_read_config(git_submodule *sm, git_config *cfg) static int submodule_read_config(git_submodule *sm, git_config *cfg)
{ {
git_buf key = GIT_BUF_INIT; git_buf key = GIT_BUF_INIT;
@ -1826,24 +1834,31 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg)
if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) { if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) {
in_config = 1; in_config = 1;
/* We would warn here if we had that API */
if (!looks_like_command_line_option(value)) {
/* /*
* TODO: if case insensitive filesystem, then the following strcmp * TODO: if case insensitive filesystem, then the following strcmp
* should be strcasecmp * should be strcasecmp
*/ */
if (strcmp(sm->name, value) != 0) { if (strcmp(sm->name, value) != 0) {
if (sm->path != sm->name) if (sm->path != sm->name)
git__free(sm->path); git__free(sm->path);
sm->path = git__strdup(value); sm->path = git__strdup(value);
GITERR_CHECK_ALLOC(sm->path); GITERR_CHECK_ALLOC(sm->path);
}
} }
} else if (error != GIT_ENOTFOUND) { } else if (error != GIT_ENOTFOUND) {
goto cleanup; goto cleanup;
} }
if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) { if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) {
in_config = 1; /* We would warn here if we had that API */
sm->url = git__strdup(value); if (!looks_like_command_line_option(value)) {
GITERR_CHECK_ALLOC(sm->url); in_config = 1;
sm->url = git__strdup(value);
GITERR_CHECK_ALLOC(sm->url);
}
} else if (error != GIT_ENOTFOUND) { } else if (error != GIT_ENOTFOUND) {
goto cleanup; goto cleanup;
} }

View File

@ -70,10 +70,9 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
static const char *tag_types[] = { static const char *tag_types[] = {
NULL, "commit\n", "tree\n", "blob\n", "tag\n" NULL, "commit\n", "tree\n", "blob\n", "tag\n"
}; };
unsigned int i;
size_t text_len, alloc_len; size_t text_len, alloc_len;
char *search; const char *search;
unsigned int i;
if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0) if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
return tag_error("object field invalid"); return tag_error("object field invalid");
@ -138,8 +137,9 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
tag->message = NULL; tag->message = NULL;
if (buffer < buffer_end) { if (buffer < buffer_end) {
/* If we're not at the end of the header, search for it */ /* If we're not at the end of the header, search for it */
if( *buffer != '\n' ) { if(*buffer != '\n') {
search = strstr(buffer, "\n\n"); search = git__memmem(buffer, buffer_end - buffer,
"\n\n", 2);
if (search) if (search)
buffer = search + 1; buffer = search + 1;
else else

View File

@ -188,6 +188,9 @@ static int apply_credentials(git_buf *buf, http_subtransport *t)
if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0) if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
return -1; return -1;
if (!context)
return 0;
return context->next_token(buf, context, cred); return context->next_token(buf, context, cred);
} }

View File

@ -45,6 +45,11 @@ GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransp
t->current_stream = NULL; t->current_stream = NULL;
} }
if (t->url) {
git__free(t->url);
t->url = NULL;
}
if (close_subtransport && if (close_subtransport &&
t->wrapped->close(t->wrapped) < 0) t->wrapped->close(t->wrapped) < 0)
return -1; return -1;

View File

@ -33,14 +33,14 @@
extern bool git_smart__ofs_delta_enabled; extern bool git_smart__ofs_delta_enabled;
enum git_pkt_type { typedef enum {
GIT_PKT_CMD, GIT_PKT_CMD,
GIT_PKT_FLUSH, GIT_PKT_FLUSH,
GIT_PKT_REF, GIT_PKT_REF,
GIT_PKT_HAVE, GIT_PKT_HAVE,
GIT_PKT_ACK, GIT_PKT_ACK,
GIT_PKT_NAK, GIT_PKT_NAK,
GIT_PKT_PACK, GIT_PKT_PACK__UNUSED,
GIT_PKT_COMMENT, GIT_PKT_COMMENT,
GIT_PKT_ERR, GIT_PKT_ERR,
GIT_PKT_DATA, GIT_PKT_DATA,
@ -48,7 +48,7 @@ enum git_pkt_type {
GIT_PKT_OK, GIT_PKT_OK,
GIT_PKT_NG, GIT_PKT_NG,
GIT_PKT_UNPACK, GIT_PKT_UNPACK,
}; } git_pkt_type;
/* Used for multi_ack and mutli_ack_detailed */ /* Used for multi_ack and mutli_ack_detailed */
enum git_ack_status { enum git_ack_status {
@ -60,11 +60,11 @@ enum git_ack_status {
/* This would be a flush pkt */ /* This would be a flush pkt */
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
} git_pkt; } git_pkt;
struct git_pkt_cmd { struct git_pkt_cmd {
enum git_pkt_type type; git_pkt_type type;
char *cmd; char *cmd;
char *path; char *path;
char *host; char *host;
@ -72,50 +72,50 @@ struct git_pkt_cmd {
/* This is a pkt-line with some info in it */ /* This is a pkt-line with some info in it */
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
git_remote_head head; git_remote_head head;
char *capabilities; char *capabilities;
} git_pkt_ref; } git_pkt_ref;
/* Useful later */ /* Useful later */
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
git_oid oid; git_oid oid;
enum git_ack_status status; enum git_ack_status status;
} git_pkt_ack; } git_pkt_ack;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
char comment[GIT_FLEX_ARRAY]; char comment[GIT_FLEX_ARRAY];
} git_pkt_comment; } git_pkt_comment;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
int len; size_t len;
char data[GIT_FLEX_ARRAY]; char data[GIT_FLEX_ARRAY];
} git_pkt_data; } git_pkt_data;
typedef git_pkt_data git_pkt_progress; typedef git_pkt_data git_pkt_progress;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
int len; size_t len;
char error[GIT_FLEX_ARRAY]; char error[GIT_FLEX_ARRAY];
} git_pkt_err; } git_pkt_err;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
char *ref; char *ref;
} git_pkt_ok; } git_pkt_ok;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
char *ref; char *ref;
char *msg; char *msg;
} git_pkt_ng; } git_pkt_ng;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
int unpack_ok; int unpack_ok;
} git_pkt_unpack; } git_pkt_unpack;
@ -189,7 +189,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream
int git_smart__update_heads(transport_smart *t, git_vector *symrefs); int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
/* smart_pkt.c */ /* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen);
int git_pkt_buffer_flush(git_buf *buf); int git_pkt_buffer_flush(git_buf *buf);
int git_pkt_send_flush(GIT_SOCKET s); int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_buf *buf); int git_pkt_buffer_done(git_buf *buf);

View File

@ -43,34 +43,43 @@ static int flush_pkt(git_pkt **out)
static int ack_pkt(git_pkt **out, const char *line, size_t len) static int ack_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_ack *pkt; git_pkt_ack *pkt;
GIT_UNUSED(line);
GIT_UNUSED(len);
pkt = git__calloc(1, sizeof(git_pkt_ack)); pkt = git__calloc(1, sizeof(git_pkt_ack));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ACK; pkt->type = GIT_PKT_ACK;
line += 3;
len -= 3;
if (len >= GIT_OID_HEXSZ) { if (git__prefixncmp(line, len, "ACK "))
git_oid_fromstr(&pkt->oid, line + 1); goto out_err;
line += GIT_OID_HEXSZ + 1; line += 4;
len -= GIT_OID_HEXSZ + 1; len -= 4;
}
if (len >= 7) { if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->oid, line) < 0)
if (!git__prefixcmp(line + 1, "continue")) goto out_err;
line += GIT_OID_HEXSZ;
len -= GIT_OID_HEXSZ;
if (len && line[0] == ' ') {
line++;
len--;
if (!git__prefixncmp(line, len, "continue"))
pkt->status = GIT_ACK_CONTINUE; pkt->status = GIT_ACK_CONTINUE;
if (!git__prefixcmp(line + 1, "common")) else if (!git__prefixncmp(line, len, "common"))
pkt->status = GIT_ACK_COMMON; pkt->status = GIT_ACK_COMMON;
if (!git__prefixcmp(line + 1, "ready")) else if (!git__prefixncmp(line, len, "ready"))
pkt->status = GIT_ACK_READY; pkt->status = GIT_ACK_READY;
else
goto out_err;
} }
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing ACK pkt-line");
git__free(pkt);
return -1;
} }
static int nak_pkt(git_pkt **out) static int nak_pkt(git_pkt **out)
@ -86,19 +95,6 @@ static int nak_pkt(git_pkt **out)
return 0; return 0;
} }
static int pack_pkt(git_pkt **out)
{
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_PACK;
*out = pkt;
return 0;
}
static int comment_pkt(git_pkt **out, const char *line, size_t len) static int comment_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_comment *pkt; git_pkt_comment *pkt;
@ -120,10 +116,12 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
static int err_pkt(git_pkt **out, const char *line, size_t len) static int err_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_err *pkt; git_pkt_err *pkt = NULL;
size_t alloclen; size_t alloclen;
/* Remove "ERR " from the line */ /* Remove "ERR " from the line */
if (git__prefixncmp(line, len, "ERR "))
goto out_err;
line += 4; line += 4;
len -= 4; len -= 4;
@ -131,15 +129,20 @@ static int err_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
pkt = git__malloc(alloclen); pkt = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ERR; pkt->type = GIT_PKT_ERR;
pkt->len = (int)len; pkt->len = len;
memcpy(pkt->error, line, len); memcpy(pkt->error, line, len);
pkt->error[len] = '\0'; pkt->error[len] = '\0';
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing ERR pkt-line");
git__free(pkt);
return -1;
} }
static int data_pkt(git_pkt **out, const char *line, size_t len) static int data_pkt(git_pkt **out, const char *line, size_t len)
@ -155,7 +158,7 @@ static int data_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_DATA; pkt->type = GIT_PKT_DATA;
pkt->len = (int) len; pkt->len = len;
memcpy(pkt->data, line, len); memcpy(pkt->data, line, len);
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
@ -176,7 +179,7 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_PROGRESS; pkt->type = GIT_PKT_PROGRESS;
pkt->len = (int) len; pkt->len = len;
memcpy(pkt->data, line, len); memcpy(pkt->data, line, len);
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
@ -212,28 +215,25 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
*/ */
static int ref_pkt(git_pkt **out, const char *line, size_t len) static int ref_pkt(git_pkt **out, const char *line, size_t len)
{ {
int error;
git_pkt_ref *pkt; git_pkt_ref *pkt;
size_t alloclen; size_t alloclen;
pkt = git__malloc(sizeof(git_pkt_ref)); pkt = git__calloc(1, sizeof(git_pkt_ref));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
memset(pkt, 0x0, sizeof(git_pkt_ref));
pkt->type = GIT_PKT_REF; pkt->type = GIT_PKT_REF;
if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)
goto error_out;
/* Check for a bit of consistency */ if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->head.oid, line) < 0)
if (line[GIT_OID_HEXSZ] != ' ') { goto out_err;
giterr_set(GITERR_NET, "error parsing pkt-line"); line += GIT_OID_HEXSZ;
error = -1; len -= GIT_OID_HEXSZ;
goto error_out;
}
/* Jump from the name */ if (git__prefixncmp(line, len, " "))
line += GIT_OID_HEXSZ + 1; goto out_err;
len -= (GIT_OID_HEXSZ + 1); line++;
len--;
if (!len)
goto out_err;
if (line[len - 1] == '\n') if (line[len - 1] == '\n')
--len; --len;
@ -245,36 +245,36 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
memcpy(pkt->head.name, line, len); memcpy(pkt->head.name, line, len);
pkt->head.name[len] = '\0'; pkt->head.name[len] = '\0';
if (strlen(pkt->head.name) < len) { if (strlen(pkt->head.name) < len)
pkt->capabilities = strchr(pkt->head.name, '\0') + 1; pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
}
*out = (git_pkt *)pkt; *out = (git_pkt *)pkt;
return 0; return 0;
error_out: out_err:
giterr_set(GITERR_NET, "error parsing REF pkt-line");
if (pkt)
git__free(pkt->head.name);
git__free(pkt); git__free(pkt);
return error; return -1;
} }
static int ok_pkt(git_pkt **out, const char *line, size_t len) static int ok_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_ok *pkt; git_pkt_ok *pkt;
const char *ptr;
size_t alloc_len; size_t alloc_len;
pkt = git__malloc(sizeof(*pkt)); pkt = git__malloc(sizeof(*pkt));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_OK; pkt->type = GIT_PKT_OK;
line += 3; /* skip "ok " */ if (git__prefixncmp(line, len, "ok "))
if (!(ptr = strchr(line, '\n'))) { goto out_err;
giterr_set(GITERR_NET, "invalid packet line"); line += 3;
git__free(pkt); len -= 3;
return -1;
} if (line[len - 1] == '\n')
len = ptr - line; --len;
GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1); GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
pkt->ref = git__malloc(alloc_len); pkt->ref = git__malloc(alloc_len);
@ -285,12 +285,17 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len)
*out = (git_pkt *)pkt; *out = (git_pkt *)pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing OK pkt-line");
git__free(pkt);
return -1;
} }
static int ng_pkt(git_pkt **out, const char *line, size_t len) static int ng_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_ng *pkt; git_pkt_ng *pkt;
const char *ptr; const char *ptr, *eol;
size_t alloclen; size_t alloclen;
pkt = git__malloc(sizeof(*pkt)); pkt = git__malloc(sizeof(*pkt));
@ -299,11 +304,13 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len)
pkt->ref = NULL; pkt->ref = NULL;
pkt->type = GIT_PKT_NG; pkt->type = GIT_PKT_NG;
if (len < 3) eol = line + len;
if (git__prefixncmp(line, len, "ng "))
goto out_err; goto out_err;
line += 3; /* skip "ng " */ line += 3;
len -= 3;
if (!(ptr = memchr(line, ' ', len))) if (!(ptr = memchr(line, ' ', eol - line)))
goto out_err; goto out_err;
len = ptr - line; len = ptr - line;
@ -314,11 +321,11 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len)
memcpy(pkt->ref, line, len); memcpy(pkt->ref, line, len);
pkt->ref[len] = '\0'; pkt->ref[len] = '\0';
if (len < 1)
goto out_err;
line = ptr + 1; line = ptr + 1;
len -= 1; if (line >= eol)
if (!(ptr = memchr(line, '\n', len))) goto out_err;
if (!(ptr = memchr(line, '\n', eol - line)))
goto out_err; goto out_err;
len = ptr - line; len = ptr - line;
@ -343,13 +350,11 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_unpack *pkt; git_pkt_unpack *pkt;
GIT_UNUSED(len);
pkt = git__malloc(sizeof(*pkt)); pkt = git__malloc(sizeof(*pkt));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_UNPACK; pkt->type = GIT_PKT_UNPACK;
if (!git__prefixcmp(line, "unpack ok"))
if (!git__prefixncmp(line, len, "unpack ok"))
pkt->unpack_ok = 1; pkt->unpack_ok = 1;
else else
pkt->unpack_ok = 0; pkt->unpack_ok = 0;
@ -358,13 +363,17 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len)
return 0; return 0;
} }
static int32_t parse_len(const char *line) static int parse_len(size_t *out, const char *line, size_t linelen)
{ {
char num[PKT_LEN_SIZE + 1]; char num[PKT_LEN_SIZE + 1];
int i, k, error; int i, k, error;
int32_t len; int32_t len;
const char *num_end; const char *num_end;
/* Not even enough for the length */
if (linelen < PKT_LEN_SIZE)
return GIT_EBUFS;
memcpy(num, line, PKT_LEN_SIZE); memcpy(num, line, PKT_LEN_SIZE);
num[PKT_LEN_SIZE] = '\0'; num[PKT_LEN_SIZE] = '\0';
@ -376,16 +385,20 @@ static int32_t parse_len(const char *line)
num[k] = '.'; num[k] = '.';
} }
} }
giterr_set(GITERR_NET, "invalid hex digit in length: '%s'", num); giterr_set(GITERR_NET, "invalid hex digit in length: '%s'", num);
return -1; return -1;
} }
} }
if ((error = git__strtol32(&len, num, &num_end, 16)) < 0) if ((error = git__strntol32(&len, num, PKT_LEN_SIZE, &num_end, 16)) < 0)
return error; return error;
return len; if (len < 0)
return -1;
*out = (size_t) len;
return 0;
} }
/* /*
@ -402,35 +415,32 @@ static int32_t parse_len(const char *line)
*/ */
int git_pkt_parse_line( int git_pkt_parse_line(
git_pkt **head, const char *line, const char **out, size_t bufflen) git_pkt **pkt, const char **endptr, const char *line, size_t linelen)
{ {
int ret; int error;
int32_t len; size_t len;
/* Not even enough for the length */ if ((error = parse_len(&len, line, linelen)) < 0) {
if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
return GIT_EBUFS;
len = parse_len(line);
if (len < 0) {
/* /*
* If we fail to parse the length, it might be because the * If we fail to parse the length, it might be
* server is trying to send us the packfile already. * because the server is trying to send us the
* packfile already or because we do not yet have
* enough data.
*/ */
if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) { if (error == GIT_EBUFS)
giterr_clear(); ;
*out = line; else if (!git__prefixncmp(line, linelen, "PACK"))
return pack_pkt(head); giterr_set(GITERR_NET, "unexpected pack file");
} else
giterr_set(GITERR_NET, "bad packet length");
return (int)len; return error;
} }
/* /*
* If we were given a buffer length, then make sure there is * Make sure there is enough in the buffer to satisfy
* enough in the buffer to satisfy this line * this line.
*/ */
if (bufflen > 0 && bufflen < (size_t)len) if (linelen < len)
return GIT_EBUFS; return GIT_EBUFS;
/* /*
@ -453,38 +463,38 @@ int git_pkt_parse_line(
} }
if (len == 0) { /* Flush pkt */ if (len == 0) { /* Flush pkt */
*out = line; *endptr = line;
return flush_pkt(head); return flush_pkt(pkt);
} }
len -= PKT_LEN_SIZE; /* the encoded length includes its own size */ len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
if (*line == GIT_SIDE_BAND_DATA) if (*line == GIT_SIDE_BAND_DATA)
ret = data_pkt(head, line, len); error = data_pkt(pkt, line, len);
else if (*line == GIT_SIDE_BAND_PROGRESS) else if (*line == GIT_SIDE_BAND_PROGRESS)
ret = sideband_progress_pkt(head, line, len); error = sideband_progress_pkt(pkt, line, len);
else if (*line == GIT_SIDE_BAND_ERROR) else if (*line == GIT_SIDE_BAND_ERROR)
ret = sideband_error_pkt(head, line, len); error = sideband_error_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ACK")) else if (!git__prefixncmp(line, len, "ACK"))
ret = ack_pkt(head, line, len); error = ack_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "NAK")) else if (!git__prefixncmp(line, len, "NAK"))
ret = nak_pkt(head); error = nak_pkt(pkt);
else if (!git__prefixcmp(line, "ERR ")) else if (!git__prefixncmp(line, len, "ERR"))
ret = err_pkt(head, line, len); error = err_pkt(pkt, line, len);
else if (*line == '#') else if (*line == '#')
ret = comment_pkt(head, line, len); error = comment_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ok")) else if (!git__prefixncmp(line, len, "ok"))
ret = ok_pkt(head, line, len); error = ok_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ng")) else if (!git__prefixncmp(line, len, "ng"))
ret = ng_pkt(head, line, len); error = ng_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "unpack")) else if (!git__prefixncmp(line, len, "unpack"))
ret = unpack_pkt(head, line, len); error = unpack_pkt(pkt, line, len);
else else
ret = ref_pkt(head, line, len); error = ref_pkt(pkt, line, len);
*out = line + len; *endptr = line + len;
return ret; return error;
} }
void git_pkt_free(git_pkt *pkt) void git_pkt_free(git_pkt *pkt)

View File

@ -44,7 +44,7 @@ int git_smart__store_refs(transport_smart *t, int flushes)
do { do {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset); error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;
@ -209,15 +209,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
return 0; return 0;
} }
static int recv_pkt(git_pkt **out, gitno_buffer *buf) static int recv_pkt(git_pkt **out, git_pkt_type *pkt_type, gitno_buffer *buf)
{ {
const char *ptr = buf->data, *line_end = ptr; const char *ptr = buf->data, *line_end = ptr;
git_pkt *pkt = NULL; git_pkt *pkt = NULL;
int pkt_type, error = 0, ret; int error = 0, ret;
do { do {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;
@ -236,13 +236,14 @@ static int recv_pkt(git_pkt **out, gitno_buffer *buf)
} while (error); } while (error);
gitno_consume(buf, line_end); gitno_consume(buf, line_end);
pkt_type = pkt->type; if (pkt_type)
*pkt_type = pkt->type;
if (out != NULL) if (out != NULL)
*out = pkt; *out = pkt;
else else
git__free(pkt); git__free(pkt);
return pkt_type; return error;
} }
static int store_common(transport_smart *t) static int store_common(transport_smart *t)
@ -252,7 +253,7 @@ static int store_common(transport_smart *t)
int error; int error;
do { do {
if ((error = recv_pkt(&pkt, buf)) < 0) if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
return error; return error;
if (pkt->type == GIT_PKT_ACK) { if (pkt->type == GIT_PKT_ACK) {
@ -320,7 +321,7 @@ static int wait_while_ack(gitno_buffer *buf)
while (1) { while (1) {
git__free(pkt); git__free(pkt);
if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0) if ((error = recv_pkt((git_pkt **)&pkt, NULL, buf)) < 0)
return error; return error;
if (pkt->type == GIT_PKT_NAK) if (pkt->type == GIT_PKT_NAK)
@ -345,7 +346,8 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
gitno_buffer *buf = &t->buffer; gitno_buffer *buf = &t->buffer;
git_buf data = GIT_BUF_INIT; git_buf data = GIT_BUF_INIT;
git_revwalk *walk = NULL; git_revwalk *walk = NULL;
int error = -1, pkt_type; int error = -1;
git_pkt_type pkt_type;
unsigned int i; unsigned int i;
git_oid oid; git_oid oid;
@ -395,16 +397,13 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
if ((error = store_common(t)) < 0) if ((error = store_common(t)) < 0)
goto on_error; goto on_error;
} else { } else {
pkt_type = recv_pkt(NULL, buf); error = recv_pkt(NULL, &pkt_type, buf);
if (error < 0) {
if (pkt_type == GIT_PKT_ACK) { goto on_error;
} else if (pkt_type == GIT_PKT_ACK) {
break; break;
} else if (pkt_type == GIT_PKT_NAK) { } else if (pkt_type == GIT_PKT_NAK) {
continue; continue;
} else if (pkt_type < 0) {
/* recv_pkt returned an error */
error = pkt_type;
goto on_error;
} else { } else {
giterr_set(GITERR_NET, "Unexpected pkt type"); giterr_set(GITERR_NET, "Unexpected pkt type");
error = -1; error = -1;
@ -470,10 +469,10 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
/* Now let's eat up whatever the server gives us */ /* Now let's eat up whatever the server gives us */
if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) { if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
pkt_type = recv_pkt(NULL, buf); error = recv_pkt(NULL, &pkt_type, buf);
if (pkt_type < 0) { if (error < 0) {
return pkt_type; return error;
} else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) { } else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
giterr_set(GITERR_NET, "Unexpected pkt type"); giterr_set(GITERR_NET, "Unexpected pkt type");
return -1; return -1;
@ -594,7 +593,7 @@ int git_smart__download_pack(
goto done; goto done;
} }
if ((error = recv_pkt(&pkt, buf)) >= 0) { if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) {
/* Check cancellation after network call */ /* Check cancellation after network call */
if (t->cancelled.val) { if (t->cancelled.val) {
giterr_clear(); giterr_clear();
@ -752,7 +751,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt,
} }
while (line_len > 0) { while (line_len > 0) {
error = git_pkt_parse_line(&pkt, line, &line_end, line_len); error = git_pkt_parse_line(&pkt, &line_end, line, line_len);
if (error == GIT_EBUFS) { if (error == GIT_EBUFS) {
/* Buffer the data when the inner packet is split /* Buffer the data when the inner packet is split
@ -795,8 +794,8 @@ static int parse_report(transport_smart *transport, git_push *push)
for (;;) { for (;;) {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, buf->data, error = git_pkt_parse_line(&pkt, &line_end,
&line_end, buf->offset); buf->data, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;

View File

@ -761,7 +761,8 @@ static int winhttp_connect(
t->connection = NULL; t->connection = NULL;
/* Prepare port */ /* Prepare port */
if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0) if (git__strntol32(&port, t->connection_data.port,
strlen(t->connection_data.port), NULL, 10) < 0)
return -1; return -1;
/* Prepare host */ /* Prepare host */
@ -845,23 +846,27 @@ on_error:
static int do_send_request(winhttp_stream *s, size_t len, int ignore_length) static int do_send_request(winhttp_stream *s, size_t len, int ignore_length)
{ {
if (ignore_length) { int attempts;
if (!WinHttpSendRequest(s->request, bool success;
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, for (attempts = 0; attempts < 5; attempts++) {
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) { if (ignore_length) {
return -1; success = WinHttpSendRequest(s->request,
} WINHTTP_NO_ADDITIONAL_HEADERS, 0,
} else { WINHTTP_NO_REQUEST_DATA, 0,
if (!WinHttpSendRequest(s->request, WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0);
WINHTTP_NO_ADDITIONAL_HEADERS, 0, } else {
WINHTTP_NO_REQUEST_DATA, 0, success = WinHttpSendRequest(s->request,
len, 0)) { WINHTTP_NO_ADDITIONAL_HEADERS, 0,
return -1; WINHTTP_NO_REQUEST_DATA, 0,
len, 0);
} }
if (success || GetLastError() != SEC_E_BUFFER_TOO_SMALL)
break;
} }
return 0; return success ? 0 : -1;
} }
static int send_request(winhttp_stream *s, size_t len, int ignore_length) static int send_request(winhttp_stream *s, size_t len, int ignore_length)

View File

@ -91,7 +91,7 @@ static int read_tree_internal(git_tree_cache **out,
return -1; return -1;
/* Blank-terminated ASCII decimal number of entries in this tree */ /* Blank-terminated ASCII decimal number of entries in this tree */
if (git__strtol32(&count, buffer, &buffer, 10) < 0) if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0)
goto corrupted; goto corrupted;
tree->entry_count = count; tree->entry_count = count;
@ -100,7 +100,7 @@ static int read_tree_internal(git_tree_cache **out,
goto corrupted; goto corrupted;
/* Number of children of the tree, newline-terminated */ /* Number of children of the tree, newline-terminated */
if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < 0) if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0 || count < 0)
goto corrupted; goto corrupted;
tree->children_count = count; tree->children_count = count;

View File

@ -490,15 +490,16 @@ static int append_entry(
git_treebuilder *bld, git_treebuilder *bld,
const char *filename, const char *filename,
const git_oid *id, const git_oid *id,
git_filemode_t filemode) git_filemode_t filemode,
bool validate)
{ {
git_tree_entry *entry; git_tree_entry *entry;
int error = 0; int error = 0;
if (!valid_entry_name(bld->repo, filename)) if (validate && !valid_entry_name(bld->repo, filename))
return tree_error("failed to insert entry: invalid name for a tree entry", filename); return tree_error("failed to insert entry: invalid name for a tree entry", filename);
if (git_oid_iszero(id)) if (validate && git_oid_iszero(id))
return tree_error("failed to insert entry: invalid null OID for a tree entry", filename); return tree_error("failed to insert entry: invalid null OID for a tree entry", filename);
entry = alloc_entry(filename, strlen(filename), id); entry = alloc_entry(filename, strlen(filename), id);
@ -596,12 +597,12 @@ static int write_tree(
last_comp = subdir; last_comp = subdir;
} }
error = append_entry(bld, last_comp, &sub_oid, S_IFDIR); error = append_entry(bld, last_comp, &sub_oid, S_IFDIR, true);
git__free(subdir); git__free(subdir);
if (error < 0) if (error < 0)
goto on_error; goto on_error;
} else { } else {
error = append_entry(bld, filename, &entry->id, entry->mode); error = append_entry(bld, filename, &entry->id, entry->mode, true);
if (error < 0) if (error < 0)
goto on_error; goto on_error;
} }
@ -699,7 +700,8 @@ int git_treebuilder_new(
if (append_entry( if (append_entry(
bld, entry_src->filename, bld, entry_src->filename,
entry_src->oid, entry_src->oid,
entry_src->attr) < 0) entry_src->attr,
false) < 0)
goto on_error; goto on_error;
} }
} }

View File

@ -68,12 +68,6 @@ int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
return 0; return 0;
} }
int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int base)
{
return git__strntol64(result, nptr, (size_t)-1, endptr, base);
}
int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base) int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
{ {
const char *p; const char *p;
@ -132,10 +126,20 @@ int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const cha
v = c - 'A' + 10; v = c - 'A' + 10;
if (v >= base) if (v >= base)
break; break;
nn = n * base + (neg ? -v : v); v = neg ? -v : v;
if ((!neg && nn < n) || (neg && nn > n)) if (n > INT64_MAX / base || n < INT64_MIN / base) {
ovfl = 1; ovfl = 1;
n = nn; /* Keep on iterating until the end of this number */
continue;
}
nn = n * base;
if ((v > 0 && nn > INT64_MAX - v) ||
(v < 0 && nn < INT64_MIN - v)) {
ovfl = 1;
/* Keep on iterating until the end of this number */
continue;
}
n = nn + v;
} }
Return: Return:
@ -156,28 +160,26 @@ Return:
return 0; return 0;
} }
int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base)
{
return git__strntol32(result, nptr, (size_t)-1, endptr, base);
}
int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base) int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
{ {
int error; const char *tmp_endptr;
int32_t tmp_int; int32_t tmp_int;
int64_t tmp_long; int64_t tmp_long;
int error;
if ((error = git__strntol64(&tmp_long, nptr, nptr_len, endptr, base)) < 0) if ((error = git__strntol64(&tmp_long, nptr, nptr_len, &tmp_endptr, base)) < 0)
return error; return error;
tmp_int = tmp_long & 0xFFFFFFFF; tmp_int = tmp_long & 0xFFFFFFFF;
if (tmp_int != tmp_long) { if (tmp_int != tmp_long) {
giterr_set(GITERR_INVALID, "failed to convert: '%s' is too large", nptr); int len = tmp_endptr - nptr;
giterr_set(GITERR_INVALID, "failed to convert: '%.*s' is too large", len, nptr);
return -1; return -1;
} }
*result = tmp_int; *result = tmp_int;
if (endptr)
*endptr = tmp_endptr;
return error; return error;
} }
@ -355,6 +357,47 @@ size_t git__linenlen(const char *buffer, size_t buffer_len)
return nl ? (size_t)(nl - buffer) + 1 : buffer_len; return nl ? (size_t)(nl - buffer) + 1 : buffer_len;
} }
/*
* Adapted Not So Naive algorithm from http://www-igm.univ-mlv.fr/~lecroq/string/
*/
const void * git__memmem(const void *haystack, size_t haystacklen,
const void *needle, size_t needlelen)
{
const char *h, *n;
size_t j, k, l;
if (needlelen > haystacklen || !haystacklen || !needlelen)
return NULL;
h = (const char *) haystack,
n = (const char *) needle;
if (needlelen == 1)
return memchr(haystack, *n, haystacklen);
if (n[0] == n[1]) {
k = 2;
l = 1;
} else {
k = 1;
l = 2;
}
j = 0;
while (j <= haystacklen - needlelen) {
if (n[1] != h[j + 1]) {
j += k;
} else {
if (memcmp(n + 2, h + j + 2, needlelen - 2) == 0 &&
n[0] == h[j])
return h + j;
j += l;
}
}
return NULL;
}
void git__hexdump(const char *buffer, size_t len) void git__hexdump(const char *buffer, size_t len)
{ {
static const size_t LINE_WIDTH = 16; static const size_t LINE_WIDTH = 16;
@ -647,7 +690,7 @@ size_t git__unescape(char *str)
return (pos - str); return (pos - str);
} }
#if defined(HAVE_QSORT_S) || (defined(HAVE_QSORT_R) && defined(BSD)) #if defined(HAVE_QSORT_S) || defined(HAVE_QSORT_R_BSD)
typedef struct { typedef struct {
git__sort_r_cmp cmp; git__sort_r_cmp cmp;
void *payload; void *payload;
@ -664,10 +707,10 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
void git__qsort_r( void git__qsort_r(
void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
{ {
#if defined(HAVE_QSORT_R) && defined(BSD) #if defined(HAVE_QSORT_R_BSD)
git__qsort_r_glue glue = { cmp, payload }; git__qsort_r_glue glue = { cmp, payload };
qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp); qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
#elif defined(HAVE_QSORT_R) && defined(__GLIBC__) #elif defined(HAVE_QSORT_R_GNU)
qsort_r(els, nel, elsize, cmp, payload); qsort_r(els, nel, elsize, cmp, payload);
#elif defined(HAVE_QSORT_S) #elif defined(HAVE_QSORT_S)
git__qsort_r_glue glue = { cmp, payload }; git__qsort_r_glue glue = { cmp, payload };

View File

@ -193,9 +193,7 @@ GIT_INLINE(int) git__signum(int val)
return ((val > 0) - (val < 0)); return ((val > 0) - (val < 0));
} }
extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);
extern int git__strntol32(int32_t *n, const char *buff, size_t buff_len, const char **end_buf, int base); extern int git__strntol32(int32_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base);
extern int git__strntol64(int64_t *n, const char *buff, size_t buff_len, const char **end_buf, int base); extern int git__strntol64(int64_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
@ -248,6 +246,9 @@ GIT_INLINE(const void *) git__memrchr(const void *s, int c, size_t n)
return NULL; return NULL;
} }
extern const void * git__memmem(const void *haystack, size_t haystacklen,
const void *needle, size_t needlelen);
typedef int (*git__tsort_cmp)(const void *a, const void *b); typedef int (*git__tsort_cmp)(const void *a, const void *b);
extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp); extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp);

View File

@ -32,6 +32,9 @@ GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size)
{ {
void *new_contents; void *new_contents;
if (new_size == 0)
return 0;
new_contents = git__reallocarray(v->contents, new_size, sizeof(void *)); new_contents = git__reallocarray(v->contents, new_size, sizeof(void *));
GITERR_CHECK_ALLOC(new_contents); GITERR_CHECK_ALLOC(new_contents);
@ -50,22 +53,24 @@ int git_vector_size_hint(git_vector *v, size_t size_hint)
int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp) int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
{ {
size_t bytes;
assert(v && src); assert(v && src);
GITERR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *)); v->_alloc_size = 0;
v->contents = NULL;
v->_alloc_size = src->length;
v->_cmp = cmp ? cmp : src->_cmp; v->_cmp = cmp ? cmp : src->_cmp;
v->length = src->length; v->length = src->length;
v->flags = src->flags; v->flags = src->flags;
if (cmp != src->_cmp) if (cmp != src->_cmp)
git_vector_set_sorted(v, 0); git_vector_set_sorted(v, 0);
v->contents = git__malloc(bytes);
GITERR_CHECK_ALLOC(v->contents);
memcpy(v->contents, src->contents, bytes); if (src->length) {
size_t bytes;
GITERR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *));
v->contents = git__malloc(bytes);
GITERR_CHECK_ALLOC(v->contents);
v->_alloc_size = src->length;
memcpy(v->contents, src->contents, bytes);
}
return 0; return 0;
} }

View File

@ -139,7 +139,7 @@ static int open_worktree_dir(git_worktree **out, const char *parent, const char
if ((wt->name = git__strdup(name)) == NULL if ((wt->name = git__strdup(name)) == NULL
|| (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL || (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL
|| (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL
|| (wt->parent_path = git__strdup(parent)) == NULL) { || (parent && (wt->parent_path = git__strdup(parent)) == NULL)) {
error = -1; error = -1;
goto out; goto out;
} }
@ -417,7 +417,7 @@ int git_worktree_unlock(git_worktree *wt)
assert(wt); assert(wt);
if (!git_worktree_is_locked(NULL, wt)) if (!git_worktree_is_locked(NULL, wt))
return 0; return 1;
if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0) if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0)
return -1; return -1;

View File

@ -52,12 +52,8 @@ IF (MSVC_IDE)
SET_SOURCE_FILES_PROPERTIES("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h") SET_SOURCE_FILES_PROPERTIES("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h")
ENDIF () ENDIF ()
IF (USE_HTTPS) ADD_TEST(offline "${libgit2_BINARY_DIR}/libgit2_clar" -v -xonline)
ADD_TEST(libgit2_clar "${libgit2_BINARY_DIR}/libgit2_clar" -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style) ADD_TEST(online "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline)
ELSE () ADD_TEST(gitdaemon "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::push)
ADD_TEST(libgit2_clar "${libgit2_BINARY_DIR}/libgit2_clar" -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style) ADD_TEST(ssh "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths)
ENDIF () ADD_TEST(proxy "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::clone::proxy_credentials_in_url -sonline::clone::proxy_credentials_request)
# Add additional test targets that require special setup
ADD_TEST(libgit2_clar-proxy_credentials "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::clone::proxy_credentials_in_url -sonline::clone::proxy_credentials_request)
ADD_TEST(libgit2_clar-ssh "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths)

View File

@ -1,10 +1,22 @@
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "buffer.h" #include "buffer.h"
#if defined(GIT_ARCH_64) /*
#define TOOBIG 0xffffffffffffff00 * We want to use some ridiculous size that `malloc` will fail with
* but that does not otherwise interfere with testing. On Linux, choose
* a number that is large enough to fail immediately but small enough
* that valgrind doesn't believe it to erroneously be a negative number.
* On macOS, choose a number that is large enough to fail immediately
* without having libc print warnings to stderr.
*/
#if defined(GIT_ARCH_64) && defined(__linux__)
# define TOOBIG 0x0fffffffffffffff
#elif defined(__linux__)
# define TOOBIG 0x0fffffff
#elif defined(GIT_ARCH_64)
# define TOOBIG 0xffffffffffffff00
#else #else
#define TOOBIG 0xffffff00 # define TOOBIG 0xffffff00
#endif #endif
/** /**

View File

@ -95,9 +95,6 @@ static const char *
fixture_path(const char *base, const char *fixture_name); fixture_path(const char *base, const char *fixture_name);
struct clar_error { struct clar_error {
const char *test;
int test_number;
const char *suite;
const char *file; const char *file;
int line_number; int line_number;
const char *error_msg; const char *error_msg;
@ -106,11 +103,34 @@ struct clar_error {
struct clar_error *next; struct clar_error *next;
}; };
static struct { struct clar_explicit {
int argc; size_t suite_idx;
char **argv; const char *filter;
struct clar_explicit *next;
};
struct clar_report {
const char *test;
int test_number;
const char *suite;
enum cl_test_status status;
struct clar_error *errors;
struct clar_error *last_error;
struct clar_report *next;
};
struct clar_summary {
const char *filename;
FILE *fp;
};
static struct {
enum cl_test_status test_status; enum cl_test_status test_status;
const char *active_test; const char *active_test;
const char *active_suite; const char *active_suite;
@ -124,8 +144,15 @@ static struct {
int exit_on_error; int exit_on_error;
int report_suite_names; int report_suite_names;
struct clar_error *errors; int write_summary;
struct clar_error *last_error; const char *summary_filename;
struct clar_summary *summary;
struct clar_explicit *explicit;
struct clar_explicit *last_explicit;
struct clar_report *reports;
struct clar_report *last_report;
void (*local_cleanup)(void *); void (*local_cleanup)(void *);
void *local_cleanup_payload; void *local_cleanup_payload;
@ -155,7 +182,7 @@ struct clar_suite {
/* From clar_print_*.c */ /* From clar_print_*.c */
static void clar_print_init(int test_count, int suite_count, const char *suite_names); static void clar_print_init(int test_count, int suite_count, const char *suite_names);
static void clar_print_shutdown(int test_count, int suite_count, int error_count); static void clar_print_shutdown(int test_count, int suite_count, int error_count);
static void clar_print_error(int num, const struct clar_error *error); static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error);
static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status failed); static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status failed);
static void clar_print_onsuite(const char *suite_name, int suite_index); static void clar_print_onsuite(const char *suite_name, int suite_index);
static void clar_print_onabort(const char *msg, ...); static void clar_print_onabort(const char *msg, ...);
@ -164,6 +191,10 @@ static void clar_print_onabort(const char *msg, ...);
static void clar_unsandbox(void); static void clar_unsandbox(void);
static int clar_sandbox(void); static int clar_sandbox(void);
/* From summary.h */
static struct clar_summary *clar_summary_init(const char *filename);
static int clar_summary_shutdown(struct clar_summary *fp);
/* Load the declarations for the test suite */ /* Load the declarations for the test suite */
#include "clar.suite" #include "clar.suite"
@ -186,21 +217,29 @@ void cl_trace_register(cl_trace_cb *cb, void *payload)
/* Core test functions */ /* Core test functions */
static void static void
clar_report_errors(void) clar_report_errors(struct clar_report *report)
{ {
struct clar_error *error;
int i = 1; int i = 1;
struct clar_error *error, *next;
error = _clar.errors; for (error = report->errors; error; error = error->next)
while (error != NULL) { clar_print_error(i++, _clar.last_report, error);
next = error->next; }
clar_print_error(i++, error);
free(error->description); static void
free(error); clar_report_all(void)
error = next; {
struct clar_report *report;
struct clar_error *error;
int i = 1;
for (report = _clar.reports; report; report = report->next) {
if (report->status != CL_TEST_FAILURE)
continue;
for (error = report->errors; error; error = error->next)
clar_print_error(i++, report, error);
} }
_clar.errors = _clar.last_error = NULL;
} }
static void static void
@ -209,7 +248,6 @@ clar_run_test(
const struct clar_func *initialize, const struct clar_func *initialize,
const struct clar_func *cleanup) const struct clar_func *cleanup)
{ {
_clar.test_status = CL_TEST_OK;
_clar.trampoline_enabled = 1; _clar.trampoline_enabled = 1;
CL_TRACE(CL_TRACE__TEST__BEGIN); CL_TRACE(CL_TRACE__TEST__BEGIN);
@ -225,6 +263,9 @@ clar_run_test(
_clar.trampoline_enabled = 0; _clar.trampoline_enabled = 0;
if (_clar.last_report->status == CL_TEST_NOTRUN)
_clar.last_report->status = CL_TEST_OK;
if (_clar.local_cleanup != NULL) if (_clar.local_cleanup != NULL)
_clar.local_cleanup(_clar.local_cleanup_payload); _clar.local_cleanup(_clar.local_cleanup_payload);
@ -240,9 +281,9 @@ clar_run_test(
_clar.local_cleanup_payload = NULL; _clar.local_cleanup_payload = NULL;
if (_clar.report_errors_only) { if (_clar.report_errors_only) {
clar_report_errors(); clar_report_errors(_clar.last_report);
} else { } else {
clar_print_ontest(test->name, _clar.tests_ran, _clar.test_status); clar_print_ontest(test->name, _clar.tests_ran, _clar.last_report->status);
} }
} }
@ -251,6 +292,7 @@ clar_run_suite(const struct clar_suite *suite, const char *filter)
{ {
const struct clar_func *test = suite->tests; const struct clar_func *test = suite->tests;
size_t i, matchlen; size_t i, matchlen;
struct clar_report *report;
if (!suite->enabled) if (!suite->enabled)
return; return;
@ -283,6 +325,21 @@ clar_run_suite(const struct clar_suite *suite, const char *filter)
continue; continue;
_clar.active_test = test[i].name; _clar.active_test = test[i].name;
report = calloc(1, sizeof(struct clar_report));
report->suite = _clar.active_suite;
report->test = _clar.active_test;
report->test_number = _clar.tests_ran;
report->status = CL_TEST_NOTRUN;
if (_clar.reports == NULL)
_clar.reports = report;
if (_clar.last_report != NULL)
_clar.last_report->next = report;
_clar.last_report = report;
clar_run_test(&test[i], &suite->initialize, &suite->cleanup); clar_run_test(&test[i], &suite->initialize, &suite->cleanup);
if (_clar.exit_on_error && _clar.total_errors) if (_clar.exit_on_error && _clar.total_errors)
@ -298,13 +355,14 @@ clar_usage(const char *arg)
{ {
printf("Usage: %s [options]\n\n", arg); printf("Usage: %s [options]\n\n", arg);
printf("Options:\n"); printf("Options:\n");
printf(" -sname\tRun only the suite with `name` (can go to individual test name)\n"); printf(" -sname Run only the suite with `name` (can go to individual test name)\n");
printf(" -iname\tInclude the suite with `name`\n"); printf(" -iname Include the suite with `name`\n");
printf(" -xname\tExclude the suite with `name`\n"); printf(" -xname Exclude the suite with `name`\n");
printf(" -v \tIncrease verbosity (show suite names)\n"); printf(" -v Increase verbosity (show suite names)\n");
printf(" -q \tOnly report tests that had an error\n"); printf(" -q Only report tests that had an error\n");
printf(" -Q \tQuit as soon as a test fails\n"); printf(" -Q Quit as soon as a test fails\n");
printf(" -l \tPrint suite names\n"); printf(" -l Print suite names\n");
printf(" -r[filename] Write summary file (to the optional filename)\n");
exit(-1); exit(-1);
} }
@ -318,7 +376,7 @@ clar_parse_args(int argc, char **argv)
char *argument = argv[i]; char *argument = argv[i];
if (argument[0] != '-' || argument[1] == '\0' if (argument[0] != '-' || argument[1] == '\0'
|| strchr("sixvqQl", argument[1]) == NULL) { || strchr("sixvqQlr", argument[1]) == NULL) {
clar_usage(argv[0]); clar_usage(argv[0]);
} }
} }
@ -359,7 +417,24 @@ clar_parse_args(int argc, char **argv)
_clar.report_suite_names = 1; _clar.report_suite_names = 1;
switch (action) { switch (action) {
case 's': _clar_suites[j].enabled = 1; clar_run_suite(&_clar_suites[j], argument); break; case 's': {
struct clar_explicit *explicit =
calloc(1, sizeof(struct clar_explicit));
assert(explicit);
explicit->suite_idx = j;
explicit->filter = argument;
if (_clar.explicit == NULL)
_clar.explicit = explicit;
if (_clar.last_explicit != NULL)
_clar.last_explicit->next = explicit;
_clar_suites[j].enabled = 1;
_clar.last_explicit = explicit;
break;
}
case 'i': _clar_suites[j].enabled = 1; break; case 'i': _clar_suites[j].enabled = 1; break;
case 'x': _clar_suites[j].enabled = 0; break; case 'x': _clar_suites[j].enabled = 0; break;
} }
@ -397,6 +472,12 @@ clar_parse_args(int argc, char **argv)
_clar.report_suite_names = 1; _clar.report_suite_names = 1;
break; break;
case 'r':
_clar.write_summary = 1;
_clar.summary_filename = *(argument + 2) ? (argument + 2) :
"summary.xml";
break;
default: default:
assert(!"Unexpected commandline argument!"); assert(!"Unexpected commandline argument!");
} }
@ -412,23 +493,31 @@ clar_test_init(int argc, char **argv)
"" ""
); );
if (argc > 1)
clar_parse_args(argc, argv);
if (_clar.write_summary &&
!(_clar.summary = clar_summary_init(_clar.summary_filename))) {
clar_print_onabort("Failed to open the summary file\n");
exit(-1);
}
if (clar_sandbox() < 0) { if (clar_sandbox() < 0) {
clar_print_onabort("Failed to sandbox the test runner.\n"); clar_print_onabort("Failed to sandbox the test runner.\n");
exit(-1); exit(-1);
} }
_clar.argc = argc;
_clar.argv = argv;
} }
int int
clar_test_run(void) clar_test_run(void)
{ {
if (_clar.argc > 1) size_t i;
clar_parse_args(_clar.argc, _clar.argv); struct clar_explicit *explicit;
if (!_clar.suites_ran) { if (_clar.explicit) {
size_t i; for (explicit = _clar.explicit; explicit; explicit = explicit->next)
clar_run_suite(&_clar_suites[explicit->suite_idx], explicit->filter);
} else {
for (i = 0; i < _clar_suite_count; ++i) for (i = 0; i < _clar_suite_count; ++i)
clar_run_suite(&_clar_suites[i], NULL); clar_run_suite(&_clar_suites[i], NULL);
} }
@ -439,6 +528,9 @@ clar_test_run(void)
void void
clar_test_shutdown(void) clar_test_shutdown(void)
{ {
struct clar_explicit *explicit, *explicit_next;
struct clar_report *report, *report_next;
clar_print_shutdown( clar_print_shutdown(
_clar.tests_ran, _clar.tests_ran,
(int)_clar_suite_count, (int)_clar_suite_count,
@ -446,6 +538,21 @@ clar_test_shutdown(void)
); );
clar_unsandbox(); clar_unsandbox();
if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0) {
clar_print_onabort("Failed to write the summary file\n");
exit(-1);
}
for (explicit = _clar.explicit; explicit; explicit = explicit_next) {
explicit_next = explicit->next;
free(explicit);
}
for (report = _clar.reports; report; report = report_next) {
report_next = report->next;
free(report);
}
} }
int int
@ -465,7 +572,7 @@ static void abort_test(void)
if (!_clar.trampoline_enabled) { if (!_clar.trampoline_enabled) {
clar_print_onabort( clar_print_onabort(
"Fatal error: a cleanup method raised an exception."); "Fatal error: a cleanup method raised an exception.");
clar_report_errors(); clar_report_errors(_clar.last_report);
exit(-1); exit(-1);
} }
@ -475,7 +582,7 @@ static void abort_test(void)
void clar__skip(void) void clar__skip(void)
{ {
_clar.test_status = CL_TEST_SKIP; _clar.last_report->status = CL_TEST_SKIP;
_clar.total_skipped++; _clar.total_skipped++;
abort_test(); abort_test();
} }
@ -489,17 +596,14 @@ void clar__fail(
{ {
struct clar_error *error = calloc(1, sizeof(struct clar_error)); struct clar_error *error = calloc(1, sizeof(struct clar_error));
if (_clar.errors == NULL) if (_clar.last_report->errors == NULL)
_clar.errors = error; _clar.last_report->errors = error;
if (_clar.last_error != NULL) if (_clar.last_report->last_error != NULL)
_clar.last_error->next = error; _clar.last_report->last_error->next = error;
_clar.last_error = error; _clar.last_report->last_error = error;
error->test = _clar.active_test;
error->test_number = _clar.tests_ran;
error->suite = _clar.active_suite;
error->file = file; error->file = file;
error->line_number = line; error->line_number = line;
error->error_msg = error_msg; error->error_msg = error_msg;
@ -508,7 +612,7 @@ void clar__fail(
error->description = strdup(description); error->description = strdup(description);
_clar.total_errors++; _clar.total_errors++;
_clar.test_status = CL_TEST_FAILURE; _clar.last_report->status = CL_TEST_FAILURE;
if (should_abort) if (should_abort)
abort_test(); abort_test();
@ -653,3 +757,4 @@ void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
#include "clar/fixtures.h" #include "clar/fixtures.h"
#include "clar/fs.h" #include "clar/fs.h"
#include "clar/print.h" #include "clar/print.h"
#include "clar/summary.h"

View File

@ -12,13 +12,16 @@
enum cl_test_status { enum cl_test_status {
CL_TEST_OK, CL_TEST_OK,
CL_TEST_FAILURE, CL_TEST_FAILURE,
CL_TEST_SKIP CL_TEST_SKIP,
CL_TEST_NOTRUN,
}; };
/** Setup clar environment */
void clar_test_init(int argc, char *argv[]); void clar_test_init(int argc, char *argv[]);
int clar_test_run(void); int clar_test_run(void);
void clar_test_shutdown(void); void clar_test_shutdown(void);
/** One shot setup & run */
int clar_test(int argc, char *argv[]); int clar_test(int argc, char *argv[]);
const char *clar_sandbox_path(void); const char *clar_sandbox_path(void);

View File

@ -13,16 +13,16 @@ static void clar_print_shutdown(int test_count, int suite_count, int error_count
(void)error_count; (void)error_count;
printf("\n\n"); printf("\n\n");
clar_report_errors(); clar_report_all();
} }
static void clar_print_error(int num, const struct clar_error *error) static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error)
{ {
printf(" %d) Failure:\n", num); printf(" %d) Failure:\n", num);
printf("%s::%s [%s:%d]\n", printf("%s::%s [%s:%d]\n",
error->suite, report->suite,
error->test, report->test,
error->file, error->file,
error->line_number); error->line_number);
@ -44,6 +44,7 @@ static void clar_print_ontest(const char *test_name, int test_number, enum cl_te
case CL_TEST_OK: printf("."); break; case CL_TEST_OK: printf("."); break;
case CL_TEST_FAILURE: printf("F"); break; case CL_TEST_FAILURE: printf("F"); break;
case CL_TEST_SKIP: printf("S"); break; case CL_TEST_SKIP: printf("S"); break;
case CL_TEST_NOTRUN: printf("N"); break;
} }
fflush(stdout); fflush(stdout);

134
tests/clar/summary.h Normal file
View File

@ -0,0 +1,134 @@
#include <stdio.h>
#include <time.h>
int clar_summary_close_tag(
struct clar_summary *summary, const char *tag, int indent)
{
const char *indt;
if (indent == 0) indt = "";
else if (indent == 1) indt = "\t";
else indt = "\t\t";
return fprintf(summary->fp, "%s</%s>\n", indt, tag);
}
int clar_summary_testsuites(struct clar_summary *summary)
{
return fprintf(summary->fp, "<testsuites>\n");
}
int clar_summary_testsuite(struct clar_summary *summary,
int idn, const char *name, const char *pkg, time_t timestamp,
double elapsed, int test_count, int fail_count, int error_count)
{
struct tm *tm = localtime(&timestamp);
char iso_dt[20];
if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", tm) == 0)
return -1;
return fprintf(summary->fp, "\t<testsuite "
" id=\"%d\""
" name=\"%s\""
" package=\"%s\""
" hostname=\"localhost\""
" timestamp=\"%s\""
" time=\"%.2f\""
" tests=\"%d\""
" failures=\"%d\""
" errors=\"%d\">\n",
idn, name, pkg, iso_dt, elapsed, test_count, fail_count, error_count);
}
int clar_summary_testcase(struct clar_summary *summary,
const char *name, const char *classname, double elapsed)
{
return fprintf(summary->fp,
"\t\t<testcase name=\"%s\" classname=\"%s\" time=\"%.2f\">\n",
name, classname, elapsed);
}
int clar_summary_failure(struct clar_summary *summary,
const char *type, const char *message, const char *desc)
{
return fprintf(summary->fp,
"\t\t\t<failure type=\"%s\"><![CDATA[%s\n%s]]></failure>\n",
type, message, desc);
}
struct clar_summary *clar_summary_init(const char *filename)
{
struct clar_summary *summary;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
return NULL;
if ((summary = malloc(sizeof(struct clar_summary))) == NULL) {
fclose(fp);
return NULL;
}
summary->filename = filename;
summary->fp = fp;
return summary;
}
int clar_summary_shutdown(struct clar_summary *summary)
{
struct clar_report *report;
const char *last_suite = NULL;
if (clar_summary_testsuites(summary) < 0)
goto on_error;
report = _clar.reports;
while (report != NULL) {
struct clar_error *error = report->errors;
if (last_suite == NULL || strcmp(last_suite, report->suite) != 0) {
if (clar_summary_testsuite(summary, 0, report->suite, "",
time(NULL), 0, _clar.tests_ran, _clar.total_errors, 0) < 0)
goto on_error;
}
last_suite = report->suite;
clar_summary_testcase(summary, report->test, "what", 0);
while (error != NULL) {
if (clar_summary_failure(summary, "assert",
error->error_msg, error->description) < 0)
goto on_error;
error = error->next;
}
if (clar_summary_close_tag(summary, "testcase", 2) < 0)
goto on_error;
report = report->next;
if (!report || strcmp(last_suite, report->suite) != 0) {
if (clar_summary_close_tag(summary, "testsuite", 1) < 0)
goto on_error;
}
}
if (clar_summary_close_tag(summary, "testsuites", 0) < 0 ||
fclose(summary->fp) != 0)
goto on_error;
printf("written summary file to %s\n", summary->filename);
free(summary);
return 0;
on_error:
fclose(summary->fp);
free(summary);
return -1;
}

View File

@ -35,6 +35,8 @@ void test_config_include__absolute(void)
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_git_pass(p_unlink("config-include-absolute"));
} }
void test_config_include__homedir(void) void test_config_include__homedir(void)
@ -48,6 +50,8 @@ void test_config_include__homedir(void)
cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_sandbox_set_search_path_defaults(); cl_sandbox_set_search_path_defaults();
cl_git_pass(p_unlink("config-include-homedir"));
} }
/* We need to pretend that the variables were defined where the file was included */ /* We need to pretend that the variables were defined where the file was included */
@ -66,6 +70,9 @@ void test_config_include__ordering(void)
git_buf_clear(&buf); git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_git_pass(p_unlink("included"));
cl_git_pass(p_unlink("including"));
} }
/* We need to pretend that the variables were defined where the file was included */ /* We need to pretend that the variables were defined where the file was included */
@ -76,8 +83,18 @@ void test_config_include__depth(void)
cl_git_fail(git_config_open_ondisk(&cfg, "a")); cl_git_fail(git_config_open_ondisk(&cfg, "a"));
p_unlink("a"); cl_git_pass(p_unlink("a"));
p_unlink("b"); cl_git_pass(p_unlink("b"));
}
void test_config_include__empty_path_sanely_handled(void)
{
cl_git_mkfile("a", "[include]\npath");
cl_git_pass(git_config_open_ondisk(&cfg, "a"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "include.path"));
cl_assert_equal_s("", git_buf_cstr(&buf));
cl_git_pass(p_unlink("a"));
} }
void test_config_include__missing(void) void test_config_include__missing(void)
@ -89,6 +106,8 @@ void test_config_include__missing(void)
cl_assert(giterr_last() == NULL); cl_assert(giterr_last() == NULL);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf)); cl_assert_equal_s("baz", git_buf_cstr(&buf));
cl_git_pass(p_unlink("including"));
} }
void test_config_include__missing_homedir(void) void test_config_include__missing_homedir(void)
@ -103,6 +122,7 @@ void test_config_include__missing_homedir(void)
cl_assert_equal_s("baz", git_buf_cstr(&buf)); cl_assert_equal_s("baz", git_buf_cstr(&buf));
cl_sandbox_set_search_path_defaults(); cl_sandbox_set_search_path_defaults();
cl_git_pass(p_unlink("including"));
} }
#define replicate10(s) s s s s s s s s s s #define replicate10(s) s s s s s s s s s s
@ -122,6 +142,10 @@ void test_config_include__depth2(void)
git_buf_clear(&buf); git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2"));
cl_assert_equal_s("baz2", git_buf_cstr(&buf)); cl_assert_equal_s("baz2", git_buf_cstr(&buf));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("middle"));
cl_git_pass(p_unlink("bottom"));
} }
void test_config_include__removing_include_removes_values(void) void test_config_include__removing_include_removes_values(void)
@ -132,6 +156,9 @@ void test_config_include__removing_include_removes_values(void)
cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_mkfile("top-level", ""); cl_git_mkfile("top-level", "");
cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar")); cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
} }
void test_config_include__rewriting_include_refreshes_values(void) void test_config_include__rewriting_include_refreshes_values(void)
@ -145,6 +172,10 @@ void test_config_include__rewriting_include_refreshes_values(void)
cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar")); cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "first.other")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "first.other"));
cl_assert_equal_s(buf.ptr, "value"); cl_assert_equal_s(buf.ptr, "value");
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("first"));
cl_git_pass(p_unlink("second"));
} }
void test_config_include__included_variables_cannot_be_deleted(void) void test_config_include__included_variables_cannot_be_deleted(void)
@ -154,13 +185,20 @@ void test_config_include__included_variables_cannot_be_deleted(void)
cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_fail(git_config_delete_entry(cfg, "foo.bar")); cl_git_fail(git_config_delete_entry(cfg, "foo.bar"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
} }
void test_config_include__included_variables_cannot_be_modified(void) void test_config_include__included_variables_cannot_be_modified(void)
{ {
cl_git_mkfile("top-level", "[include]\npath = included\n"); cl_git_mkfile("top-level", "[include]\npath = included\n");
cl_git_mkfile("included", "[foo]\nbar = value"); cl_git_mkfile("included", "[foo]\nbar = value");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_fail(git_config_set_string(cfg, "foo.bar", "other-value")); cl_git_fail(git_config_set_string(cfg, "foo.bar", "other-value"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
} }

View File

@ -748,3 +748,36 @@ void test_config_read__bom(void)
git_config_free(cfg); git_config_free(cfg);
git_buf_free(&buf); git_buf_free(&buf);
} }
static int read_nosection_cb(const git_config_entry *entry, void *payload) {
int *seen = (int*)payload;
if (strcmp(entry->name, "key") == 0) {
(*seen)++;
}
return 0;
}
/* This would ideally issue a warning, if we had a way to do so. */
void test_config_read__nosection(void)
{
git_config *cfg;
git_buf buf = GIT_BUF_INIT;
int seen = 0;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-nosection")));
/*
* Given a key with no section, we do not allow reading it,
* but we do include it in an iteration over the config
* store. This appears to match how git's own APIs (and
* git-config(1)) behave.
*/
cl_git_fail_with(git_config_get_string_buf(&buf, cfg, "key"), GIT_EINVALIDSPEC);
cl_git_pass(git_config_foreach(cfg, read_nosection_cb, &seen));
cl_assert_equal_i(seen, 1);
git_buf_free(&buf);
git_config_free(cfg);
}

46
tests/core/memmem.c Normal file
View File

@ -0,0 +1,46 @@
#include "clar_libgit2.h"
static void assert_found(const char *haystack, const char *needle, size_t expected_pos)
{
cl_assert_equal_p(git__memmem(haystack, haystack ? strlen(haystack) : 0,
needle, needle ? strlen(needle) : 0),
haystack + expected_pos);
}
static void assert_absent(const char *haystack, const char *needle)
{
cl_assert_equal_p(git__memmem(haystack, haystack ? strlen(haystack) : 0,
needle, needle ? strlen(needle) : 0),
NULL);
}
void test_core_memmem__found(void)
{
assert_found("a", "a", 0);
assert_found("ab", "a", 0);
assert_found("ba", "a", 1);
assert_found("aa", "a", 0);
assert_found("aab", "aa", 0);
assert_found("baa", "aa", 1);
assert_found("dabc", "abc", 1);
assert_found("abababc", "abc", 4);
}
void test_core_memmem__absent(void)
{
assert_absent("a", "b");
assert_absent("a", "aa");
assert_absent("ba", "ab");
assert_absent("ba", "ab");
assert_absent("abc", "abcd");
assert_absent("abcabcabc", "bcac");
}
void test_core_memmem__edgecases(void)
{
assert_absent(NULL, NULL);
assert_absent("a", NULL);
assert_absent(NULL, "a");
assert_absent("", "a");
assert_absent("a", "");
}

View File

@ -1,45 +1,84 @@
#include "clar_libgit2.h" #include "clar_libgit2.h"
static void assert_l32_parses(const char *string, int32_t expected, int base)
{
int32_t i;
cl_git_pass(git__strntol32(&i, string, strlen(string), NULL, base));
cl_assert_equal_i(i, expected);
}
static void assert_l32_fails(const char *string, int base)
{
int32_t i;
cl_git_fail(git__strntol32(&i, string, strlen(string), NULL, base));
}
static void assert_l64_parses(const char *string, int64_t expected, int base)
{
int64_t i;
cl_git_pass(git__strntol64(&i, string, strlen(string), NULL, base));
cl_assert_equal_i(i, expected);
}
static void assert_l64_fails(const char *string, int base)
{
int64_t i;
cl_git_fail(git__strntol64(&i, string, strlen(string), NULL, base));
}
void test_core_strtol__int32(void) void test_core_strtol__int32(void)
{ {
int32_t i; assert_l32_parses("123", 123, 10);
assert_l32_parses(" +123 ", 123, 10);
assert_l32_parses(" +2147483647 ", 2147483647, 10);
assert_l32_parses(" -2147483648 ", -2147483648LL, 10);
assert_l32_parses("A", 10, 16);
assert_l32_parses("1x1", 1, 10);
cl_git_pass(git__strtol32(&i, "123", NULL, 10)); assert_l32_fails("", 10);
cl_assert(i == 123); assert_l32_fails("a", 10);
cl_git_pass(git__strtol32(&i, " +123 ", NULL, 10)); assert_l32_fails("x10x", 10);
cl_assert(i == 123); assert_l32_fails(" 2147483657 ", 10);
cl_git_pass(git__strtol32(&i, " +2147483647 ", NULL, 10)); assert_l32_fails(" -2147483657 ", 10);
cl_assert(i == 2147483647);
cl_git_pass(git__strtol32(&i, " -2147483648 ", NULL, 10));
cl_assert(i == -2147483648LL);
cl_git_fail(git__strtol32(&i, " 2147483657 ", NULL, 10));
cl_git_fail(git__strtol32(&i, " -2147483657 ", NULL, 10));
} }
void test_core_strtol__int64(void) void test_core_strtol__int64(void)
{ {
int64_t i; assert_l64_parses("123", 123, 10);
assert_l64_parses(" +123 ", 123, 10);
assert_l64_parses(" +2147483647 ", 2147483647, 10);
assert_l64_parses(" -2147483648 ", -2147483648LL, 10);
assert_l64_parses(" 2147483657 ", 2147483657LL, 10);
assert_l64_parses(" -2147483657 ", -2147483657LL, 10);
assert_l64_parses(" 9223372036854775807 ", INT64_MAX, 10);
assert_l64_parses(" -9223372036854775808 ", INT64_MIN, 10);
assert_l64_parses(" 0x7fffffffffffffff ", INT64_MAX, 16);
assert_l64_parses(" -0x8000000000000000 ", INT64_MIN, 16);
assert_l64_parses("1a", 26, 16);
assert_l64_parses("1A", 26, 16);
cl_git_pass(git__strtol64(&i, "123", NULL, 10)); assert_l64_fails("", 10);
cl_assert(i == 123); assert_l64_fails("a", 10);
cl_git_pass(git__strtol64(&i, " +123 ", NULL, 10)); assert_l64_fails("x10x", 10);
cl_assert(i == 123); assert_l64_fails("0x8000000000000000", 16);
cl_git_pass(git__strtol64(&i, " +2147483647 ", NULL, 10)); assert_l64_fails("-0x8000000000000001", 16);
cl_assert(i == 2147483647);
cl_git_pass(git__strtol64(&i, " -2147483648 ", NULL, 10));
cl_assert(i == -2147483648LL);
cl_git_pass(git__strtol64(&i, " 2147483657 ", NULL, 10));
cl_assert(i == 2147483657LL);
cl_git_pass(git__strtol64(&i, " -2147483657 ", NULL, 10));
cl_assert(i == -2147483657LL);
cl_git_pass(git__strtol64(&i, " 9223372036854775807 ", NULL, 10));
cl_assert(i == INT64_MAX);
cl_git_pass(git__strtol64(&i, " -9223372036854775808 ", NULL, 10));
cl_assert(i == INT64_MIN);
cl_git_pass(git__strtol64(&i, " 0x7fffffffffffffff ", NULL, 16));
cl_assert(i == INT64_MAX);
cl_git_pass(git__strtol64(&i, " -0x8000000000000000 ", NULL, 16));
cl_assert(i == INT64_MIN);
} }
void test_core_strtol__buffer_length_truncates(void)
{
int32_t i32;
int64_t i64;
cl_git_pass(git__strntol32(&i32, "11", 1, NULL, 10));
cl_assert_equal_i(i32, 1);
cl_git_pass(git__strntol64(&i64, "11", 1, NULL, 10));
cl_assert_equal_i(i64, 1);
}
void test_core_strtol__error_message_cuts_off(void)
{
assert_l32_fails("2147483657foobar", 10);
cl_assert(strstr(giterr_last()->message, "2147483657") != NULL);
cl_assert(strstr(giterr_last()->message, "foobar") == NULL);
}

View File

@ -407,3 +407,22 @@ void test_core_vector__reverse(void)
git_vector_free(&v); git_vector_free(&v);
} }
void test_core_vector__dup_empty_vector(void)
{
git_vector v = GIT_VECTOR_INIT;
git_vector dup = GIT_VECTOR_INIT;
int dummy;
cl_assert_equal_i(0, v.length);
cl_git_pass(git_vector_dup(&dup, &v, v._cmp));
cl_assert_equal_i(0, dup._alloc_size);
cl_assert_equal_i(0, dup.length);
cl_git_pass(git_vector_insert(&dup, &dummy));
cl_assert_equal_i(8, dup._alloc_size);
cl_assert_equal_i(1, dup.length);
git_vector_free(&dup);
}

View File

@ -288,3 +288,74 @@ void test_diff_parse__patch_roundtrip_succeeds(void)
git_buf_free(&patchbuf); git_buf_free(&patchbuf);
git_buf_free(&diffbuf); git_buf_free(&diffbuf);
} }
#define cl_assert_equal_i_src(i1,i2,file,line) clar__assert_equal(file,line,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
static void cl_git_assert_lineinfo_(int old_lineno, int new_lineno, int num_lines, git_patch *patch, size_t hunk_idx, size_t line_idx, const char *file, int lineno)
{
const git_diff_line *line;
cl_git_expect(git_patch_get_line_in_hunk(&line, patch, hunk_idx, line_idx), 0, file, lineno);
cl_assert_equal_i_src(old_lineno, line->old_lineno, file, lineno);
cl_assert_equal_i_src(new_lineno, line->new_lineno, file, lineno);
cl_assert_equal_i_src(num_lines, line->num_lines, file, lineno);
}
#define cl_git_assert_lineinfo(old, new, num, p, h, l) \
cl_git_assert_lineinfo_(old,new,num,p,h,l,__FILE__,__LINE__)
void test_diff_parse__issue4672(void)
{
const char *text = "diff --git a/a b/a\n"
"index 7f129fd..af431f2 100644\n"
"--- a/a\n"
"+++ b/a\n"
"@@ -3 +3 @@\n"
"-a contents 2\n"
"+a contents\n";
git_diff *diff;
git_patch *patch;
const git_diff_hunk *hunk;
size_t n, l = 0;
cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
cl_git_assert_lineinfo(3, -1, 1, patch, 0, l++);
cl_git_assert_lineinfo(-1, 3, 1, patch, 0, l++);
cl_assert_equal_i(n, l);
git_patch_free(patch);
git_diff_free(diff);
}
void test_diff_parse__lineinfo(void)
{
const char *text = PATCH_ORIGINAL_TO_CHANGE_MIDDLE;
git_diff *diff;
git_patch *patch;
const git_diff_hunk *hunk;
size_t n, l = 0;
cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
cl_git_assert_lineinfo(3, 3, 1, patch, 0, l++);
cl_git_assert_lineinfo(4, 4, 1, patch, 0, l++);
cl_git_assert_lineinfo(5, 5, 1, patch, 0, l++);
cl_git_assert_lineinfo(6, -1, 1, patch, 0, l++);
cl_git_assert_lineinfo(-1, 6, 1, patch, 0, l++);
cl_git_assert_lineinfo(7, 7, 1, patch, 0, l++);
cl_git_assert_lineinfo(8, 8, 1, patch, 0, l++);
cl_git_assert_lineinfo(9, 9, 1, patch, 0, l++);
cl_assert_equal_i(n, l);
git_patch_free(patch);
git_diff_free(diff);
}

View File

@ -284,3 +284,19 @@ void test_object_tree_update__add_conflict2(void)
cl_git_fail(git_tree_create_updated(&tree_updater_id, g_repo, NULL, 2, updates)); cl_git_fail(git_tree_create_updated(&tree_updater_id, g_repo, NULL, 2, updates));
} }
void test_object_tree_update__remove_invalid_submodule(void)
{
git_tree *baseline;
git_oid updated_tree_id, baseline_id;
git_tree_update updates[] = {
{GIT_TREE_UPDATE_REMOVE, {{0}}, GIT_FILEMODE_BLOB, "submodule"},
};
/* This tree contains a submodule with an all-zero commit for a submodule named 'submodule' */
cl_git_pass(git_oid_fromstr(&baseline_id, "396c7f1adb7925f51ba13a75f48252f44c5a14a2"));
cl_git_pass(git_tree_lookup(&baseline, g_repo, &baseline_id));
cl_git_pass(git_tree_create_updated(&updated_tree_id, g_repo, baseline, 1, updates));
git_tree_free(baseline);
}

View File

@ -263,6 +263,9 @@ static int cred_failure_cb(
void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void) void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
{ {
git__free(_remote_url);
git__free(_remote_user);
_remote_url = git__strdup("https://github.com/libgit2/non-existent"); _remote_url = git__strdup("https://github.com/libgit2/non-existent");
_remote_user = git__strdup("libgit2test"); _remote_user = git__strdup("libgit2test");
@ -293,6 +296,9 @@ void test_online_clone__cred_callback_called_again_on_auth_failure(void)
{ {
size_t counter = 0; size_t counter = 0;
git__free(_remote_url);
git__free(_remote_user);
_remote_url = git__strdup("https://github.com/libgit2/non-existent"); _remote_url = git__strdup("https://github.com/libgit2/non-existent");
_remote_user = git__strdup("libgit2test"); _remote_user = git__strdup("libgit2test");

View File

@ -152,8 +152,12 @@ static void do_verify_push_status(record_callbacks_data *data, const push_status
git_buf_free(&msg); git_buf_free(&msg);
} }
git_vector_foreach(actual, i, iter) git_vector_foreach(actual, i, iter) {
git__free(iter); push_status *s = (push_status *)iter;
git__free(s->ref);
git__free(s->msg);
git__free(s);
}
git_vector_free(actual); git_vector_free(actual);
} }
@ -393,7 +397,7 @@ void test_online_push__initialize(void)
} }
git_remote_disconnect(_remote); git_remote_disconnect(_remote);
git_vector_free(&delete_specs); git_vector_free_deep(&delete_specs);
/* Now that we've deleted everything, fetch from the remote */ /* Now that we've deleted everything, fetch from the remote */
memcpy(&fetch_opts.callbacks, &_record_cbs, sizeof(git_remote_callbacks)); memcpy(&fetch_opts.callbacks, &_record_cbs, sizeof(git_remote_callbacks));

Binary file not shown.

View File

@ -0,0 +1,80 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "fileops.h"
#include "repository.h"
static git_repository *g_repo = NULL;
void test_submodule_inject_option__initialize(void)
{
g_repo = setup_fixture_submodule_simple();
}
void test_submodule_inject_option__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int find_naughty(git_submodule *sm, const char *name, void *payload)
{
int *foundit = (int *) payload;
GIT_UNUSED(sm);
if (!git__strcmp("naughty", name))
*foundit = true;
return 0;
}
void test_submodule_inject_option__url(void)
{
int foundit;
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
cl_git_rewritefile(buf.ptr,
"[submodule \"naughty\"]\n"
" path = testrepo\n"
" url = -u./payload\n");
git_buf_free(&buf);
/* We do want to find it, but with the appropriate field empty */
foundit = 0;
cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit));
cl_assert_equal_i(1, foundit);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty"));
cl_assert_equal_s("testrepo", git_submodule_path(sm));
cl_assert_equal_p(NULL, git_submodule_url(sm));
git_submodule_free(sm);
}
void test_submodule_inject_option__path(void)
{
int foundit;
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
cl_git_rewritefile(buf.ptr,
"[submodule \"naughty\"]\n"
" path = --something\n"
" url = blah.git\n");
git_buf_free(&buf);
/* We do want to find it, but with the appropriate field empty */
foundit = 0;
cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit));
cl_assert_equal_i(1, foundit);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty"));
cl_assert_equal_s("naughty", git_submodule_path(sm));
cl_assert_equal_s("blah.git", git_submodule_url(sm));
git_submodule_free(sm);
}

View File

@ -0,0 +1,340 @@
#include "clar_libgit2.h"
#include "transports/smart.h"
enum expected_status {
PARSE_SUCCESS,
PARSE_FAILURE
};
static void assert_flush_parses(const char *line)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_FLUSH);
cl_assert_equal_strn(endptr, line + 4, linelen - 4);
git_pkt_free((git_pkt *) pkt);
}
static void assert_data_pkt_parses(const char *line, const char *expected_data, size_t expected_len)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_data *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_DATA);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->data, expected_data, expected_len);
git_pkt_free((git_pkt *) pkt);
}
static void assert_sideband_progress_parses(const char *line, const char *expected_data, size_t expected_len)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_progress *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_PROGRESS);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->data, expected_data, expected_len);
git_pkt_free((git_pkt *) pkt);
}
static void assert_error_parses(const char *line, const char *expected_error, size_t expected_len)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_err *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_ERR);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->error, expected_error, expected_len);
git_pkt_free((git_pkt *) pkt);
}
static void assert_ack_parses(const char *line, const char *expected_oid, enum git_ack_status expected_status)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_ack *pkt;
git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, expected_oid));
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_ACK);
cl_assert_equal_oid(&pkt->oid, &oid);
cl_assert_equal_i(pkt->status, expected_status);
git_pkt_free((git_pkt *) pkt);
}
static void assert_nak_parses(const char *line)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_NAK);
cl_assert_equal_strn(endptr, line + 7, linelen - 7);
git_pkt_free((git_pkt *) pkt);
}
static void assert_comment_parses(const char *line, const char *expected_comment)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_comment *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_COMMENT);
cl_assert_equal_strn(pkt->comment, expected_comment, strlen(expected_comment));
git_pkt_free((git_pkt *) pkt);
}
static void assert_ok_parses(const char *line, const char *expected_ref)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_ok *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_OK);
cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref));
git_pkt_free((git_pkt *) pkt);
}
static void assert_unpack_parses(const char *line, bool ok)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_unpack *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_UNPACK);
cl_assert_equal_i(pkt->unpack_ok, ok);
git_pkt_free((git_pkt *) pkt);
}
static void assert_ng_parses(const char *line, const char *expected_ref, const char *expected_msg)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_ng *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_NG);
cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref));
cl_assert_equal_strn(pkt->msg, expected_msg, strlen(expected_msg));
git_pkt_free((git_pkt *) pkt);
}
#define assert_ref_parses(line, expected_oid, expected_ref, expected_capabilities) \
assert_ref_parses_(line, sizeof(line), expected_oid, expected_ref, expected_capabilities)
static void assert_ref_parses_(const char *line, size_t linelen, const char *expected_oid,
const char *expected_ref, const char *expected_capabilities)
{
const char *endptr;
git_pkt_ref *pkt;
git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, expected_oid));
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_REF);
cl_assert_equal_oid(&pkt->head.oid, &oid);
cl_assert_equal_strn(pkt->head.name, expected_ref, strlen(expected_ref));
if (expected_capabilities)
cl_assert_equal_strn(pkt->capabilities, expected_capabilities, strlen(expected_capabilities));
else
cl_assert_equal_p(NULL, pkt->capabilities);
git_pkt_free((git_pkt *) pkt);
}
static void assert_pkt_fails(const char *line)
{
const char *endptr;
git_pkt *pkt;
cl_git_fail(git_pkt_parse_line(&pkt, &endptr, line, strlen(line) + 1));
}
void test_transports_smart_packet__parsing_garbage_fails(void)
{
assert_pkt_fails("0foobar");
assert_pkt_fails("00foobar");
assert_pkt_fails("000foobar");
assert_pkt_fails("0001");
assert_pkt_fails("");
assert_pkt_fails("0");
assert_pkt_fails("0i00");
assert_pkt_fails("f");
}
void test_transports_smart_packet__flush_parses(void)
{
assert_flush_parses("0000");
assert_flush_parses("0000foobar");
}
void test_transports_smart_packet__data_pkt(void)
{
assert_pkt_fails("000foobar");
assert_pkt_fails("0001o");
assert_pkt_fails("0001\1");
assert_data_pkt_parses("0005\1", "", 0);
assert_pkt_fails("0009\1o");
assert_data_pkt_parses("0009\1data", "data", 4);
assert_data_pkt_parses("000a\1data", "data", 5);
}
void test_transports_smart_packet__sideband_progress_pkt(void)
{
assert_pkt_fails("0001\2");
assert_sideband_progress_parses("0005\2", "", 0);
assert_pkt_fails("0009\2o");
assert_sideband_progress_parses("0009\2data", "data", 4);
assert_sideband_progress_parses("000a\2data", "data", 5);
}
void test_transports_smart_packet__sideband_err_pkt(void)
{
assert_pkt_fails("0001\3");
assert_error_parses("0005\3", "", 0);
assert_pkt_fails("0009\3o");
assert_error_parses("0009\3data", "data", 4);
assert_error_parses("000a\3data", "data", 5);
}
void test_transports_smart_packet__ack_pkt(void)
{
assert_ack_parses("0030ACK 0000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000", 0);
assert_ack_parses("0039ACK 0000000000000000000000000000000000000000 continue",
"0000000000000000000000000000000000000000",
GIT_ACK_CONTINUE);
assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 common",
"0000000000000000000000000000000000000000",
GIT_ACK_COMMON);
assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 ready",
"0000000000000000000000000000000000000000",
GIT_ACK_READY);
/* these should fail as they don't have OIDs */
assert_pkt_fails("0007ACK");
assert_pkt_fails("0008ACK ");
/* this one is missing a space and should thus fail */
assert_pkt_fails("0036ACK00000000000000000x0000000000000000000000 ready");
/* the following ones have invalid OIDs and should thus fail */
assert_pkt_fails("0037ACK 00000000000000000x0000000000000000000000 ready");
assert_pkt_fails("0036ACK 000000000000000000000000000000000000000 ready");
assert_pkt_fails("0036ACK 00000000000000000x0000000000000000000000ready");
/* this one has an invalid status and should thus fail */
assert_pkt_fails("0036ACK 0000000000000000000000000000000000000000 read");
}
void test_transports_smart_packet__nak_pkt(void)
{
assert_nak_parses("0007NAK");
assert_pkt_fails("0007NaK");
assert_pkt_fails("0007nak");
assert_nak_parses("0007NAKfoobar");
assert_pkt_fails("0007nakfoobar");
assert_pkt_fails("0007 NAK");
}
void test_transports_smart_packet__error_pkt(void)
{
assert_pkt_fails("0007ERR");
assert_pkt_fails("0008ERRx");
assert_error_parses("0008ERR ", "", 0);
assert_error_parses("000EERR ERRMSG", "ERRMSG", 6);
}
void test_transports_smart_packet__comment_pkt(void)
{
assert_comment_parses("0005#", "");
assert_comment_parses("000B#foobar", "#fooba");
assert_comment_parses("000C#foobar", "#foobar");
assert_comment_parses("001A#this is a comment\nfoo", "#this is a comment\nfoo");
}
void test_transports_smart_packet__ok_pkt(void)
{
assert_pkt_fails("0007ok\n");
assert_ok_parses("0007ok ", "");
assert_ok_parses("0008ok \n", "");
assert_ok_parses("0008ok x", "x");
assert_ok_parses("0009ok x\n", "x");
assert_pkt_fails("001OK ref/foo/bar");
assert_ok_parses("0012ok ref/foo/bar", "ref/foo/bar");
assert_pkt_fails("0013OK ref/foo/bar\n");
assert_ok_parses("0013ok ref/foo/bar\n", "ref/foo/bar");
}
void test_transports_smart_packet__ng_pkt(void)
{
/* TODO: same as for ok pkt */
assert_pkt_fails("0007ng\n");
assert_pkt_fails("0008ng \n");
assert_pkt_fails("000Bng ref\n");
assert_pkt_fails("000Bng ref\n");
/* TODO: is this a valid packet line? Probably not. */
assert_ng_parses("000Ang x\n", "", "x");
assert_ng_parses("000Fng ref msg\n", "ref", "msg");
assert_ng_parses("000Fng ref msg\n", "ref", "msg");
}
void test_transports_smart_packet__unpack_pkt(void)
{
assert_unpack_parses("000Dunpack ok", 1);
assert_unpack_parses("000Dunpack ng error-msg", 0);
/* TODO: the following tests should fail */
assert_unpack_parses("000Aunpack", 0);
assert_unpack_parses("0011unpack foobar", 0);
assert_unpack_parses("0010unpack ng ok", 0);
assert_unpack_parses("0010unpack okfoo", 1);
}
void test_transports_smart_packet__ref_pkt(void)
{
assert_pkt_fails("002C0000000000000000000000000000000000000000");
assert_pkt_fails("002D0000000000000000000000000000000000000000\n");
assert_pkt_fails("00300000000000000000000000000000000000000000HEAD");
assert_pkt_fails("004800000000x0000000000000000000000000000000 refs/heads/master\0multi_ack");
assert_ref_parses(
"003F0000000000000000000000000000000000000000 refs/heads/master\0",
"0000000000000000000000000000000000000000", "refs/heads/master", "");
assert_ref_parses(
"00480000000000000000000000000000000000000000 refs/heads/master\0multi_ack",
"0000000000000000000000000000000000000000", "refs/heads/master", "multi_ack");
assert_ref_parses(
"00460000000000000000000000000000000000000000 refs/heads/master\0one two",
"0000000000000000000000000000000000000000", "refs/heads/master", "one two");
assert_ref_parses(
"00310000000000000000000000000000000000000000 HEAD",
"0000000000000000000000000000000000000000", "HEAD", NULL);
assert_pkt_fails("0031000000000000000000000000000000000000000 HEAD");
assert_ref_parses(
"00360000000000000000000000000000000000000000 HEAD HEAD",
"0000000000000000000000000000000000000000", "HEAD HEAD", NULL);
}

View File

@ -228,6 +228,26 @@ void test_worktree_worktree__init(void)
git_repository_free(repo); git_repository_free(repo);
} }
void test_worktree_worktree__add_from_bare(void)
{
git_worktree *wt;
git_repository *repo, *wtrepo;
repo = cl_git_sandbox_init("short_tag.git");
cl_assert_equal_i(1, git_repository_is_bare(repo));
cl_assert_equal_i(0, git_repository_is_worktree(repo));
cl_git_pass(git_worktree_add(&wt, repo, "worktree-frombare", "worktree-frombare", NULL));
cl_git_pass(git_repository_open(&wtrepo, "worktree-frombare"));
cl_assert_equal_i(0, git_repository_is_bare(wtrepo));
cl_assert_equal_i(1, git_repository_is_worktree(wtrepo));
git_worktree_free(wt);
git_repository_free(repo);
git_repository_free(wtrepo);
}
void test_worktree_worktree__add_locked(void) void test_worktree_worktree__add_locked(void)
{ {
git_worktree *wt; git_worktree *wt;
@ -435,7 +455,7 @@ void test_worktree_worktree__unlock_unlocked_worktree(void)
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
cl_assert(!git_worktree_is_locked(NULL, wt)); cl_assert(!git_worktree_is_locked(NULL, wt));
cl_assert(git_worktree_unlock(wt) == 0); cl_assert_equal_i(1, git_worktree_unlock(wt));
cl_assert(!wt->locked); cl_assert(!wt->locked);
git_worktree_free(wt); git_worktree_free(wt);
@ -448,7 +468,7 @@ void test_worktree_worktree__unlock_locked_worktree(void)
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
cl_git_pass(git_worktree_lock(wt, NULL)); cl_git_pass(git_worktree_lock(wt, NULL));
cl_assert(git_worktree_is_locked(NULL, wt)); cl_assert(git_worktree_is_locked(NULL, wt));
cl_git_pass(git_worktree_unlock(wt)); cl_assert_equal_i(0, git_worktree_unlock(wt));
cl_assert(!wt->locked); cl_assert(!wt->locked);
git_worktree_free(wt); git_worktree_free(wt);