Imported Upstream version 0.25.1

This commit is contained in:
Russell Sim 2017-04-24 17:27:25 +10:00
commit 9c87194ea8
512 changed files with 22296 additions and 9311 deletions

14
.github/ISSUE_TEMPLATE vendored Normal file
View File

@ -0,0 +1,14 @@
You are opening a _bug report_ against the libgit2 project. If you have a
question about an API or usage, please ask on StackOverflow:
http://stackoverflow.com/questions/tagged/libgit2. Please fill out the
reproduction steps (below) and delete this introductory paragraph. Thanks!
### Reproduction steps
### Expected behavior
### Actual behavior
### Version of libgit2 (release number or SHA1)
### Operating system(s) tested

View File

@ -37,9 +37,6 @@ matrix:
- os: osx
compiler: gcc
include:
- compiler: i586-mingw32msvc-gcc
env: OPTIONS="-DCMAKE_TOOLCHAIN_FILE=../script/toolchain-mingw32.cmake" SKIP_TESTS=1
os: linux
- compiler: gcc
env: COVERITY=1
os: linux
@ -50,9 +47,6 @@ matrix:
os: linux
allow_failures:
- env: COVERITY=1
- env:
- VALGRIND=1
OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi

View File

@ -1,4 +1,4 @@
v0.24 + 1
v0.25 + 1
-------
### Changes or improvements
@ -9,11 +9,158 @@ v0.24 + 1
### Breaking API changes
v0.25
-------
### Changes or improvements
* Fix repository discovery with `git_repository_discover` and
`git_repository_open_ext` to match git's handling of a ceiling
directory at the current directory. git only checks ceiling
directories when its search ascends to a parent directory. A ceiling
directory matching the starting directory will not prevent git from
finding a repository in the starting directory or a parent directory.
* Do not fail when deleting remotes in the presence of broken
global configs which contain branches.
* Support for reading and writing git index v4 files
* Improve the performance of the revwalk and bring us closer to git's code.
* The reference db has improved support for concurrency and returns `GIT_ELOCKED`
when an operation could not be performed due to locking.
* Nanosecond resolution is now activated by default, following git's change to
do this.
* We now restrict the set of ciphers we let OpenSSL use by default.
* Users can now register their own merge drivers for use with `.gitattributes`.
The library also gained built-in support for the union merge driver.
* The default for creating references is now to validate that the object does
exist.
* Add `git_proxy_options` which is used by the different networking
implementations to let the caller specify the proxy settings instead of
relying on the environment variables.
### API additions
* You can now get the user-agent used by libgit2 using the
`GIT_OPT_GET_USER_AGENT` option with `git_libgit2_opts()`.
It is the counterpart to `GIT_OPT_SET_USER_AGENT`.
* The `GIT_OPT_SET_SSL_CIPHERS` option for `git_libgit2_opts()` lets you specify
a custom list of ciphers to use for OpenSSL.
* `git_commit_create_buffer()` creates a commit and writes it into a
user-provided buffer instead of writing it into the object db. Combine it with
`git_commit_create_with_signature()` in order to create a commit with a
cryptographic signature.
* `git_blob_create_fromstream()` and
`git_blob_create_fromstream_commit()` allow you to create a blob by
writing into a stream. Useful when you do not know the final size or
want to copy the contents from another stream.
* New flags for `git_repository_open_ext`:
* `GIT_REPOSITORY_OPEN_NO_DOTGIT` - Do not check for a repository by
appending `/.git` to the `start_path`; only open the repository if
`start_path` itself points to the git directory.
* `GIT_REPOSITORY_OPEN_FROM_ENV` - Find and open a git repository,
respecting the environment variables used by the git command-line
tools. If set, `git_repository_open_ext` will ignore the other
flags and the `ceiling_dirs` argument, and will allow a NULL
`path` to use `GIT_DIR` or search from the current directory. The
search for a repository will respect `$GIT_CEILING_DIRECTORIES`
and `$GIT_DISCOVERY_ACROSS_FILESYSTEM`. The opened repository
will respect `$GIT_INDEX_FILE`, `$GIT_NAMESPACE`,
`$GIT_OBJECT_DIRECTORY`, and `$GIT_ALTERNATE_OBJECT_DIRECTORIES`.
In the future, this flag will also cause `git_repository_open_ext`
to respect `$GIT_WORK_TREE` and `$GIT_COMMON_DIR`; currently,
`git_repository_open_ext` with this flag will error out if either
`$GIT_WORK_TREE` or `$GIT_COMMON_DIR` is set.
* `git_diff_from_buffer()` can create a `git_diff` object from the contents
of a git-style patch file.
* `git_index_version()` and `git_index_set_version()` to get and set
the index version
* `git_odb_expand_ids()` lets you check for the existence of multiple
objects at once.
* The new `git_blob_dup()`, `git_commit_dup()`, `git_tag_dup()` and
`git_tree_dup()` functions provide type-specific wrappers for
`git_object_dup()` to reduce noise and increase type safety for callers.
* `git_reference_dup()` lets you duplicate a reference to aid in ownership
management and cleanup.
* `git_signature_from_buffer()` lets you create a signature from a string in the
format that appear in objects.
* `git_tree_create_updated()` lets you create a tree based on another one
together with a list of updates. For the covered update cases, it's more
efficient than the `git_index` route.
* `git_apply_patch()` applies hunks from a `git_patch` to a buffer.
* `git_diff_to_buf()` lets you print an entire diff directory to a buffer,
similar to how `git_patch_to_buf()` works.
* `git_proxy_init_options()` is added to initialize a `git_proxy_options`
structure at run-time.
* `git_merge_driver_register()`, `git_merge_driver_unregister()` let you
register and unregister a custom merge driver to be used when `.gitattributes`
specifies it.
* `git_merge_driver_lookup()` can be used to look up a merge driver by name.
* `git_merge_driver_source_repo()`, `git_merge_driver_source_ancestor()`,
`git_merge_driver_source_ours()`, `git_merge_driver_source_theirs()`,
`git_merge_driver_source_file_options()` added as accessors to
`git_merge_driver_source`.
### API removals
* `git_blob_create_fromchunks()` has been removed in favour of
`git_blob_create_fromstream()`.
### Breaking API changes
* `git_packbuilder_object_count` and `git_packbuilder_written` now
return a `size_t` instead of a `uint32_t` for more thorough
compatibility with the rest of the library.
* `git_packbuiler_progress` now provides explicitly sized `uint32_t`
values instead of `unsigned int`.
* `git_diff_file` now includes an `id_abbrev` field that reflects the
number of nibbles set in the `id` field.
* `git_odb_backend` now has a `freshen` function pointer. This optional
function pointer is similar to the `exists` function, but it will update
a last-used marker. For filesystem-based object databases, this updates
the timestamp of the file containing the object, to indicate "freshness".
If this is `NULL`, then it will not be called and the `exists` function
will be used instead.
* `git_remote_connect()` now accepts proxy options.
v0.24
-------
### Changes or improvements
* Custom merge drivers can now be registered, which allows callers to
configure callbacks to honor `merge=driver` configuration in
`.gitattributes`.
* Custom filters can now be registered with wildcard attributes, for
example `filter=*`. Consumers should examine the attributes parameter
of the `check` function for details.
@ -80,6 +227,10 @@ v0.24
### Breaking API changes
* `git_merge_options` now provides a `default_driver` that can be used
to provide the name of a merge driver to be used to handle files changed
during a merge.
* The `git_merge_tree_flag_t` is now `git_merge_flag_t`. Subsequently,
its members are no longer prefixed with `GIT_MERGE_TREE_FLAG` but are
now prefixed with `GIT_MERGE_FLAG`, and the `tree_flags` field of the
@ -111,12 +262,15 @@ v0.24
`GIT_CONFIG_LEVEL_PROGRAMDATA` which represent a rough Windows equivalent
to the system level configuration.
* `git_rebase_init()` not also takes a merge options.
* `git_rebase_options` now has a `merge_options` field.
* The index no longer performs locking itself. This is not something
users of the library should have been relying on as it's not part of
the concurrency guarantees.
* `git_remote_connect()` now takes a `custom_headers` argument to set
the extra HTTP header fields to send.
v0.23
------

View File

@ -20,6 +20,7 @@ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Mo
INCLUDE(CheckLibraryExists)
INCLUDE(CheckFunctionExists)
INCLUDE(CheckSymbolExists)
INCLUDE(CheckStructHasMember)
INCLUDE(AddCFlagIfSupported)
INCLUDE(FindPkgConfig)
@ -109,7 +110,7 @@ ELSE ()
ENDIF()
IF (HAVE_STRUCT_STAT_NSEC OR WIN32)
OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF )
OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" ON )
ENDIF()
# This variable will contain the libraries we need to put into
@ -507,6 +508,11 @@ ELSE ()
ENDIF ()
ENDIF()
CHECK_SYMBOL_EXISTS(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L)
IF (HAVE_REGCOMP_L)
ADD_DEFINITIONS(-DHAVE_REGCOMP_L)
ENDIF ()
CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
IF (HAVE_FUTIMENS)
ADD_DEFINITIONS(-DHAVE_FUTIMENS)
@ -589,8 +595,10 @@ IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
ADD_DEFINITIONS(-DGIT_ARCH_64)
ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4)
ADD_DEFINITIONS(-DGIT_ARCH_32)
ELSEIF (CMAKE_SIZEOF_VOID_P)
MESSAGE(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
ELSE()
MESSAGE(FATAL_ERROR "Unsupported architecture")
MESSAGE(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
ENDIF()
# Compile and link libgit2
@ -697,6 +705,8 @@ IF (BUILD_CLAR)
# Add a test target which runs the cred callback tests, to be
# called after setting the url and user
ADD_TEST(libgit2_clar-cred_callback libgit2_clar -v -sonline::clone::cred_callback)
ADD_TEST(libgit2_clar-proxy_credentials_in_url libgit2_clar -v -sonline::clone::proxy_credentials_in_url)
ADD_TEST(libgit2_clar-proxy_credentials_request libgit2_clar -v -sonline::clone::proxy_credentials_request)
ENDIF ()
IF (TAGS)

View File

@ -94,7 +94,7 @@ the change, but pass with your changes.
In addition to new tests, please ensure that your changes do not cause
any other test failures. Running the entire test suite is helpful
before you submit a pull request. When you build libgit2, the test
suite will also be built. You can run all tests by simply running
suite will also be built. You can run most of the tests by simply running
the resultant `libgit2_clar` binary. If you want to run a specific
unit test, you can name it with the `-s` option. For example:
@ -105,6 +105,21 @@ worktree status tests:
libgit2_clar -sstatus::worktree
The default test run is fairly exhaustive, but it will exclude some
unit tests by default: in particular, those that talk to network
servers and the tests that manipulate the filesystem in onerous
ways (and may need to have special privileges to run). To run the
network tests:
libgit2_clar -ionline
In addition, various tests may be enabled by environment variables,
like the ones that write exceptionally large repositories or manipulate
the filesystem structure in unexpected ways. These tests *may be
dangerous* to run on a normal machine and may harm your filesystem. It's
not recommended that you run these; instead, the continuous integration
servers will run these (in a sandbox).
## Porting Code From Other Open-Source Projects
`libgit2` is licensed under the terms of the GPL v2 with a linking

View File

@ -1,60 +0,0 @@
PLATFORM=$(shell uname -s)
ifneq (,$(CROSS_COMPILE))
PREFIX=$(CROSS_COMPILE)-
else
PREFIX=
endif
MINGW=0
ifneq (,$(findstring MINGW32,$(PLATFORM)))
MINGW=1
endif
ifneq (,$(findstring mingw,$(CROSS_COMPILE)))
MINGW=1
endif
rm=rm -f
AR=$(PREFIX)ar cq
RANLIB=$(PREFIX)ranlib
LIBNAME=libgit2.a
ifeq ($(MINGW),1)
CC=gcc
else
CC=cc
endif
CC:=$(PREFIX)$(CC)
INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib
DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(EXTRA_DEFINES)
CFLAGS= -g $(DEFINES) -Wall -Wextra -Wno-missing-field-initializers -O2 $(EXTRA_CFLAGS)
SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) src/hash/hash_generic.c
ifeq ($(MINGW),1)
SRCS += $(wildcard src/win32/*.c) $(wildcard src/compat/*.c) deps/regex/regex.c
INCLUDES += -Ideps/regex
DEFINES += -DWIN32 -D_WIN32_WINNT=0x0501 -D__USE_MINGW_ANSI_STDIO=1
else
SRCS += $(wildcard src/unix/*.c)
CFLAGS += -fPIC
endif
OBJS = $(patsubst %.c,%.o,$(SRCS))
%.c.o:
$(CC) $(CFLAGS) -c $*.c
all: $(LIBNAME)
$(LIBNAME): $(OBJS)
$(rm) $@
$(AR) $@ $(OBJS)
$(RANLIB) $@
clean:
$(rm) $(OBJS) $(LIBNAME)

View File

@ -5,7 +5,7 @@ So, you want to start helping out with `libgit2`? That's fantastic! We
welcome contributions and we promise we'll try to be nice.
This is a list of libgit2 related projects that new contributors can take
on. It includes a number of good starter projects and well as some larger
on. It includes a number of good starter projects as well as some larger
ideas that no one is actively working on.
## Before You Start
@ -70,11 +70,11 @@ some incremental steps listed towards the larger goal. Those steps
might make good smaller projects by themselves.
* Port part of the Git test suite to run against the command line emulation
in examples/
* Pick a Git command that is emulated in our examples/ area
in `examples/`
* Pick a Git command that is emulated in our `examples/` area
* Extract the Git tests that exercise that command
* Convert the tests to call our emulation
* These tests could go in examples/tests/...
* These tests could go in `examples/tests/`...
* Add hooks API to enumerate and manage hooks (not run them at this point)
* Enumeration of available hooks
* Lookup API to see which hooks have a script and get the script

View File

@ -43,9 +43,17 @@ We ask that you not open a GitHub Issue for help, only for bug reports.
What It Can Do
==============
`libgit2` is already very usable and is being used in production for many
applications including the GitHub.com site, in Plastic SCM and also powering
Microsoft's Visual Studio tools for Git. The library provides:
The goal of this library is to allow its users the ability to handle Git data in
their applications from their programming language of choice, as is used in
production for many applications including the GitHub.com site, in Plastic SCM
and also powering Microsoft's Visual Studio tools for Git.
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
Git system, but most commands a user would type are out of scope for this
library to implement directly.
The library provides:
* SHA conversions, formatting and shortening
* abstracted ODB backend system
@ -190,7 +198,7 @@ Language Bindings
Here are the bindings to libgit2 that are currently available:
* C++
* libqgit2, Qt bindings <https://projects.kde.org/projects/playground/libs/libqgit2/>
* libqgit2, Qt bindings <https://projects.kde.org/projects/playground/libs/libqgit2/repository/>
* Chicken Scheme
* chicken-git <https://wiki.call-cc.org/egg/git>
* D
@ -214,7 +222,6 @@ Here are the bindings to libgit2 that are currently available:
* .NET
* libgit2sharp <https://github.com/libgit2/libgit2sharp>
* Node.js
* node-gitteh <https://github.com/libgit2/node-gitteh>
* nodegit <https://github.com/nodegit/nodegit>
* Objective-C
* objective-git <https://github.com/libgit2/objective-git>
@ -227,7 +234,7 @@ Here are the bindings to libgit2 that are currently available:
* PHP
* php-git <https://github.com/libgit2/php-git>
* PowerShell
* GitPowerShell <https://github.com/ethomson/gitpowershell>
* PSGit <https://github.com/PoshCode/PSGit>
* Python
* pygit2 <https://github.com/libgit2/pygit2>
* R
@ -237,7 +244,7 @@ Here are the bindings to libgit2 that are currently available:
* Rust
* git2-rs <https://github.com/alexcrichton/git2-rs>
* Swift
* Gift <https://github.com/modocache/Gift>
* SwiftGit2 <https://github.com/SwiftGit2/SwiftGit2>
* Vala
* libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>

View File

@ -62,33 +62,38 @@ general case still affects you if you use ssh.
General Case
------------
By default we use libcurl, which has its own ![recommendations for
thread safety](http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading).
If it's available, by default we use libcurl to provide HTTP tunneling support,
which may be linked against a number of cryptographic libraries and has its
own
[recommendations for thread safety](https://curl.haxx.se/libcurl/c/threadsafe.html).
If libcurl was not found or was disabled, libgit2 uses OpenSSL to be
able to use HTTPS as a transport. This library is made to be
thread-implementation agnostic, and the users of the library must set
which locking function it should use. This means that libgit2 cannot
know what to set as the user of libgit2 may use OpenSSL independently
and the locking settings must survive libgit2 shutting down.
If there are no alternative TLS implementations (currently only
SecureTransport), libgit2 uses OpenSSL in order to use HTTPS as a transport.
OpenSSL is thread-safe starting at version 1.1.0. If your copy of libgit2 is
linked against that version, you do not need to take any further steps.
Even if libgit2 doesn't use OpenSSL directly, OpenSSL can still be used
by libssh2 depending on the configuration. If OpenSSL is used both by
libgit2 and libssh2, you only need to set up threading for OpenSSL once.
Older versions of OpenSSL are made to be thread-implementation agnostic, and the
users of the library must set which locking function it should use. libgit2
cannot know what to set as the user of libgit2 may also be using OpenSSL independently and
the locking settings must then live outside the lifetime of libgit2.
libgit2 does provide a last-resort convenience function
Even if libgit2 doesn't use OpenSSL directly, OpenSSL can still be used by
libssh2 or libcurl depending on the configuration. If OpenSSL is used by
more than one library, you only need to set up threading for OpenSSL once.
If libgit2 is linked against OpenSSL, it provides a last-resort convenience function
`git_openssl_set_locking()` (available in `sys/openssl.h`) to use the
platform-native mutex mechanisms to perform the locking, which you may
rely on if you do not want to use OpenSSL outside of libgit2, or you
know that libgit2 will outlive the rest of the operations. It is not
platform-native mutex mechanisms to perform the locking, which you can use
if you do not want to use OpenSSL outside of libgit2, or you
know that libgit2 will outlive the rest of the operations. It is then not
safe to use OpenSSL multi-threaded after libgit2's shutdown function
has been called. Note `git_openssl_set_locking()` only works if
libgit2 uses OpenSSL directly - if OpenSSL is only used as a dependency
of libssh2 as described above, `git_openssl_set_locking()` is a no-op.
of libssh2 or libcurl as described above, `git_openssl_set_locking()` is a no-op.
If your programming language offers a package/bindings for OpenSSL,
you should very strongly prefer to use that in order to set up
locking, as they provide a level of coördination which is impossible
locking, as they provide a level of coordination which is impossible
when using this function.
See the
@ -96,13 +101,10 @@ See the
on threading for more details, and http://trac.libssh2.org/wiki/MultiThreading
for a specific example of providing the threading callbacks.
Be also aware that libgit2 does not always link against OpenSSL
if there are alternatives provided by the system.
libssh2 may be linked against OpenSSL or libgcrypt. If it uses OpenSSL,
see the above paragraphs. If it uses libgcrypt, then you need to
set up its locking before using it multi-threaded. libgit2 has no
direct connection to libgcrypt and thus has not convenience functions for
direct connection to libgcrypt and thus has no convenience functions for
it (but libgcrypt has macros). Read libgcrypt's
[threading documentation for more information](http://www.gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html)

View File

@ -12,19 +12,14 @@ environment:
ARCH: 32
- GENERATOR: "Visual Studio 11 Win64"
ARCH: 64
- GENERATOR: "MSYS Makefiles"
ARCH: 32
- GENERATOR: "MSYS Makefiles"
ARCH: i686 # this is for 32-bit MinGW-w64
- GENERATOR: "MSYS Makefiles"
ARCH: 64
matrix:
allow_failures:
- GENERATOR: "MSYS Makefiles"
ARCH: 32
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
@ -37,7 +32,18 @@ build_script:
if "%GENERATOR%"=="MSYS Makefiles" (C:\MinGW\msys\1.0\bin\sh --login /c/projects/libgit2/script/appveyor-mingw.sh)
test_script:
- ps: |
$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
$env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
$env:GITTEST_REMOTE_USER="libgit2test"
ctest -V -R libgit2_clar-cred_callback
Receive-Job -Job $proxyJob
$env:GITTEST_REMOTE_PROXY_URL = "http://foo:bar@localhost:8080"
ctest -V -R libgit2_clar-proxy_credentials_in_url
$env:GITTEST_REMOTE_PROXY_URL = "http://localhost:8080"
$env:GITTEST_REMOTE_PROXY_USER = "foo"
$env:GITTEST_REMOTE_PROXY_PASS = "bar"
ctest -V -R libgit2_clar-proxy_credentials_request

View File

@ -75,15 +75,14 @@ int print_matched_cb(const char *path, const char *matched_pathspec, void *paylo
{
struct print_payload p = *(struct print_payload*)(payload);
int ret;
git_status_t status;
unsigned status;
(void)matched_pathspec;
if (git_status_file((unsigned int*)(&status), p.repo, path)) {
return -1; //abort
if (git_status_file(&status, p.repo, path)) {
return -1;
}
if (status & GIT_STATUS_WT_MODIFIED ||
status & GIT_STATUS_WT_NEW) {
if (status & GIT_STATUS_WT_MODIFIED || status & GIT_STATUS_WT_NEW) {
printf("add '%s'\n", path);
ret = 0;
} else {

View File

@ -146,6 +146,25 @@ int match_uint16_arg(
return 1;
}
int match_uint32_arg(
uint32_t *out, struct args_info *args, const char *opt)
{
const char *found = match_numeric_arg(args, opt);
uint16_t val;
char *endptr = NULL;
if (!found)
return 0;
val = (uint32_t)strtoul(found, &endptr, 0);
if (!endptr || *endptr != '\0')
fatal("expected number after argument", opt);
if (out)
*out = val;
return 1;
}
static int match_int_internal(
int *out, const char *str, int allow_negative, const char *opt)
{

View File

@ -72,6 +72,15 @@ extern int match_str_arg(
extern int match_uint16_arg(
uint16_t *out, struct args_info *args, const char *opt);
/**
* Check current `args` entry against `opt` string parsing as uint32. If
* `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
* is a prefix (equal sign optional), take the remainder of the arg as a
* uint32_t value; otherwise return 0.
*/
extern int match_uint32_arg(
uint32_t *out, struct args_info *args, const char *opt);
/**
* Check current `args` entry against `opt` string parsing as int. If
* `opt` matches exactly, take the next arg as an int value; if it matches

View File

@ -293,11 +293,11 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
/* TODO: parse thresholds */
o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
else if (!match_uint16_arg(
else if (!match_uint32_arg(
&o->diffopts.context_lines, &args, "-U") &&
!match_uint16_arg(
!match_uint32_arg(
&o->diffopts.context_lines, &args, "--unified") &&
!match_uint16_arg(
!match_uint32_arg(
&o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
!match_uint16_arg(
&o->diffopts.id_abbrev, &args, "--abbrev") &&

File diff suppressed because it is too large Load Diff

View File

@ -55,6 +55,8 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo
*/
static int transfer_progress_cb(const git_transfer_progress *stats, void *payload)
{
(void)payload;
if (stats->received_objects == stats->total_objects) {
printf("Resolving deltas %d/%d\r",
stats->indexed_deltas, stats->total_deltas);
@ -71,7 +73,6 @@ int fetch(git_repository *repo, int argc, char **argv)
{
git_remote *remote = NULL;
const git_transfer_progress *stats;
struct dl_data data;
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
if (argc < 2) {
@ -79,14 +80,13 @@ int fetch(git_repository *repo, int argc, char **argv)
return EXIT_FAILURE;
}
// Figure out whether it's a named remote or a URL
/* Figure out whether it's a named remote or a URL */
printf("Fetching %s for repo %p\n", argv[1], repo);
if (git_remote_lookup(&remote, repo, argv[1]) < 0) {
if (git_remote_lookup(&remote, repo, argv[1]) < 0)
if (git_remote_create_anonymous(&remote, repo, argv[1]) < 0)
return -1;
}
goto on_error;
// Set up the callbacks (only update_tips for now)
/* Set up the callbacks (only update_tips for now) */
fetch_opts.callbacks.update_tips = &update_cb;
fetch_opts.callbacks.sideband_progress = &progress_cb;
fetch_opts.callbacks.transfer_progress = transfer_progress_cb;
@ -98,7 +98,7 @@ int fetch(git_repository *repo, int argc, char **argv)
* "fetch".
*/
if (git_remote_fetch(remote, NULL, &fetch_opts, "fetch") < 0)
return -1;
goto on_error;
/**
* If there are local objects (we got a thin pack), then tell

View File

@ -26,7 +26,7 @@ static int use_remote(git_repository *repo, char *name)
*/
callbacks.credentials = cred_acquire_cb;
error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL);
error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL, NULL);
if (error < 0)
goto cleanup;

View File

@ -40,6 +40,7 @@
#include "git2/pack.h"
#include "git2/patch.h"
#include "git2/pathspec.h"
#include "git2/proxy.h"
#include "git2/rebase.h"
#include "git2/refdb.h"
#include "git2/reflog.h"

View File

@ -150,46 +150,48 @@ GIT_EXTERN(int) git_blob_create_fromworkdir(git_oid *id, git_repository *repo, c
*/
GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path);
typedef int (*git_blob_chunk_cb)(char *content, size_t max_length, void *payload);
/**
* Write a loose blob to the Object Database from a
* provider of chunks of data.
* Create a stream to write a new blob into the object db
*
* This function may need to buffer the data on disk and will in
* general not be the right choice if you know the size of the data
* to write. If you have data in memory, use
* `git_blob_create_frombuffer()`. If you do not, but know the size of
* the contents (and don't want/need to perform filtering), use
* `git_odb_open_wstream()`.
*
* Don't close this stream yourself but pass it to
* `git_blob_create_fromstream_commit()` to commit the write to the
* object db and get the object id.
*
* If the `hintpath` parameter is filled, it will be used to determine
* what git filters should be applied to the object before it is written
* to the object database.
*
* The implementation of the callback MUST respect the following rules:
*
* - `content` must be filled by the callback. The maximum number of
* bytes that the buffer can accept per call is defined by the
* `max_length` parameter. Allocation and freeing of the buffer will
* be taken care of by libgit2.
*
* - The `callback` must return the number of bytes that have been
* written to the `content` buffer.
*
* - When there is no more data to stream, `callback` should return 0.
* This will prevent it from being invoked anymore.
*
* - If an error occurs, the callback should return a negative value.
* This value will be returned to the caller.
*
* @param id Return the id of the written blob
* @param out the stream into which to write
* @param repo Repository where the blob will be written.
* This repository can be bare or not.
* @param hintpath If not NULL, will be used to select data filters
* to apply onto the content of the blob to be created.
* @return 0 or error code (from either libgit2 or callback function)
* @return 0 or error code
*/
GIT_EXTERN(int) git_blob_create_fromchunks(
git_oid *id,
GIT_EXTERN(int) git_blob_create_fromstream(
git_writestream **out,
git_repository *repo,
const char *hintpath,
git_blob_chunk_cb callback,
void *payload);
const char *hintpath);
/**
* Close the stream and write the blob to the object db
*
* The stream will be closed and freed.
*
* @param out the id of the new blob
* @param stream the stream to close
* @return 0 or an error code
*/
GIT_EXTERN(int) git_blob_create_fromstream_commit(
git_oid *out,
git_writestream *stream);
/**
* Write an in-memory buffer to the ODB as a blob
@ -216,6 +218,15 @@ GIT_EXTERN(int) git_blob_create_frombuffer(
*/
GIT_EXTERN(int) git_blob_is_binary(const git_blob *blob);
/**
* Create an in-memory copy of a blob. The copy must be explicitly
* free'd or it will leak.
*
* @param out Pointer to store the copy of the object
* @param source Original object to copy
*/
GIT_EXTERN(int) git_blob_dup(git_blob **out, git_blob *source);
/** @} */
GIT_END_DECL
#endif

View File

@ -394,6 +394,82 @@ GIT_EXTERN(int) git_commit_amend(
const char *message,
const git_tree *tree);
/**
* Create a commit and write it into a buffer
*
* Create a commit as with `git_commit_create()` but instead of
* writing it to the objectdb, write the contents of the object into a
* buffer.
*
* @param out the buffer into which to write the commit object content
*
* @param repo Repository where the referenced tree and parents live
*
* @param author Signature with author and author time of commit
*
* @param committer Signature with committer and * commit time of commit
*
* @param message_encoding The encoding for the message in the
* commit, represented with a standard encoding name.
* E.g. "UTF-8". If NULL, no encoding header is written and
* UTF-8 is assumed.
*
* @param message Full message for this commit
*
* @param tree An instance of a `git_tree` object that will
* be used as the tree for the commit. This tree object must
* also be owned by the given `repo`.
*
* @param parent_count Number of parents for this commit
*
* @param parents Array of `parent_count` pointers to `git_commit`
* objects that will be used as the parents for this commit. This
* array may be NULL if `parent_count` is 0 (root commit). All the
* given commits must be owned by the `repo`.
*
* @return 0 or an error code
*/
GIT_EXTERN(int) git_commit_create_buffer(
git_buf *out,
git_repository *repo,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message,
const git_tree *tree,
size_t parent_count,
const git_commit *parents[]);
/**
* Create a commit object from the given buffer and signature
*
* Given the unsigned commit object's contents, its signature and the
* header field in which to store the signature, attach the signature
* to the commit and write it into the given repository.
*
* @param out the resulting commit id
* @param commit_content the content of the unsigned commit object
* @param signature the signature to add to the commit
* @param signature_field which header field should contain this
* signature. Leave `NULL` for the default of "gpgsig"
* @return 0 or an error code
*/
GIT_EXTERN(int) git_commit_create_with_signature(
git_oid *out,
git_repository *repo,
const char *commit_content,
const char *signature,
const char *signature_field);
/**
* Create an in-memory copy of a commit. The copy must be explicitly
* free'd or it will leak.
*
* @param out Pointer to store the copy of the commit
* @param source Original commit to copy
*/
GIT_EXTERN(int) git_commit_dup(git_commit **out, git_commit *source);
/** @} */
GIT_END_DECL
#endif

View File

@ -109,9 +109,27 @@ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev);
* was compiled
*/
typedef enum {
/**
* If set, libgit2 was built thread-aware and can be safely used from multiple
* threads.
*/
GIT_FEATURE_THREADS = (1 << 0),
/**
* If set, libgit2 was built with and linked against a TLS implementation.
* Custom TLS streams may still be added by the user to support HTTPS
* regardless of this.
*/
GIT_FEATURE_HTTPS = (1 << 1),
/**
* If set, libgit2 was built with and linked against libssh2. A custom
* transport may still be added by the user to support libssh2 regardless of
* this.
*/
GIT_FEATURE_SSH = (1 << 2),
/**
* If set, libgit2 was built with support for sub-second resolution in file
* modification times.
*/
GIT_FEATURE_NSEC = (1 << 3),
} git_feature_t;
@ -158,6 +176,7 @@ typedef enum {
GIT_OPT_SET_USER_AGENT,
GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
GIT_OPT_SET_SSL_CIPHERS,
GIT_OPT_GET_USER_AGENT,
} git_libgit2_opt_t;
/**
@ -268,7 +287,8 @@ typedef enum {
* > to ensure that all inputs to the new objects are valid. For
* > example, when this is enabled, the parent(s) and tree inputs
* > will be validated when creating a new commit. This defaults
* > to disabled.
* > to enabled.
*
* * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
*
* > Set the SSL ciphers use for HTTPS connections.

View File

@ -44,8 +44,8 @@ typedef enum {
typedef struct git_describe_options {
unsigned int version;
unsigned int max_candidates_tags; /** default: 10 */
unsigned int describe_strategy; /** default: GIT_DESCRIBE_DEFAULT */
unsigned int max_candidates_tags; /**< default: 10 */
unsigned int describe_strategy; /**< default: GIT_DESCRIBE_DEFAULT */
const char *pattern;
/**
* When calculating the distance from the matching tag or
@ -105,6 +105,9 @@ typedef struct {
GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *opts, unsigned int version);
/**
* A struct that stores the result of a describe operation.
*/
typedef struct git_describe_result git_describe_result;
/**

View File

@ -264,7 +264,7 @@ typedef enum {
* link, a submodule commit id, or even a tree (although that only if you
* are tracking type changes or ignored/untracked directories).
*
* The `oid` is the `git_oid` of the item. If the entry represents an
* The `id` is the `git_oid` of the item. If the entry represents an
* absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
* then the oid will be zeroes.
*
@ -277,6 +277,11 @@ typedef enum {
*
* `mode` is, roughly, the stat() `st_mode` value for the item. This will
* be restricted to one of the `git_filemode_t` values.
*
* The `id_abbrev` represents the known length of the `id` field, when
* converted to a hex string. It is generally `GIT_OID_HEXSZ`, unless this
* delta was created from reading a patch file, in which case it may be
* abbreviated to something reasonable, like 7 characters.
*/
typedef struct {
git_oid id;
@ -284,6 +289,7 @@ typedef struct {
git_off_t size;
uint32_t flags;
uint16_t mode;
uint16_t id_abbrev;
} git_diff_file;
/**
@ -448,6 +454,8 @@ typedef int (*git_diff_file_cb)(
float progress,
void *payload);
#define GIT_DIFF_HUNK_HEADER_SIZE 128
/**
* When producing a binary diff, the binary data returned will be
* either the deflated full ("literal") contents of the file, or
@ -482,6 +490,14 @@ typedef struct {
/** Structure describing the binary contents of a diff. */
typedef struct {
/**
* Whether there is data in this binary structure or not. If this
* is `1`, then this was produced and included binary content. If
* this is `0` then this was generated knowing only that a binary
* file changed but without providing the data, probably from a patch
* that said `Binary files a/file.txt and b/file.txt differ`.
*/
unsigned int contains_data;
git_diff_binary_file old_file; /**< The contents of the old file. */
git_diff_binary_file new_file; /**< The contents of the new file. */
} git_diff_binary;
@ -499,12 +515,12 @@ typedef int(*git_diff_binary_cb)(
* Structure describing a hunk of a diff.
*/
typedef struct {
int old_start; /**< Starting line number in old_file */
int old_lines; /**< Number of lines in old_file */
int new_start; /**< Starting line number in new_file */
int new_lines; /**< Number of lines in new_file */
size_t header_len; /**< Number of bytes in header text */
char header[128]; /**< Header text, NUL-byte terminated */
int old_start; /** Starting line number in old_file */
int old_lines; /** Number of lines in old_file */
int new_start; /** Starting line number in new_file */
int new_lines; /** Number of lines in new_file */
size_t header_len; /** Number of bytes in header text */
char header[GIT_DIFF_HUNK_HEADER_SIZE]; /** Header text, NUL-byte terminated */
} git_diff_hunk;
/**
@ -1046,6 +1062,21 @@ GIT_EXTERN(int) git_diff_print(
git_diff_line_cb print_cb,
void *payload);
/**
* Produce the complete formatted text output from a diff into a
* buffer.
*
* @param out A pointer to a user-allocated git_buf that will
* contain the diff text
* @param diff A git_diff generated by one of the above functions.
* @param format A git_diff_format_t value to pick the text format.
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_diff_to_buf(
git_buf *out,
git_diff *diff,
git_diff_format_t format);
/**@}*/
@ -1166,6 +1197,30 @@ GIT_EXTERN(int) git_diff_buffers(
git_diff_line_cb line_cb,
void *payload);
/**
* Read the contents of a git patch file into a `git_diff` object.
*
* The diff object produced is similar to the one that would be
* produced if you actually produced it computationally by comparing
* two trees, however there may be subtle differences. For example,
* a patch file likely contains abbreviated object IDs, so the
* object IDs in a `git_diff_delta` produced by this function will
* also be abbreviated.
*
* This function will only read patch files created by a git
* implementation, it will not read unified diffs produced by
* the `diff` program, nor any other types of patch files.
*
* @param out A pointer to a git_diff pointer that will be allocated.
* @param content The contents of a patch file
* @param content_len The length of the patch file contents
* @return 0 or an error code
*/
GIT_EXTERN(int) git_diff_from_buffer(
git_diff **out,
const char *content,
size_t content_len);
/**
* This is an opaque structure which is allocated by `git_diff_get_stats`.
* You are responsible for releasing the object memory when done, using the

View File

@ -98,7 +98,8 @@ typedef enum {
GITERR_CHERRYPICK,
GITERR_DESCRIBE,
GITERR_REBASE,
GITERR_FILESYSTEM
GITERR_FILESYSTEM,
GITERR_PATCH,
} git_error_t;
/**

View File

@ -251,6 +251,31 @@ GIT_EXTERN(int) git_index_caps(const git_index *index);
*/
GIT_EXTERN(int) git_index_set_caps(git_index *index, int caps);
/**
* Get index on-disk version.
*
* Valid return values are 2, 3, or 4. If 3 is returned, an index
* with version 2 may be written instead, if the extension data in
* version 3 is not necessary.
*
* @param index An existing index object
* @return the index version
*/
GIT_EXTERN(unsigned int) git_index_version(git_index *index);
/**
* Set index on-disk version.
*
* Valid values are 2, 3, or 4. If 2 is given, git_index_write may
* write an index with version 3 instead, if necessary to accurately
* represent the index.
*
* @param index An existing index object
* @param version The new version number
* @return 0 on success, -1 on failure
*/
GIT_EXTERN(int) git_index_set_version(git_index *index, unsigned int version);
/**
* Update the contents of an existing index object in memory by reading
* from the hard disk.

View File

@ -273,7 +273,16 @@ typedef struct {
*/
unsigned int recursion_limit;
/** Flags for handling conflicting content. */
/**
* Default merge driver to be used when both sides of a merge have
* changed. The default is the `text` driver.
*/
const char *default_driver;
/**
* Flags for handling conflicting content, to be used with the standard
* (`text`) merge driver.
*/
git_merge_file_favor_t file_favor;
/** see `git_merge_file_flag_t` above */

View File

@ -10,6 +10,7 @@
#include "common.h"
#include "types.h"
#include "oid.h"
#include "oidarray.h"
/**
* @file git2/odb.h
@ -159,7 +160,8 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
/**
* Determine if objects can be found in the object database from a short OID.
* Determine if an object can be found in the object database by an
* abbreviated object ID.
*
* @param out The full OID of the found object if just one is found.
* @param db The database to be searched for the given object.
@ -171,6 +173,50 @@ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
GIT_EXTERN(int) git_odb_exists_prefix(
git_oid *out, git_odb *db, const git_oid *short_id, size_t len);
/**
* The information about object IDs to query in `git_odb_expand_ids`,
* which will be populated upon return.
*/
typedef struct git_odb_expand_id {
/** The object ID to expand */
git_oid id;
/**
* The length of the object ID (in nibbles, or packets of 4 bits; the
* number of hex characters)
* */
unsigned short length;
/**
* The (optional) type of the object to search for; leave as `0` or set
* to `GIT_OBJ_ANY` to query for any object matching the ID.
*/
git_otype type;
} git_odb_expand_id;
/**
* Determine if one or more objects can be found in the object database
* by their abbreviated object ID and type. The given array will be
* updated in place: for each abbreviated ID that is unique in the
* database, and of the given type (if specified), the full object ID,
* object ID length (`GIT_OID_HEXSZ`) and type will be written back to
* the array. For IDs that are not found (or are ambiguous), the
* array entry will be zeroed.
*
* Note that since this function operates on multiple objects, the
* underlying database will not be asked to be reloaded if an object is
* not found (which is unlike other object database operations.)
*
* @param db The database to be searched for the given objects.
* @param ids An array of short object IDs to search for
* @param count The length of the `ids` array
* @return 0 on success or an error code on failure
*/
GIT_EXTERN(int) git_odb_expand_ids(
git_odb *db,
git_odb_expand_id *ids,
size_t count);
/**
* Refresh the object database to load newly added files.
*

View File

@ -196,7 +196,7 @@ GIT_EXTERN(int) git_packbuilder_foreach(git_packbuilder *pb, git_packbuilder_for
* @param pb the packbuilder
* @return the number of objects in the packfile
*/
GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
GIT_EXTERN(size_t) git_packbuilder_object_count(git_packbuilder *pb);
/**
* Get the number of objects the packbuilder has already written out
@ -204,13 +204,13 @@ GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
* @param pb the packbuilder
* @return the number of objects which have already been written
*/
GIT_EXTERN(uint32_t) git_packbuilder_written(git_packbuilder *pb);
GIT_EXTERN(size_t) git_packbuilder_written(git_packbuilder *pb);
/** Packbuilder progress notification function */
typedef int (*git_packbuilder_progress)(
int stage,
unsigned int current,
unsigned int total,
uint32_t current,
uint32_t total,
void *payload);
/**

View File

@ -191,7 +191,7 @@ GIT_EXTERN(int) git_patch_get_hunk(
*
* @param patch The git_patch object
* @param hunk_idx Index of the hunk
* @return Number of lines in hunk or -1 if invalid hunk index
* @return Number of lines in hunk or GIT_ENOTFOUND if invalid hunk index
*/
GIT_EXTERN(int) git_patch_num_lines_in_hunk(
const git_patch *patch,

92
include/git2/proxy.h Normal file
View File

@ -0,0 +1,92 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_proxy_h__
#define INCLUDE_git_proxy_h__
#include "common.h"
#include "transport.h"
GIT_BEGIN_DECL
/**
* The type of proxy to use.
*/
typedef enum {
/**
* Do not attempt to connect through a proxy
*
* If built against libcurl, it itself may attempt to connect
* to a proxy if the environment variables specify it.
*/
GIT_PROXY_NONE,
/**
* Try to auto-detect the proxy from the git configuration.
*/
GIT_PROXY_AUTO,
/**
* Connect via the URL given in the options
*/
GIT_PROXY_SPECIFIED,
} git_proxy_t;
/**
* Options for connecting through a proxy
*
* Note that not all types may be supported, depending on the platform
* and compilation options.
*/
typedef struct {
unsigned int version;
/**
* The type of proxy to use, by URL, auto-detect.
*/
git_proxy_t type;
/**
* The URL of the proxy.
*/
const char *url;
/**
* This will be called if the remote host requires
* authentication in order to connect to it.
*
* Returning GIT_PASSTHROUGH will make libgit2 behave as
* though this field isn't set.
*/
git_cred_acquire_cb credentials;
/**
* If cert verification fails, this will be called to let the
* user make the final decision of whether to allow the
* connection to proceed. Returns 1 to allow the connection, 0
* to disallow it or a negative value to indicate an error.
*/
git_transport_certificate_check_cb certificate_check;
/**
* Payload to be provided to the credentials and certificate
* check callbacks.
*/
void *payload;
} git_proxy_options;
#define GIT_PROXY_OPTIONS_VERSION 1
#define GIT_PROXY_OPTIONS_INIT {GIT_PROXY_OPTIONS_VERSION}
/**
* Initialize a proxy options structure
*
* @param opts the options struct to initialize
* @param version the version of the struct, use `GIT_PROXY_OPTIONS_VERSION`
*/
GIT_EXTERN(int) git_proxy_init_options(git_proxy_options *opts, unsigned int version);
GIT_END_DECL
#endif

View File

@ -461,6 +461,17 @@ GIT_EXTERN(int) git_reference_foreach_name(
git_reference_foreach_name_cb callback,
void *payload);
/**
* Create a copy of an existing reference.
*
* Call `git_reference_free` to free the data.
*
* @param dest pointer where to store the copy
* @param source object to copy
* @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_dup(git_reference **dest, git_reference *source);
/**
* Free the given reference.
*

View File

@ -15,6 +15,7 @@
#include "strarray.h"
#include "transport.h"
#include "pack.h"
#include "proxy.h"
/**
* @file git2/remote.h
@ -25,8 +26,6 @@
*/
GIT_BEGIN_DECL
typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload);
/**
* Add a remote with the default fetch refspec to the repository's configuration.
*
@ -241,10 +240,11 @@ GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote,
* @param direction GIT_DIRECTION_FETCH if you want to fetch or
* GIT_DIRECTION_PUSH if you want to push
* @param callbacks the callbacks to use for this connection
* @param proxy_opts proxy settings
* @param custom_headers extra HTTP headers to use in this connection
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers);
GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy_opts, const git_strarray *custom_headers);
/**
* Get the remote repository's reference advertisement list
@ -358,6 +358,8 @@ typedef struct {
} git_push_update;
/**
* Callback used to inform of upcoming updates.
*
* @param updates an array containing the updates which will be sent
* as commands to the destination.
* @param len number of elements in `updates`
@ -376,7 +378,7 @@ struct git_remote_callbacks {
/**
* Textual progress from the remote. Text send over the
* progress side-band will be passed to this function (this is
* the 'counting objects' output.
* the 'counting objects' output).
*/
git_transport_message_cb sideband_progress;
@ -401,7 +403,7 @@ struct git_remote_callbacks {
* connection to proceed. Returns 1 to allow the connection, 0
* to disallow it or a negative value to indicate an error.
*/
git_transport_certificate_check_cb certificate_check;
git_transport_certificate_check_cb certificate_check;
/**
* During the download of new data, this will be regularly
@ -548,6 +550,11 @@ typedef struct {
*/
git_remote_autotag_option_t download_tags;
/**
* Proxy options to use, by default no proxy is used.
*/
git_proxy_options proxy_opts;
/**
* Extra headers for this fetch operation
*/
@ -555,13 +562,14 @@ typedef struct {
} git_fetch_options;
#define GIT_FETCH_OPTIONS_VERSION 1
#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1 }
#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1, \
GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT }
/**
* Initializes a `git_fetch_options` with default values. Equivalent to
* creating an instance with GIT_FETCH_OPTIONS_INIT.
*
* @param opts the `git_push_options` instance to initialize.
* @param opts the `git_fetch_options` instance to initialize.
* @param version the version of the struct; you should pass
* `GIT_FETCH_OPTIONS_VERSION` here.
* @return Zero on success; -1 on failure.
@ -592,6 +600,11 @@ typedef struct {
*/
git_remote_callbacks callbacks;
/**
* Proxy options to use, by default no proxy is used.
*/
git_proxy_options proxy_opts;
/**
* Extra headers for this push operation
*/
@ -599,7 +612,7 @@ typedef struct {
} git_push_options;
#define GIT_PUSH_OPTIONS_VERSION 1
#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION, 0, GIT_REMOTE_CALLBACKS_INIT }
#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION, 0, GIT_REMOTE_CALLBACKS_INIT, GIT_PROXY_OPTIONS_INIT }
/**
* Initializes a `git_push_options` with default values. Equivalent to

View File

@ -95,11 +95,29 @@ GIT_EXTERN(int) git_repository_discover(
* * GIT_REPOSITORY_OPEN_BARE - Open repository as a bare repo regardless
* of core.bare config, and defer loading config file for faster setup.
* Unlike `git_repository_open_bare`, this can follow gitlinks.
* * GIT_REPOSITORY_OPEN_NO_DOTGIT - Do not check for a repository by
* appending /.git to the start_path; only open the repository if
* start_path itself points to the git directory.
* * GIT_REPOSITORY_OPEN_FROM_ENV - Find and open a git repository,
* respecting the environment variables used by the git command-line
* tools. If set, `git_repository_open_ext` will ignore the other
* flags and the `ceiling_dirs` argument, and will allow a NULL `path`
* to use `GIT_DIR` or search from the current directory. The search
* for a repository will respect $GIT_CEILING_DIRECTORIES and
* $GIT_DISCOVERY_ACROSS_FILESYSTEM. The opened repository will
* respect $GIT_INDEX_FILE, $GIT_NAMESPACE, $GIT_OBJECT_DIRECTORY, and
* $GIT_ALTERNATE_OBJECT_DIRECTORIES. In the future, this flag will
* also cause `git_repository_open_ext` to respect $GIT_WORK_TREE and
* $GIT_COMMON_DIR; currently, `git_repository_open_ext` with this
* flag will error out if either $GIT_WORK_TREE or $GIT_COMMON_DIR is
* set.
*/
typedef enum {
GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
GIT_REPOSITORY_OPEN_BARE = (1 << 2),
GIT_REPOSITORY_OPEN_NO_DOTGIT = (1 << 3),
GIT_REPOSITORY_OPEN_FROM_ENV = (1 << 4),
} git_repository_open_flag_t;
/**
@ -110,7 +128,8 @@ typedef enum {
* see if a repo at this path could be opened.
* @param path Path to open as git repository. If the flags
* permit "searching", then this can be a path to a subdirectory
* inside the working directory of the repository.
* inside the working directory of the repository. May be NULL if
* flags is GIT_REPOSITORY_OPEN_FROM_ENV.
* @param flags A combination of the GIT_REPOSITORY_OPEN flags above.
* @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR delimited list of path
* prefixes at which the search for a containing repository should

View File

@ -25,17 +25,15 @@ GIT_BEGIN_DECL
*/
typedef enum {
/**
* Sort the repository contents in no particular ordering;
* this sorting is arbitrary, implementation-specific
* and subject to change at any time.
* Sort the output with the same default time-order method from git.
* This is the default sorting for new walkers.
*/
GIT_SORT_NONE = 0,
/**
* Sort the repository contents in topological order
* (parents before children); this sorting mode
* can be combined with time sorting.
* Sort the repository contents in topological order (parents before
* children); this sorting mode can be combined with time sorting to
* produce git's "time-order".
*/
GIT_SORT_TOPOLOGICAL = 1 << 0,

View File

@ -62,6 +62,19 @@ GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const c
*/
GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo);
/**
* Create a new signature by parsing the given buffer, which is
* expected to be in the format "Real Name <email> timestamp tzoffset",
* where `timestamp` is the number of seconds since the Unix epoch and
* `tzoffset` is the timezone offset in `hhmm` format (note the lack
* of a colon separator).
*
* @param out new signature
* @param buf signature string
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_signature_from_buffer(git_signature **out, const char *buf);
/**
* Create a copy of an existing signature. All internal strings are also
* duplicated.

View File

@ -154,13 +154,19 @@ typedef struct git_submodule_update_options {
* in the working directory for the newly cloned repository.
*/
unsigned int clone_checkout_strategy;
/**
* Allow fetching from the submodule's default remote if the target
* commit isn't found. Enabled by default.
*/
int allow_fetch;
} git_submodule_update_options;
#define GIT_SUBMODULE_UPDATE_OPTIONS_VERSION 1
#define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \
{ GIT_CHECKOUT_OPTIONS_VERSION, \
{ GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \
{ GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
GIT_FETCH_OPTIONS_INIT, GIT_CHECKOUT_SAFE }
GIT_FETCH_OPTIONS_INIT, GIT_CHECKOUT_SAFE, 1 }
/**
* Initializes a `git_submodule_update_options` with default values.
@ -176,7 +182,9 @@ GIT_EXTERN(int) git_submodule_update_init_options(
/**
* Update a submodule. This will clone a missing submodule and
* checkout the subrepository to the commit specified in the index of
* containing repository.
* the containing repository. If the submodule repository doesn't contain
* the target commit (e.g. because fetchRecurseSubmodules isn't set), then
* the submodule is fetched using the fetch options supplied in options.
*
* @param submodule Submodule object
* @param init If the submodule is not initialized, setting this flag to true

177
include/git2/sys/merge.h Normal file
View File

@ -0,0 +1,177 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_sys_git_merge_h__
#define INCLUDE_sys_git_merge_h__
/**
* @file git2/sys/merge.h
* @brief Git merge driver backend and plugin routines
* @defgroup git_backend Git custom backend APIs
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
typedef struct git_merge_driver git_merge_driver;
/**
* Look up a merge driver by name
*
* @param name The name of the merge driver
* @return Pointer to the merge driver object or NULL if not found
*/
GIT_EXTERN(git_merge_driver *) git_merge_driver_lookup(const char *name);
#define GIT_MERGE_DRIVER_TEXT "text"
#define GIT_MERGE_DRIVER_BINARY "binary"
#define GIT_MERGE_DRIVER_UNION "union"
/**
* A merge driver source represents the file to be merged
*/
typedef struct git_merge_driver_source git_merge_driver_source;
/** Get the repository that the source data is coming from. */
GIT_EXTERN(git_repository *) git_merge_driver_source_repo(
const git_merge_driver_source *src);
/** Gets the ancestor of the file to merge. */
GIT_EXTERN(git_index_entry *) git_merge_driver_source_ancestor(
const git_merge_driver_source *src);
/** Gets the ours side of the file to merge. */
GIT_EXTERN(git_index_entry *) git_merge_driver_source_ours(
const git_merge_driver_source *src);
/** Gets the theirs side of the file to merge. */
GIT_EXTERN(git_index_entry *) git_merge_driver_source_theirs(
const git_merge_driver_source *src);
/** Gets the merge file options that the merge was invoked with */
GIT_EXTERN(git_merge_file_options *) git_merge_driver_source_file_options(
const git_merge_driver_source *src);
/**
* Initialize callback on merge driver
*
* Specified as `driver.initialize`, this is an optional callback invoked
* before a merge driver is first used. It will be called once at most
* per library lifetime.
*
* If non-NULL, the merge driver's `initialize` callback will be invoked
* right before the first use of the driver, so you can defer expensive
* initialization operations (in case libgit2 is being used in a way that
* doesn't need the merge driver).
*/
typedef int (*git_merge_driver_init_fn)(git_merge_driver *self);
/**
* Shutdown callback on merge driver
*
* Specified as `driver.shutdown`, this is an optional callback invoked
* when the merge driver is unregistered or when libgit2 is shutting down.
* It will be called once at most and should release resources as needed.
* This may be called even if the `initialize` callback was not made.
*
* Typically this function will free the `git_merge_driver` object itself.
*/
typedef void (*git_merge_driver_shutdown_fn)(git_merge_driver *self);
/**
* Callback to perform the merge.
*
* Specified as `driver.apply`, this is the callback that actually does the
* merge. If it can successfully perform a merge, it should populate
* `path_out` with a pointer to the filename to accept, `mode_out` with
* the resultant mode, and `merged_out` with the buffer of the merged file
* and then return 0. If the driver returns `GIT_PASSTHROUGH`, then the
* default merge driver should instead be run. It can also return
* `GIT_EMERGECONFLICT` if the driver is not able to produce a merge result,
* and the file will remain conflicted. Any other errors will fail and
* return to the caller.
*
* The `filter_name` contains the name of the filter that was invoked, as
* specified by the file's attributes.
*
* The `src` contains the data about the file to be merged.
*/
typedef int (*git_merge_driver_apply_fn)(
git_merge_driver *self,
const char **path_out,
uint32_t *mode_out,
git_buf *merged_out,
const char *filter_name,
const git_merge_driver_source *src);
/**
* Merge driver structure used to register custom merge drivers.
*
* To associate extra data with a driver, allocate extra data and put the
* `git_merge_driver` struct at the start of your data buffer, then cast
* the `self` pointer to your larger structure when your callback is invoked.
*/
struct git_merge_driver {
/** The `version` should be set to `GIT_MERGE_DRIVER_VERSION`. */
unsigned int version;
/** Called when the merge driver is first used for any file. */
git_merge_driver_init_fn initialize;
/** Called when the merge driver is unregistered from the system. */
git_merge_driver_shutdown_fn shutdown;
/**
* Called to merge the contents of a conflict. If this function
* returns `GIT_PASSTHROUGH` then the default (`text`) merge driver
* will instead be invoked. If this function returns
* `GIT_EMERGECONFLICT` then the file will remain conflicted.
*/
git_merge_driver_apply_fn apply;
};
#define GIT_MERGE_DRIVER_VERSION 1
/**
* Register a merge driver under a given name.
*
* As mentioned elsewhere, the initialize callback will not be invoked
* immediately. It is deferred until the driver is used in some way.
*
* Currently the merge driver registry is not thread safe, so any
* registering or deregistering of merge drivers must be done outside of
* any possible usage of the drivers (i.e. during application setup or
* shutdown).
*
* @param name The name of this driver to match an attribute. Attempting
* to register with an in-use name will return GIT_EEXISTS.
* @param driver The merge driver definition. This pointer will be stored
* as is by libgit2 so it must be a durable allocation (either
* static or on the heap).
* @return 0 on successful registry, error code <0 on failure
*/
GIT_EXTERN(int) git_merge_driver_register(
const char *name, git_merge_driver *driver);
/**
* Remove the merge driver with the given name.
*
* Attempting to remove the builtin libgit2 merge drivers is not permitted
* and will return an error.
*
* Currently the merge driver registry is not thread safe, so any
* registering or deregistering of drivers must be done outside of any
* possible usage of the drivers (i.e. during application setup or shutdown).
*
* @param name The name under which the merge driver was registered
* @return 0 on success, error code <0 on failure
*/
GIT_EXTERN(int) git_merge_driver_unregister(const char *name);
/** @} */
GIT_END_DECL
#endif

View File

@ -83,6 +83,17 @@ struct git_odb_backend {
git_odb_writepack **, git_odb_backend *, git_odb *odb,
git_transfer_progress_cb progress_cb, void *progress_payload);
/**
* "Freshens" an already existing object, updating its last-used
* time. This occurs when `git_odb_write` was called, but the
* object already existed (and will not be re-written). The
* underlying implementation may want to update last-used timestamps.
*
* If callers implement this, they should return `0` if the object
* exists and was freshened, and non-zero otherwise.
*/
int (* freshen)(git_odb_backend *, const git_oid *);
/**
* Frees any resources held by the odb (including the `git_odb_backend`
* itself). An odb backend implementation must provide this function.

16
include/git2/sys/remote.h Normal file
View File

@ -0,0 +1,16 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_sys_git_transport_h
#define INCLUDE_sys_git_transport_h
#include "git2/net.h"
#include "git2/types.h"
GIT_BEGIN_DECL
GIT_END_DECL

View File

@ -9,6 +9,7 @@
#include "git2/common.h"
#include "git2/types.h"
#include "git2/proxy.h"
GIT_BEGIN_DECL
@ -32,7 +33,7 @@ typedef struct git_stream {
int proxy_support;
int (*connect)(struct git_stream *);
int (*certificate)(git_cert **, struct git_stream *);
int (*set_proxy)(struct git_stream *, const char *proxy_url);
int (*set_proxy)(struct git_stream *, const git_proxy_options *proxy_opts);
ssize_t (*read)(struct git_stream *, void *, size_t);
ssize_t (*write)(struct git_stream *, const char *, size_t, int);
int (*close)(struct git_stream *);

31
include/git2/sys/time.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_time_h__
#define INCLUDE_git_time_h__
#include "git2/common.h"
GIT_BEGIN_DECL
/**
* Return a monotonic time value, useful for measuring running time
* and setting up timeouts.
*
* The returned value is an arbitrary point in time -- it can only be
* used when comparing it to another `git_time_monotonic` call.
*
* The time is returned in seconds, with a decimal fraction that differs
* on accuracy based on the underlying system, but should be least
* accurate to Nanoseconds.
*
* This function cannot fail.
*/
GIT_EXTERN(double) git_time_monotonic(void);
GIT_END_DECL
#endif

View File

@ -11,6 +11,7 @@
#include "git2/net.h"
#include "git2/types.h"
#include "git2/strarray.h"
#include "git2/proxy.h"
/**
* @file git2/sys/transport.h
@ -53,6 +54,7 @@ struct git_transport {
const char *url,
git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
const git_proxy_options *proxy_opts,
int direction,
int flags);
@ -65,7 +67,7 @@ struct git_transport {
git_transport *transport);
/* Executes the push whose context is in the git_push object. */
int (*push)(git_transport *transport, git_push *push, const git_remote_callbacks *callbacks);
int(*push)(git_transport *transport, git_push *push, const git_remote_callbacks *callbacks);
/* This function may be called after a successful call to connect(), when
* the direction is FETCH. The function performs a negotiation to calculate

View File

@ -347,6 +347,15 @@ GIT_EXTERN(int) git_tag_peel(
git_object **tag_target_out,
const git_tag *tag);
/**
* Create an in-memory copy of a tag. The copy must be explicitly
* free'd or it will leak.
*
* @param out Pointer to store the copy of the tag
* @param source Original tag to copy
*/
GIT_EXTERN(int) git_tag_dup(git_tag **out, git_tag *source);
/** @} */
GIT_END_DECL
#endif

View File

@ -8,6 +8,14 @@
#define INCLUDE_git_transaction_h__
#include "common.h"
/**
* @file git2/transaction.h
* @brief Git transactional reference routines
* @defgroup git_transaction Git transactional reference routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
@ -107,5 +115,6 @@ GIT_EXTERN(int) git_transaction_commit(git_transaction *tx);
*/
GIT_EXTERN(void) git_transaction_free(git_transaction *tx);
/** @} */
GIT_END_DECL
#endif

View File

@ -409,6 +409,61 @@ GIT_EXTERN(int) git_tree_walk(
git_treewalk_cb callback,
void *payload);
/**
* Create an in-memory copy of a tree. The copy must be explicitly
* free'd or it will leak.
*
* @param out Pointer to store the copy of the tree
* @param source Original tree to copy
*/
GIT_EXTERN(int) git_tree_dup(git_tree **out, git_tree *source);
/**
* The kind of update to perform
*/
typedef enum {
/** Update or insert an entry at the specified path */
GIT_TREE_UPDATE_UPSERT,
/** Remove an entry from the specified path */
GIT_TREE_UPDATE_REMOVE,
} git_tree_update_t;
/**
* An action to perform during the update of a tree
*/
typedef struct {
/** Update action. If it's an removal, only the path is looked at */
git_tree_update_t action;
/** The entry's id */
git_oid id;
/** The filemode/kind of object */
git_filemode_t filemode;
/** The full path from the root tree */
const char *path;
} git_tree_update;
/**
* Create a tree based on another one with the specified modifications
*
* Given the `baseline` perform the changes described in the list of
* `updates` and create a new tree.
*
* This function is optimized for common file/directory addition, removal and
* replacement in trees. It is much more efficient than reading the tree into a
* `git_index` and modifying that, but in exchange it is not as flexible.
*
* Deleting and adding the same entry is undefined behaviour, changing
* a tree to a blob or viceversa is not supported.
*
* @param out id of the new tree
* @param repo the repository in which to create the tree, must be the
* same as for `baseline`
* @param baseline the tree to base these changes on
* @param nupdates the number of elements in the update list
* @param updates the list of updates to perform
*/
GIT_EXTERN(int) git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates);
/** @} */
GIT_END_DECL

View File

@ -7,12 +7,12 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
#define LIBGIT2_VERSION "0.24.5"
#define LIBGIT2_VERSION "0.25.1"
#define LIBGIT2_VER_MAJOR 0
#define LIBGIT2_VER_MINOR 24
#define LIBGIT2_VER_REVISION 5
#define LIBGIT2_VER_MINOR 25
#define LIBGIT2_VER_REVISION 1
#define LIBGIT2_VER_PATCH 0
#define LIBGIT2_SOVERSION 24
#define LIBGIT2_SOVERSION 25
#endif

View File

@ -1,9 +1,7 @@
#!/bin/sh
set -e
cd `dirname "$0"`/..
if [ "$ARCH" = "32" ]; then
echo 'C:\MinGW\ /MinGW' > /etc/fstab
elif [ "$ARCH" = "i686" ]; then
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

View File

@ -1,5 +1,7 @@
#!/bin/sh
set -x
if [ -n "$COVERITY" ];
then
./script/coverity.sh;
@ -10,10 +12,15 @@ if [ "$TRAVIS_OS_NAME" = "osx" ]; then
export PKG_CONFIG_PATH=$(ls -d /usr/local/Cellar/{curl,zlib}/*/lib/pkgconfig | paste -s -d':' -)
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 .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
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
@ -58,12 +65,22 @@ export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa"
export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE=""
if [ -e ./libgit2_clar ]; then
./libgit2_clar -sonline::push -sonline::clone::ssh_cert &&
./libgit2_clar -sonline::clone::ssh_with_paths || exit $?
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
./libgit2_clar -sonline::clone::cred_callback || exit $?
fi
# Use the proxy we started at the beginning
export GITTEST_REMOTE_PROXY_URL="http://foo:bar@localhost:8080/"
./libgit2_clar -sonline::clone::proxy_credentials_in_url || exit $?
export GITTEST_REMOTE_PROXY_URL="http://localhost:8080/"
export GITTEST_REMOTE_PROXY_USER="foo"
export GITTEST_REMOTE_PROXY_PASS="bar"
./libgit2_clar -sonline::clone::proxy_credentials_request || exit $?
fi
export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"

View File

@ -1,23 +1,22 @@
#!/bin/bash
set -e
# Only run this on our branches
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
[ -z "$COVERITY_TOKEN" ] && echo "Need to set a coverity token" && exit 1
# Only run this on our branches
echo "Pull request: $TRAVIS_PULL_REQUEST | Slug: $TRAVIS_REPO_SLUG"
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "libgit2/libgit2" ];
then
echo "Only analyzing 'development' on the main repo."
exit 0
fi
COV_VERSION=6.6.1
case $(uname -m) in
i?86) BITS=32 ;;
amd64|x86_64) BITS=64 ;;
esac
SCAN_TOOL=https://scan.coverity.com/download/linux-${BITS}
SCAN_TOOL=https://scan.coverity.com/download/cxx/linux${BITS}
TOOL_BASE=$(pwd)/_coverity-scan
# Install coverity tools

View File

@ -1,13 +0,0 @@
# CMake toolchain file for Win32 cross-compile
SET(CMAKE_SYSTEM_NAME Windows)
SET(CMAKE_C_COMPILER i586-mingw32msvc-gcc)
SET(CMAKE_RC_COMPILER i586-mingw32msvc-windres)
SET(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig)

75
script/user_model.c Normal file
View File

@ -0,0 +1,75 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
void *realloc(void *ptr, size_t size);
void *memmove(void *dest, const void *src, size_t n);
size_t strlen(const char *s);
typedef struct va_list_str *va_list;
typedef struct git_vector {
void **contents;
size_t length;
} git_vector;
typedef struct git_buf {
char *ptr;
size_t asize, size;
} git_buf;
int git_vector_insert(git_vector *v, void *element)
{
if (!v)
__coverity_panic__();
v->contents = realloc(v->contents, ++v->length);
if (!v->contents)
__coverity_panic__();
v->contents[v->length] = element;
return 0;
}
int git_buf_len(const struct git_buf *buf)
{
return strlen(buf->ptr);
}
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
char ch, *s;
size_t len;
__coverity_string_null_sink__(format);
__coverity_string_size_sink__(format);
ch = *format;
ch = *(char *)ap;
buf->ptr = __coverity_alloc__(len);
__coverity_writeall__(buf->ptr);
buf->size = len;
return 0;
}
int git_buf_put(git_buf *buf, const char *data, size_t len)
{
buf->ptr = __coverity_alloc__(buf->size + len + 1);
memmove(buf->ptr + buf->size, data, len);
buf->size += len;
buf->ptr[buf->size + len] = 0;
return 0;
}
int git_buf_set(git_buf *buf, const void *data, size_t len)
{
buf->ptr = __coverity_alloc__(len + 1);
memmove(buf->ptr, data, len);
buf->size = len + 1;
return 0;
}

View File

@ -20,41 +20,104 @@
static int annotated_commit_init(
git_annotated_commit **out,
git_repository *repo,
const git_oid *id,
const char *ref_name,
const char *remote_url)
git_commit *commit,
const char *description)
{
git_annotated_commit *annotated_commit;
git_commit *commit = NULL;
int error = 0;
assert(out && id);
assert(out && commit);
*out = NULL;
if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
(error = git_annotated_commit_from_commit(&annotated_commit,
commit)) < 0)
annotated_commit = git__calloc(1, sizeof(git_annotated_commit));
GITERR_CHECK_ALLOC(annotated_commit);
annotated_commit->type = GIT_ANNOTATED_COMMIT_REAL;
if ((error = git_commit_dup(&annotated_commit->commit, commit)) < 0)
goto done;
if (ref_name) {
annotated_commit->ref_name = git__strdup(ref_name);
GITERR_CHECK_ALLOC(annotated_commit->ref_name);
}
git_oid_fmt(annotated_commit->id_str, git_commit_id(commit));
annotated_commit->id_str[GIT_OID_HEXSZ] = '\0';
if (remote_url) {
annotated_commit->remote_url = git__strdup(remote_url);
GITERR_CHECK_ALLOC(annotated_commit->remote_url);
}
if (!description)
description = annotated_commit->id_str;
*out = annotated_commit;
annotated_commit->description = git__strdup(description);
GITERR_CHECK_ALLOC(annotated_commit->description);
done:
if (!error)
*out = annotated_commit;
return error;
}
static int annotated_commit_init_from_id(
git_annotated_commit **out,
git_repository *repo,
const git_oid *id,
const char *description)
{
git_commit *commit = NULL;
int error = 0;
assert(out && repo && id);
*out = NULL;
if ((error = git_commit_lookup(&commit, repo, id)) < 0)
goto done;
error = annotated_commit_init(out, commit, description);
done:
git_commit_free(commit);
return error;
}
int git_annotated_commit_lookup(
git_annotated_commit **out,
git_repository *repo,
const git_oid *id)
{
return annotated_commit_init_from_id(out, repo, id, NULL);
}
int git_annotated_commit_from_commit(
git_annotated_commit **out,
git_commit *commit)
{
return annotated_commit_init(out, commit, NULL);
}
int git_annotated_commit_from_revspec(
git_annotated_commit **out,
git_repository *repo,
const char *revspec)
{
git_object *obj, *commit;
int error;
assert(out && repo && revspec);
if ((error = git_revparse_single(&obj, repo, revspec)) < 0)
return error;
if ((error = git_object_peel(&commit, obj, GIT_OBJ_COMMIT))) {
git_object_free(obj);
return error;
}
error = annotated_commit_init(out, (git_commit *)commit, revspec);
git_object_free(obj);
git_object_free(commit);
return error;
}
int git_annotated_commit_from_ref(
git_annotated_commit **out,
git_repository *repo,
@ -70,8 +133,15 @@ int git_annotated_commit_from_ref(
if ((error = git_reference_resolve(&resolved, ref)) < 0)
return error;
error = annotated_commit_init(out, repo, git_reference_target(resolved),
git_reference_name(ref), NULL);
error = annotated_commit_init_from_id(out,
repo,
git_reference_target(resolved),
git_reference_name(ref));
if (!error) {
(*out)->ref_name = git__strdup(git_reference_name(ref));
GITERR_CHECK_ALLOC((*out)->ref_name);
}
git_reference_free(resolved);
return error;
@ -97,41 +167,6 @@ int git_annotated_commit_from_head(
return error;
}
int git_annotated_commit_from_commit(
git_annotated_commit **out,
git_commit *commit)
{
git_annotated_commit *annotated_commit;
assert(out && commit);
*out = NULL;
annotated_commit = git__calloc(1, sizeof(git_annotated_commit));
GITERR_CHECK_ALLOC(annotated_commit);
annotated_commit->type = GIT_ANNOTATED_COMMIT_REAL;
git_cached_obj_incref(commit);
annotated_commit->commit = commit;
git_oid_fmt(annotated_commit->id_str, git_commit_id(commit));
annotated_commit->id_str[GIT_OID_HEXSZ] = '\0';
*out = annotated_commit;
return 0;
}
int git_annotated_commit_lookup(
git_annotated_commit **out,
git_repository *repo,
const git_oid *id)
{
assert(out && repo && id);
return annotated_commit_init(out, repo, id, NULL, NULL);
}
int git_annotated_commit_from_fetchhead(
git_annotated_commit **out,
git_repository *repo,
@ -141,33 +176,16 @@ int git_annotated_commit_from_fetchhead(
{
assert(repo && id && branch_name && remote_url);
return annotated_commit_init(out, repo, id, branch_name, remote_url);
}
if (annotated_commit_init_from_id(out, repo, id, branch_name) < 0)
return -1;
int git_annotated_commit_from_revspec(
git_annotated_commit **out,
git_repository *repo,
const char *revspec)
{
git_object *obj, *commit;
int error;
(*out)->ref_name = git__strdup(branch_name);
GITERR_CHECK_ALLOC((*out)->ref_name);
assert(out && repo && revspec);
(*out)->remote_url = git__strdup(remote_url);
GITERR_CHECK_ALLOC((*out)->remote_url);
if ((error = git_revparse_single(&obj, repo, revspec)) < 0)
return error;
if ((error = git_object_peel(&commit, obj, GIT_OBJ_COMMIT))) {
git_object_free(obj);
return error;
}
error = annotated_commit_init(out, repo, git_object_id(commit), revspec, NULL);
git_object_free(obj);
git_object_free(commit);
return error;
return 0;
}
@ -187,8 +205,9 @@ void git_annotated_commit_free(git_annotated_commit *annotated_commit)
case GIT_ANNOTATED_COMMIT_REAL:
git_commit_free(annotated_commit->commit);
git_tree_free(annotated_commit->tree);
git__free(annotated_commit->ref_name);
git__free(annotated_commit->remote_url);
git__free((char *)annotated_commit->description);
git__free((char *)annotated_commit->ref_name);
git__free((char *)annotated_commit->remote_url);
break;
case GIT_ANNOTATED_COMMIT_VIRTUAL:
git_index_free(annotated_commit->index);

View File

@ -33,8 +33,11 @@ struct git_annotated_commit {
git_index *index;
git_array_oid_t parents;
char *ref_name;
char *remote_url;
/* how this commit was looked up */
const char *description;
const char *ref_name;
const char *remote_url;
char id_str[GIT_OID_HEXSZ+1];
};

377
src/apply.c Normal file
View File

@ -0,0 +1,377 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include <assert.h>
#include "git2/patch.h"
#include "git2/filter.h"
#include "array.h"
#include "patch.h"
#include "fileops.h"
#include "apply.h"
#include "delta.h"
#include "zstream.h"
#define apply_err(...) \
( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
typedef struct {
/* The lines that we allocate ourself are allocated out of the pool.
* (Lines may have been allocated out of the diff.)
*/
git_pool pool;
git_vector lines;
} patch_image;
static void patch_line_init(
git_diff_line *out,
const char *in,
size_t in_len,
size_t in_offset)
{
out->content = in;
out->content_len = in_len;
out->content_offset = in_offset;
}
#define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
static int patch_image_init_fromstr(
patch_image *out, const char *in, size_t in_len)
{
git_diff_line *line;
const char *start, *end;
memset(out, 0x0, sizeof(patch_image));
git_pool_init(&out->pool, sizeof(git_diff_line));
for (start = in; start < in + in_len; start = end) {
end = memchr(start, '\n', in_len);
if (end == NULL)
end = in + in_len;
else if (end < in + in_len)
end++;
line = git_pool_mallocz(&out->pool, 1);
GITERR_CHECK_ALLOC(line);
if (git_vector_insert(&out->lines, line) < 0)
return -1;
patch_line_init(line, start, (end - start), (start - in));
}
return 0;
}
static void patch_image_free(patch_image *image)
{
if (image == NULL)
return;
git_pool_clear(&image->pool);
git_vector_free(&image->lines);
}
static bool match_hunk(
patch_image *image,
patch_image *preimage,
size_t linenum)
{
bool match = 0;
size_t i;
/* Ensure this hunk is within the image boundaries. */
if (git_vector_length(&preimage->lines) + linenum >
git_vector_length(&image->lines))
return 0;
match = 1;
/* Check exact match. */
for (i = 0; i < git_vector_length(&preimage->lines); i++) {
git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);
if (preimage_line->content_len != image_line->content_len ||
memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
match = 0;
break;
}
}
return match;
}
static bool find_hunk_linenum(
size_t *out,
patch_image *image,
patch_image *preimage,
size_t linenum)
{
size_t max = git_vector_length(&image->lines);
bool match;
if (linenum > max)
linenum = max;
match = match_hunk(image, preimage, linenum);
*out = linenum;
return match;
}
static int update_hunk(
patch_image *image,
unsigned int linenum,
patch_image *preimage,
patch_image *postimage)
{
size_t postlen = git_vector_length(&postimage->lines);
size_t prelen = git_vector_length(&preimage->lines);
size_t i;
int error = 0;
if (postlen > prelen)
error = git_vector_insert_null(
&image->lines, linenum, (postlen - prelen));
else if (prelen > postlen)
error = git_vector_remove_range(
&image->lines, linenum, (prelen - postlen));
if (error) {
giterr_set_oom();
return -1;
}
for (i = 0; i < git_vector_length(&postimage->lines); i++) {
image->lines.contents[linenum + i] =
git_vector_get(&postimage->lines, i);
}
return 0;
}
static int apply_hunk(
patch_image *image,
git_patch *patch,
git_patch_hunk *hunk)
{
patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
size_t line_num, i;
int error = 0;
for (i = 0; i < hunk->line_count; i++) {
size_t linenum = hunk->line_start + i;
git_diff_line *line = git_array_get(patch->lines, linenum);
if (!line) {
error = apply_err("Preimage does not contain line %"PRIuZ, linenum);
goto done;
}
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
line->origin == GIT_DIFF_LINE_DELETION) {
if ((error = git_vector_insert(&preimage.lines, line)) < 0)
goto done;
}
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
line->origin == GIT_DIFF_LINE_ADDITION) {
if ((error = git_vector_insert(&postimage.lines, line)) < 0)
goto done;
}
}
line_num = hunk->hunk.new_start ? hunk->hunk.new_start - 1 : 0;
if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
error = apply_err("Hunk at line %d did not apply",
hunk->hunk.new_start);
goto done;
}
error = update_hunk(image, line_num, &preimage, &postimage);
done:
patch_image_free(&preimage);
patch_image_free(&postimage);
return error;
}
static int apply_hunks(
git_buf *out,
const char *source,
size_t source_len,
git_patch *patch)
{
git_patch_hunk *hunk;
git_diff_line *line;
patch_image image;
size_t i;
int error = 0;
if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
goto done;
git_array_foreach(patch->hunks, i, hunk) {
if ((error = apply_hunk(&image, patch, hunk)) < 0)
goto done;
}
git_vector_foreach(&image.lines, i, line)
git_buf_put(out, line->content, line->content_len);
done:
patch_image_free(&image);
return error;
}
static int apply_binary_delta(
git_buf *out,
const char *source,
size_t source_len,
git_diff_binary_file *binary_file)
{
git_buf inflated = GIT_BUF_INIT;
int error = 0;
/* no diff means identical contents */
if (binary_file->datalen == 0)
return git_buf_put(out, source, source_len);
error = git_zstream_inflatebuf(&inflated,
binary_file->data, binary_file->datalen);
if (!error && inflated.size != binary_file->inflatedlen) {
error = apply_err("inflated delta does not match expected length");
git_buf_free(out);
}
if (error < 0)
goto done;
if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
void *data;
size_t data_len;
error = git_delta_apply(&data, &data_len, (void *)source, source_len,
(void *)inflated.ptr, inflated.size);
out->ptr = data;
out->size = data_len;
out->asize = data_len;
}
else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
git_buf_swap(out, &inflated);
}
else {
error = apply_err("unknown binary delta type");
goto done;
}
done:
git_buf_free(&inflated);
return error;
}
static int apply_binary(
git_buf *out,
const char *source,
size_t source_len,
git_patch *patch)
{
git_buf reverse = GIT_BUF_INIT;
int error = 0;
if (!patch->binary.contains_data) {
error = apply_err("patch does not contain binary data");
goto done;
}
if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen)
goto done;
/* first, apply the new_file delta to the given source */
if ((error = apply_binary_delta(out, source, source_len,
&patch->binary.new_file)) < 0)
goto done;
/* second, apply the old_file delta to sanity check the result */
if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
&patch->binary.old_file)) < 0)
goto done;
if (source_len != reverse.size ||
memcmp(source, reverse.ptr, source_len) != 0) {
error = apply_err("binary patch did not apply cleanly");
goto done;
}
done:
if (error < 0)
git_buf_free(out);
git_buf_free(&reverse);
return error;
}
int git_apply__patch(
git_buf *contents_out,
char **filename_out,
unsigned int *mode_out,
const char *source,
size_t source_len,
git_patch *patch)
{
char *filename = NULL;
unsigned int mode = 0;
int error = 0;
assert(contents_out && filename_out && mode_out && (source || !source_len) && patch);
*filename_out = NULL;
*mode_out = 0;
if (patch->delta->status != GIT_DELTA_DELETED) {
const git_diff_file *newfile = &patch->delta->new_file;
filename = git__strdup(newfile->path);
mode = newfile->mode ?
newfile->mode : GIT_FILEMODE_BLOB;
}
if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
error = apply_binary(contents_out, source, source_len, patch);
else if (patch->hunks.size)
error = apply_hunks(contents_out, source, source_len, patch);
else
error = git_buf_put(contents_out, source, source_len);
if (error)
goto done;
if (patch->delta->status == GIT_DELTA_DELETED &&
git_buf_len(contents_out) > 0) {
error = apply_err("removal patch leaves file contents");
goto done;
}
*filename_out = filename;
*mode_out = mode;
done:
if (error < 0)
git__free(filename);
return error;
}

21
src/apply.h Normal file
View File

@ -0,0 +1,21 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_apply_h__
#define INCLUDE_apply_h__
#include "git2/patch.h"
#include "buffer.h"
extern int git_apply__patch(
git_buf *out,
char **filename,
unsigned int *mode,
const char *source,
size_t source_len,
git_patch *patch);
#endif

View File

@ -85,7 +85,6 @@ on_oom:
#define git_array_foreach(a, i, element) \
for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
GIT_INLINE(int) git_array__search(
size_t *out,
void *array_ptr,

View File

@ -274,66 +274,98 @@ int git_blob_create_fromdisk(
return error;
}
#define BUFFER_SIZE 4096
typedef struct {
git_writestream parent;
git_filebuf fbuf;
git_repository *repo;
char *hintpath;
} blob_writestream;
int git_blob_create_fromchunks(
git_oid *id,
git_repository *repo,
const char *hintpath,
int (*source_cb)(char *content, size_t max_length, void *payload),
void *payload)
static int blob_writestream_close(git_writestream *_stream)
{
blob_writestream *stream = (blob_writestream *) _stream;
git_filebuf_cleanup(&stream->fbuf);
return 0;
}
static void blob_writestream_free(git_writestream *_stream)
{
blob_writestream *stream = (blob_writestream *) _stream;
git_filebuf_cleanup(&stream->fbuf);
git__free(stream->hintpath);
git__free(stream);
}
static int blob_writestream_write(git_writestream *_stream, const char *buffer, size_t len)
{
blob_writestream *stream = (blob_writestream *) _stream;
return git_filebuf_write(&stream->fbuf, buffer, len);
}
int git_blob_create_fromstream(git_writestream **out, git_repository *repo, const char *hintpath)
{
int error;
char *content = NULL;
git_filebuf file = GIT_FILEBUF_INIT;
git_buf path = GIT_BUF_INIT;
blob_writestream *stream;
assert(id && repo && source_cb);
assert(out && repo);
if ((error = git_buf_joinpath(
&path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0)
goto cleanup;
stream = git__calloc(1, sizeof(blob_writestream));
GITERR_CHECK_ALLOC(stream);
content = git__malloc(BUFFER_SIZE);
GITERR_CHECK_ALLOC(content);
if ((error = git_filebuf_open(
&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666)) < 0)
goto cleanup;
while (1) {
int read_bytes = source_cb(content, BUFFER_SIZE, payload);
if (!read_bytes)
break;
if (read_bytes > BUFFER_SIZE) {
giterr_set(GITERR_OBJECT, "Invalid chunk size while creating blob");
error = GIT_EBUFS;
} else if (read_bytes < 0) {
error = giterr_set_after_callback(read_bytes);
} else {
error = git_filebuf_write(&file, content, read_bytes);
}
if (error < 0)
goto cleanup;
if (hintpath) {
stream->hintpath = git__strdup(hintpath);
GITERR_CHECK_ALLOC(stream->hintpath);
}
if ((error = git_filebuf_flush(&file)) < 0)
stream->repo = repo;
stream->parent.write = blob_writestream_write;
stream->parent.close = blob_writestream_close;
stream->parent.free = blob_writestream_free;
if ((error = git_buf_joinpath(&path,
git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0)
goto cleanup;
error = git_blob__create_from_paths(
id, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL);
if ((error = git_filebuf_open_withsize(&stream->fbuf, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY,
0666, 2 * 1024 * 1024)) < 0)
goto cleanup;
*out = (git_writestream *) stream;
cleanup:
git_buf_free(&path);
git_filebuf_cleanup(&file);
git__free(content);
if (error < 0)
blob_writestream_free((git_writestream *) stream);
git_buf_free(&path);
return error;
}
int git_blob_create_fromstream_commit(git_oid *out, git_writestream *_stream)
{
int error;
blob_writestream *stream = (blob_writestream *) _stream;
/*
* We can make this more officient by avoiding writing to
* disk, but for now let's re-use the helper functions we
* have.
*/
if ((error = git_filebuf_flush(&stream->fbuf)) < 0)
goto cleanup;
error = git_blob__create_from_paths(out, NULL, stream->repo, stream->fbuf.path_lock,
stream->hintpath, 0, !!stream->hintpath);
cleanup:
blob_writestream_free(_stream);
return error;
}
int git_blob_is_binary(const git_blob *blob)
{
git_buf content = GIT_BUF_INIT;

View File

@ -58,16 +58,17 @@ static int create_branch(
const char *from,
int force)
{
int is_head = 0;
int is_unmovable_head = 0;
git_reference *branch = NULL;
git_buf canonical_branch_name = GIT_BUF_INIT,
log_message = GIT_BUF_INIT;
int error = -1;
int bare = git_repository_is_bare(repository);
assert(branch_name && commit && ref_out);
assert(git_object_owner((const git_object *)commit) == repository);
if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
error = git_branch_is_head(branch);
git_reference_free(branch);
branch = NULL;
@ -75,10 +76,10 @@ static int create_branch(
if (error < 0)
goto cleanup;
is_head = error;
is_unmovable_head = error;
}
if (is_head && force) {
if (is_unmovable_head && force) {
giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
"the current HEAD of the repository.", branch_name);
error = -1;
@ -121,7 +122,8 @@ int git_branch_create_from_annotated(
const git_annotated_commit *commit,
int force)
{
return create_branch(ref_out, repository, branch_name, commit->commit, commit->ref_name, force);
return create_branch(ref_out,
repository, branch_name, commit->commit, commit->description, force);
}
int git_branch_delete(git_reference *branch)

View File

@ -273,42 +273,51 @@ int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
return 0;
}
/* The inverse of base64_encode, offset by '+' == 43. */
/* The inverse of base64_encode */
static const int8_t base64_decode[] = {
62,
-1, -1, -1,
63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-1, -1, -1, 0, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
-1, -1, -1, -1, -1, -1,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
#define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43])
int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
{
size_t i;
int8_t a, b, c, d;
size_t orig_size = buf->size, new_size;
if (len % 4) {
giterr_set(GITERR_INVALID, "invalid base64 input");
return -1;
}
assert(len % 4 == 0);
GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
ENSURE_SIZE(buf, new_size);
for (i = 0; i < len; i += 4) {
if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
(b = BASE64_DECODE_VALUE(base64[i+1])) < 0 ||
(c = BASE64_DECODE_VALUE(base64[i+2])) < 0 ||
(d = BASE64_DECODE_VALUE(base64[i+3])) < 0) {
if ((a = base64_decode[(unsigned char)base64[i]]) < 0 ||
(b = base64_decode[(unsigned char)base64[i+1]]) < 0 ||
(c = base64_decode[(unsigned char)base64[i+2]]) < 0 ||
(d = base64_decode[(unsigned char)base64[i+3]]) < 0) {
buf->size = orig_size;
buf->ptr[buf->size] = '\0';
giterr_set(GITERR_INVALID, "Invalid base64 input");
giterr_set(GITERR_INVALID, "invalid base64 input");
return -1;
}
@ -321,7 +330,7 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
return 0;
}
static const char b85str[] =
static const char base85_encode[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
@ -351,7 +360,7 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
int val = acc % 85;
acc /= 85;
b85[i] = b85str[val];
b85[i] = base85_encode[val];
}
for (i = 0; i < 5; i++)
@ -363,6 +372,88 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
return 0;
}
/* The inverse of base85_encode */
static const int8_t base85_decode[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 63, -1, 64, 65, 66, 67, -1, 68, 69, 70, 71, -1, 72, -1, -1,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 73, 74, 75, 76, 77,
78, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, 79, 80,
81, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
int git_buf_decode_base85(
git_buf *buf,
const char *base85,
size_t base85_len,
size_t output_len)
{
size_t orig_size = buf->size, new_size;
if (base85_len % 5 ||
output_len > base85_len * 4 / 5) {
giterr_set(GITERR_INVALID, "invalid base85 input");
return -1;
}
GITERR_CHECK_ALLOC_ADD(&new_size, output_len, buf->size);
GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
ENSURE_SIZE(buf, new_size);
while (output_len) {
unsigned acc = 0;
int de, cnt = 4;
unsigned char ch;
do {
ch = *base85++;
de = base85_decode[ch];
if (--de < 0)
goto on_error;
acc = acc * 85 + de;
} while (--cnt);
ch = *base85++;
de = base85_decode[ch];
if (--de < 0)
goto on_error;
/* Detect overflow. */
if (0xffffffff / 85 < acc ||
0xffffffff - de < (acc *= 85))
goto on_error;
acc += de;
cnt = (output_len < 4) ? output_len : 4;
output_len -= cnt;
do {
acc = (acc << 8) | (acc >> 24);
buf->ptr[buf->size++] = acc;
} while (--cnt);
}
buf->ptr[buf->size] = 0;
return 0;
on_error:
buf->size = orig_size;
buf->ptr[buf->size] = '\0';
giterr_set(GITERR_INVALID, "invalid base85 input");
return -1;
}
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
size_t expected_size, new_size;
@ -766,3 +857,144 @@ int git_buf_splice(
buf->ptr[buf->size] = '\0';
return 0;
}
/* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
int git_buf_quote(git_buf *buf)
{
const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
git_buf quoted = GIT_BUF_INIT;
size_t i = 0;
bool quote = false;
int error = 0;
/* walk to the first char that needs quoting */
if (buf->size && buf->ptr[0] == '!')
quote = true;
for (i = 0; !quote && i < buf->size; i++) {
if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
quote = true;
break;
}
}
if (!quote)
goto done;
git_buf_putc(&quoted, '"');
git_buf_put(&quoted, buf->ptr, i);
for (; i < buf->size; i++) {
/* whitespace - use the map above, which is ordered by ascii value */
if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
git_buf_putc(&quoted, '\\');
git_buf_putc(&quoted, whitespace[buf->ptr[i] - '\a']);
}
/* double quote and backslash must be escaped */
else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
git_buf_putc(&quoted, '\\');
git_buf_putc(&quoted, buf->ptr[i]);
}
/* escape anything unprintable as octal */
else if (buf->ptr[i] != ' ' &&
(buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
git_buf_printf(&quoted, "\\%03o", (unsigned char)buf->ptr[i]);
}
/* yay, printable! */
else {
git_buf_putc(&quoted, buf->ptr[i]);
}
}
git_buf_putc(&quoted, '"');
if (git_buf_oom(&quoted)) {
error = -1;
goto done;
}
git_buf_swap(&quoted, buf);
done:
git_buf_free(&quoted);
return error;
}
/* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
int git_buf_unquote(git_buf *buf)
{
size_t i, j;
char ch;
git_buf_rtrim(buf);
if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
goto invalid;
for (i = 0, j = 1; j < buf->size-1; i++, j++) {
ch = buf->ptr[j];
if (ch == '\\') {
if (j == buf->size-2)
goto invalid;
ch = buf->ptr[++j];
switch (ch) {
/* \" or \\ simply copy the char in */
case '"': case '\\':
break;
/* add the appropriate escaped char */
case 'a': ch = '\a'; break;
case 'b': ch = '\b'; break;
case 'f': ch = '\f'; break;
case 'n': ch = '\n'; break;
case 'r': ch = '\r'; break;
case 't': ch = '\t'; break;
case 'v': ch = '\v'; break;
/* \xyz digits convert to the char*/
case '0': case '1': case '2': case '3':
if (j == buf->size-3) {
giterr_set(GITERR_INVALID,
"Truncated quoted character \\%c", ch);
return -1;
}
if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
giterr_set(GITERR_INVALID,
"Truncated quoted character \\%c%c%c",
buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
return -1;
}
ch = ((buf->ptr[j] - '0') << 6) |
((buf->ptr[j+1] - '0') << 3) |
(buf->ptr[j+2] - '0');
j += 2;
break;
default:
giterr_set(GITERR_INVALID, "Invalid quoted character \\%c", ch);
return -1;
}
}
buf->ptr[i] = ch;
}
buf->ptr[i] = '\0';
buf->size = i;
return 0;
invalid:
giterr_set(GITERR_INVALID, "Invalid quoted line");
return -1;
}

View File

@ -173,6 +173,12 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Quote and unquote a buffer as specified in
* http://marc.info/?l=git&m=112927316408690&w=2
*/
int git_buf_quote(git_buf *buf);
int git_buf_unquote(git_buf *buf);
/* Write data as base64 encoded in buffer */
int git_buf_encode_base64(git_buf *buf, const char *data, size_t len);
/* Decode the given bas64 and write the result to the buffer */
@ -180,6 +186,8 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len);
/* Write data as "base85" encoded in buffer */
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len);
/* Decode the given "base85" and write the result to the buffer */
int git_buf_decode_base85(git_buf *buf, const char *base64, size_t len, size_t output_len);
/*
* Insert, remove or replace a portion of the buffer.

View File

@ -26,6 +26,7 @@
#include "filter.h"
#include "blob.h"
#include "diff.h"
#include "diff_generate.h"
#include "pathspec.h"
#include "buf_text.h"
#include "diff_xdiff.h"
@ -66,8 +67,8 @@ typedef struct {
git_vector update_conflicts;
git_vector *update_reuc;
git_vector *update_names;
git_buf path;
size_t workdir_len;
git_buf target_path;
size_t target_len;
git_buf tmp;
unsigned int strategy;
int can_symlink;
@ -211,6 +212,10 @@ static bool checkout_is_workdir_modified(
if (baseitem->size && wditem->file_size != baseitem->size)
return true;
/* if the workdir item is a directory, it cannot be a modified file */
if (S_ISDIR(wditem->mode))
return false;
if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
return false;
@ -294,14 +299,30 @@ static int checkout_action_no_wd(
return checkout_action_common(action, data, delta, NULL);
}
static bool wd_item_is_removable(git_iterator *iter, const git_index_entry *wd)
static int checkout_target_fullpath(
git_buf **out, checkout_data *data, const char *path)
{
git_buf *full = NULL;
git_buf_truncate(&data->target_path, data->target_len);
if (path && git_buf_puts(&data->target_path, path) < 0)
return -1;
*out = &data->target_path;
return 0;
}
static bool wd_item_is_removable(
checkout_data *data, const git_index_entry *wd)
{
git_buf *full;
if (wd->mode != GIT_FILEMODE_TREE)
return true;
if (git_iterator_current_workdir_path(&full, iter) < 0)
return true;
if (checkout_target_fullpath(&full, data, wd->path) < 0)
return false;
return !full || !git_path_contains(full, DOT_GIT);
}
@ -363,14 +384,14 @@ static int checkout_action_wd_only(
if ((error = checkout_notify(data, notify, NULL, wd)) != 0)
return error;
if (remove && wd_item_is_removable(workdir, wd))
if (remove && wd_item_is_removable(data, wd))
error = checkout_queue_remove(data, wd->path);
if (!error)
error = git_iterator_advance(wditem, workdir);
} else {
/* untracked or ignored - can't know which until we advance through */
bool over = false, removable = wd_item_is_removable(workdir, wd);
bool over = false, removable = wd_item_is_removable(data, wd);
git_iterator_status_t untracked_state;
/* copy the entry for issuing notification callback later */
@ -378,7 +399,7 @@ static int checkout_action_wd_only(
git_buf_sets(&data->tmp, wd->path);
saved_wd.path = data->tmp.ptr;
error = git_iterator_advance_over_with_status(
error = git_iterator_advance_over(
wditem, &untracked_state, workdir);
if (error == GIT_ITEROVER)
over = true;
@ -428,10 +449,12 @@ static bool submodule_is_config_only(
static bool checkout_is_empty_dir(checkout_data *data, const char *path)
{
git_buf_truncate(&data->path, data->workdir_len);
if (git_buf_puts(&data->path, path) < 0)
git_buf *fullpath;
if (checkout_target_fullpath(&fullpath, data, path) < 0)
return false;
return git_path_is_empty_dir(data->path.ptr);
return git_path_is_empty_dir(fullpath->ptr);
}
static int checkout_action_with_wd(
@ -640,7 +663,7 @@ static int checkout_action(
if (cmp == 0) {
if (wd->mode == GIT_FILEMODE_TREE) {
/* case 2 - entry prefixed by workdir tree */
error = git_iterator_advance_into_or_over(wditem, workdir);
error = git_iterator_advance_into(wditem, workdir);
if (error < 0 && error != GIT_ITEROVER)
goto done;
continue;
@ -913,7 +936,7 @@ static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, g
git_index *index;
/* Only write conficts from sources that have them: indexes. */
if ((index = git_iterator_get_index(data->target)) == NULL)
if ((index = git_iterator_index(data->target)) == NULL)
return 0;
data->update_conflicts._cmp = checkout_conflictdata_cmp;
@ -1063,7 +1086,7 @@ static int checkout_conflicts_coalesce_renames(
size_t i, names;
int error = 0;
if ((index = git_iterator_get_index(data->target)) == NULL)
if ((index = git_iterator_index(data->target)) == NULL)
return 0;
/* Juggle entries based on renames */
@ -1121,7 +1144,7 @@ static int checkout_conflicts_mark_directoryfile(
const char *path;
int prefixed, error = 0;
if ((index = git_iterator_get_index(data->target)) == NULL)
if ((index = git_iterator_index(data->target)) == NULL)
return 0;
len = git_index_entrycount(index);
@ -1585,18 +1608,18 @@ static int checkout_submodule_update_index(
checkout_data *data,
const git_diff_file *file)
{
git_buf *fullpath;
struct stat st;
/* update the index unless prevented */
if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0)
return 0;
git_buf_truncate(&data->path, data->workdir_len);
if (git_buf_puts(&data->path, file->path) < 0)
if (checkout_target_fullpath(&fullpath, data, file->path) < 0)
return -1;
data->perfdata.stat_calls++;
if (p_stat(git_buf_cstr(&data->path), &st) < 0) {
if (p_stat(fullpath->ptr, &st) < 0) {
giterr_set(
GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
return GIT_ENOTFOUND;
@ -1721,22 +1744,23 @@ static int checkout_blob(
checkout_data *data,
const git_diff_file *file)
{
int error = 0;
git_buf *fullpath;
struct stat st;
int error = 0;
git_buf_truncate(&data->path, data->workdir_len);
if (git_buf_puts(&data->path, file->path) < 0)
if (checkout_target_fullpath(&fullpath, data, file->path) < 0)
return -1;
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
int rval = checkout_safe_for_update_only(
data, git_buf_cstr(&data->path), file->mode);
data, fullpath->ptr, file->mode);
if (rval <= 0)
return rval;
}
error = checkout_write_content(
data, &file->id, git_buf_cstr(&data->path), NULL, file->mode, &st);
data, &file->id, fullpath->ptr, NULL, file->mode, &st);
/* update the index unless prevented */
if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
@ -1757,18 +1781,21 @@ static int checkout_remove_the_old(
git_diff_delta *delta;
const char *str;
size_t i;
const char *workdir = git_buf_cstr(&data->path);
git_buf *fullpath;
uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
if (data->opts.checkout_strategy & GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES)
flg |= GIT_RMDIR_SKIP_NONEMPTY;
git_buf_truncate(&data->path, data->workdir_len);
if (checkout_target_fullpath(&fullpath, data, NULL) < 0)
return -1;
git_vector_foreach(&data->diff->deltas, i, delta) {
if (actions[i] & CHECKOUT_ACTION__REMOVE) {
error = git_futils_rmdir_r(delta->old_file.path, workdir, flg);
error = git_futils_rmdir_r(
delta->old_file.path, fullpath->ptr, flg);
if (error < 0)
return error;
@ -1785,7 +1812,7 @@ static int checkout_remove_the_old(
}
git_vector_foreach(&data->removes, i, str) {
error = git_futils_rmdir_r(str, workdir, flg);
error = git_futils_rmdir_r(str, fullpath->ptr, flg);
if (error < 0)
return error;
@ -1939,7 +1966,7 @@ static int checkout_path_suffixed(git_buf *path, const char *suffix)
if (i == INT_MAX) {
git_buf_truncate(path, path_len);
giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path);
giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path->ptr);
return GIT_EEXISTS;
}
@ -1952,13 +1979,13 @@ static int checkout_write_entry(
const git_index_entry *side)
{
const char *hint_path = NULL, *suffix;
git_buf *fullpath;
struct stat st;
int error;
assert (side == conflict->ours || side == conflict->theirs);
git_buf_truncate(&data->path, data->workdir_len);
if (git_buf_puts(&data->path, side->path) < 0)
if (checkout_target_fullpath(&fullpath, data, side->path) < 0)
return -1;
if ((conflict->name_collision || conflict->directoryfile) &&
@ -1972,18 +1999,18 @@ static int checkout_write_entry(
suffix = data->opts.their_label ? data->opts.their_label :
"theirs";
if (checkout_path_suffixed(&data->path, suffix) < 0)
if (checkout_path_suffixed(fullpath, suffix) < 0)
return -1;
hint_path = side->path;
}
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
(error = checkout_safe_for_update_only(data, git_buf_cstr(&data->path), side->mode)) <= 0)
(error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0)
return error;
return checkout_write_content(data,
&side->id, git_buf_cstr(&data->path), hint_path, side->mode, &st);
&side->id, fullpath->ptr, hint_path, side->mode, &st);
}
static int checkout_write_entries(
@ -2296,7 +2323,7 @@ static void checkout_data_clear(checkout_data *data)
git_strmap_free(data->mkdir_map);
git_buf_free(&data->path);
git_buf_free(&data->target_path);
git_buf_free(&data->tmp);
git_index_free(data->index);
@ -2359,7 +2386,7 @@ static int checkout_data_init(
if ((error = git_repository_index(&data->index, data->repo)) < 0)
goto cleanup;
if (data->index != git_iterator_get_index(target)) {
if (data->index != git_iterator_index(target)) {
if ((error = git_index_read(data->index, true)) < 0)
goto cleanup;
@ -2442,7 +2469,7 @@ static int checkout_data_init(
data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
else {
giterr_set(GITERR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'",
conflict_style);
conflict_style->value);
error = -1;
git_config_entry_free(conflict_style);
goto cleanup;
@ -2455,12 +2482,12 @@ static int checkout_data_init(
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
(error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 ||
(error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 ||
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
(error = git_path_to_dir(&data->path)) < 0 ||
(error = git_buf_puts(&data->target_path, data->opts.target_directory)) < 0 ||
(error = git_path_to_dir(&data->target_path)) < 0 ||
(error = git_strmap_alloc(&data->mkdir_map)) < 0)
goto cleanup;
data->workdir_len = git_buf_len(&data->path);
data->target_len = git_buf_len(&data->target_path);
git_attr_session__init(&data->attr_session, data->repo);
@ -2516,7 +2543,7 @@ int git_checkout_iterator(
workdir_opts.start = data.pfx;
workdir_opts.end = data.pfx;
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
if ((error = git_iterator_reset_range(target, data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_workdir_ext(
&workdir, data.repo, data.opts.target_directory, index, NULL,
&workdir_opts)) < 0)
@ -2586,7 +2613,7 @@ int git_checkout_iterator(
(error = checkout_create_conflicts(&data)) < 0)
goto cleanup;
if (data.index != git_iterator_get_index(target) &&
if (data.index != git_iterator_index(target) &&
(error = checkout_extensions_update_index(&data)) < 0)
goto cleanup;

View File

@ -461,14 +461,6 @@ int git_clone_init_options(git_clone_options *opts, unsigned int version)
return 0;
}
static const char *repository_base(git_repository *repo)
{
if (git_repository_is_bare(repo))
return git_repository_path(repo);
return git_repository_workdir(repo);
}
static bool can_link(const char *src, const char *dst, int link)
{
#ifdef GIT_WIN32

View File

@ -18,6 +18,7 @@
#include "message.h"
#include "refs.h"
#include "object.h"
#include "oidarray.h"
void git_commit__free(void *_commit)
{
@ -37,6 +38,84 @@ void git_commit__free(void *_commit)
git__free(commit);
}
static int git_commit__create_buffer_internal(
git_buf *out,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message,
const git_oid *tree,
git_array_oid_t *parents)
{
size_t i = 0;
const git_oid *parent;
assert(out && tree);
git_oid__writebuf(out, "tree ", tree);
for (i = 0; i < git_array_size(*parents); i++) {
parent = git_array_get(*parents, i);
git_oid__writebuf(out, "parent ", parent);
}
git_signature__writebuf(out, "author ", author);
git_signature__writebuf(out, "committer ", committer);
if (message_encoding != NULL)
git_buf_printf(out, "encoding %s\n", message_encoding);
git_buf_putc(out, '\n');
if (git_buf_puts(out, message) < 0)
goto on_error;
return 0;
on_error:
git_buf_free(out);
return -1;
}
static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
git_commit_parent_callback parent_cb, void *parent_payload,
const git_oid *current_id, bool validate)
{
size_t i;
int error;
git_oid *parent_cpy;
const git_oid *parent;
if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
return -1;
i = 0;
while ((parent = parent_cb(i, parent_payload)) != NULL) {
if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
error = -1;
goto on_error;
}
parent_cpy = git_array_alloc(*parents);
GITERR_CHECK_ALLOC(parent_cpy);
git_oid_cpy(parent_cpy, parent);
i++;
}
if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) {
giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
error = GIT_EMODIFIED;
goto on_error;
}
return 0;
on_error:
git_array_clear(*parents);
return error;
}
static int git_commit__create_internal(
git_oid *id,
git_repository *repo,
@ -50,18 +129,12 @@ static int git_commit__create_internal(
void *parent_payload,
bool validate)
{
git_reference *ref = NULL;
int error = 0, matched_parent = 0;
const git_oid *current_id = NULL;
git_buf commit = GIT_BUF_INIT;
size_t i = 0;
int error;
git_odb *odb;
const git_oid *parent;
assert(id && repo && tree && parent_cb);
if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
return -1;
git_reference *ref = NULL;
git_buf buf = GIT_BUF_INIT;
const git_oid *current_id = NULL;
git_array_oid_t parents = GIT_ARRAY_INIT;
if (update_ref) {
error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
@ -73,58 +146,34 @@ static int git_commit__create_internal(
if (ref)
current_id = git_reference_target(ref);
git_oid__writebuf(&commit, "tree ", tree);
if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
goto cleanup;
while ((parent = parent_cb(i, parent_payload)) != NULL) {
if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
error = -1;
goto on_error;
}
error = git_commit__create_buffer_internal(&buf, author, committer,
message_encoding, message, tree,
&parents);
git_oid__writebuf(&commit, "parent ", parent);
if (i == 0 && current_id && git_oid_equal(current_id, parent))
matched_parent = 1;
i++;
}
if (ref && !matched_parent) {
git_reference_free(ref);
git_buf_free(&commit);
giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
return GIT_EMODIFIED;
}
git_signature__writebuf(&commit, "author ", author);
git_signature__writebuf(&commit, "committer ", committer);
if (message_encoding != NULL)
git_buf_printf(&commit, "encoding %s\n", message_encoding);
git_buf_putc(&commit, '\n');
if (git_buf_puts(&commit, message) < 0)
goto on_error;
if (error < 0)
goto cleanup;
if (git_repository_odb__weakptr(&odb, repo) < 0)
goto on_error;
goto cleanup;
if (git_odb_write(id, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
goto on_error;
if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0)
goto cleanup;
git_buf_free(&commit);
if (update_ref != NULL) {
error = git_reference__update_for_commit(
repo, ref, update_ref, id, "commit");
git_reference_free(ref);
return error;
goto cleanup;
}
return 0;
on_error:
git_buf_free(&commit);
return -1;
cleanup:
git_array_clear(parents);
git_reference_free(ref);
git_buf_free(&buf);
return error;
}
int git_commit_create_from_callback(
@ -566,7 +615,7 @@ int git_commit_nth_gen_ancestor(
assert(ancestor && commit);
if (git_object_dup((git_object **) &current, (git_object *) commit) < 0)
if (git_commit_dup(&current, (git_commit *)commit) < 0)
return -1;
if (n == 0) {
@ -740,3 +789,98 @@ cleanup:
git_buf_clear(signed_data);
return error;
}
int git_commit_create_buffer(git_buf *out,
git_repository *repo,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message,
const git_tree *tree,
size_t parent_count,
const git_commit *parents[])
{
int error;
commit_parent_data data = { parent_count, parents, repo };
git_array_oid_t parents_arr = GIT_ARRAY_INIT;
const git_oid *tree_id;
assert(tree && git_tree_owner(tree) == repo);
tree_id = git_tree_id(tree);
if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
return error;
error = git_commit__create_buffer_internal(
out, author, committer,
message_encoding, message, tree_id,
&parents_arr);
git_array_clear(parents_arr);
return error;
}
/**
* Append to 'out' properly marking continuations when there's a newline in 'content'
*/
static void format_header_field(git_buf *out, const char *field, const char *content)
{
const char *lf;
assert(out && field && content);
git_buf_puts(out, field);
git_buf_putc(out, ' ');
while ((lf = strchr(content, '\n')) != NULL) {
git_buf_put(out, content, lf - content);
git_buf_puts(out, "\n ");
content = lf + 1;
}
git_buf_puts(out, content);
git_buf_putc(out, '\n');
}
int git_commit_create_with_signature(
git_oid *out,
git_repository *repo,
const char *commit_content,
const char *signature,
const char *signature_field)
{
git_odb *odb;
int error = 0;
const char *field;
const char *header_end;
git_buf commit = GIT_BUF_INIT;
/* We start by identifying the end of the commit header */
header_end = strstr(commit_content, "\n\n");
if (!header_end) {
giterr_set(GITERR_INVALID, "malformed commit contents");
return -1;
}
field = signature_field ? signature_field : "gpgsig";
/* The header ends after the first LF */
header_end++;
git_buf_put(&commit, commit_content, header_end - commit_content);
format_header_field(&commit, field, signature);
git_buf_puts(&commit, header_end);
if (git_buf_oom(&commit))
return -1;
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
goto cleanup;
if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT)) < 0)
goto cleanup;
cleanup:
git_buf_free(&commit);
return error;
}

View File

@ -13,10 +13,15 @@
int git_commit_list_time_cmp(const void *a, const void *b)
{
const git_commit_list_node *commit_a = a;
const git_commit_list_node *commit_b = b;
int64_t time_a = ((git_commit_list_node *) a)->time;
int64_t time_b = ((git_commit_list_node *) b)->time;
return (commit_a->time < commit_b->time);
if (time_a < time_b)
return 1;
if (time_a > time_b)
return -1;
return 0;
}
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)

View File

@ -28,6 +28,7 @@ typedef struct git_commit_list_node {
uninteresting:1,
topo_delay:1,
parsed:1,
added:1,
flags : FLAG_BITS;
unsigned short in_degree;

View File

@ -103,7 +103,8 @@
/**
* Set the error message for this thread, formatting as needed.
*/
void giterr_set(int error_class, const char *string, ...);
void giterr_set(int error_class, const char *string, ...) GIT_FORMAT_PRINTF(2, 3);
/**
* Set the error message for a regex failure, using the internal regex

View File

@ -478,7 +478,7 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf
iter = git__calloc(1, sizeof(all_iter));
GITERR_CHECK_ALLOC(iter);
if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) != 0) {
if ((result = p_regcomp(&iter->regex, regexp, REG_EXTENDED)) != 0) {
giterr_set_regex(&iter->regex, result);
git__free(iter);
return -1;
@ -512,7 +512,7 @@ int git_config_backend_foreach_match(
int error = 0;
if (regexp != NULL) {
if ((error = regcomp(&regex, regexp, REG_EXTENDED)) != 0) {
if ((error = p_regcomp(&regex, regexp, REG_EXTENDED)) != 0) {
giterr_set_regex(&regex, error);
regfree(&regex);
return -1;
@ -1003,7 +1003,7 @@ int git_config_multivar_iterator_new(git_config_iterator **out, const git_config
goto on_error;
if (regexp != NULL) {
error = regcomp(&iter->regex, regexp, REG_EXTENDED);
error = p_regcomp(&iter->regex, regexp, REG_EXTENDED);
if (error != 0) {
giterr_set_regex(&iter->regex, error);
error = -1;

View File

@ -232,7 +232,10 @@ static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
{
refcounted_strmap *map;
git_mutex_lock(&h->values_mutex);
if (git_mutex_lock(&h->values_mutex) < 0) {
giterr_set(GITERR_OS, "Failed to lock config backend");
return NULL;
}
map = h->values;
git_atomic_inc(&map->refcount);
@ -318,7 +321,10 @@ static int config__refresh(git_config_backend *cfg)
if ((error = config_read(values->values, b, reader, b->level, 0)) < 0)
goto out;
git_mutex_lock(&b->header.values_mutex);
if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
giterr_set(GITERR_OS, "Failed to lock config backend");
goto out;
}
tmp = b->header.values;
b->header.values = values;
@ -460,7 +466,8 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
if ((rval = git_config__normalize_name(name, &key)) < 0)
return rval;
map = refcounted_strmap_take(&b->header);
if ((map = refcounted_strmap_take(&b->header)) == NULL)
return -1;
values = map->values;
/*
@ -527,7 +534,8 @@ static int config_get(git_config_backend *cfg, const char *key, git_config_entry
if (!h->parent.readonly && ((error = config_refresh(cfg)) < 0))
return error;
map = refcounted_strmap_take(h);
if ((map = refcounted_strmap_take(h)) == NULL)
return -1;
values = map->values;
pos = git_strmap_lookup_index(values, key);
@ -562,7 +570,7 @@ static int config_set_multivar(
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
result = regcomp(&preg, regexp, REG_EXTENDED);
result = p_regcomp(&preg, regexp, REG_EXTENDED);
if (result != 0) {
giterr_set_regex(&preg, result);
result = -1;
@ -594,7 +602,8 @@ static int config_delete(git_config_backend *cfg, const char *name)
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
map = refcounted_strmap_take(&b->header);
if ((map = refcounted_strmap_take(&b->header)) == NULL)
return -1;
values = b->header.values->values;
pos = git_strmap_lookup_index(values, key);
@ -633,7 +642,8 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
map = refcounted_strmap_take(&b->header);
if ((map = refcounted_strmap_take(&b->header)) == NULL)
return -1;
values = b->header.values->values;
pos = git_strmap_lookup_index(values, key);
@ -647,7 +657,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
refcounted_strmap_free(map);
result = regcomp(&preg, regexp, REG_EXTENDED);
result = p_regcomp(&preg, regexp, REG_EXTENDED);
if (result != 0) {
giterr_set_regex(&preg, result);
result = -1;
@ -816,7 +826,8 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
/* We're just copying data, don't care about the level */
GIT_UNUSED(level);
src_map = refcounted_strmap_take(src_header);
if ((src_map = refcounted_strmap_take(src_header)) == NULL)
return -1;
b->header.values = src_map;
return 0;
@ -1946,4 +1957,3 @@ done:
git_buf_free(&reader->buffer);
return result;
}

View File

@ -289,6 +289,7 @@ static int crlf_check(
ca.eol = check_eol(attr_values[1]); /* eol */
}
ca.auto_crlf = GIT_AUTO_CRLF_DEFAULT;
ca.safe_crlf = GIT_SAFE_CRLF_DEFAULT;
/*
* Use the core Git logic to see if we should perform CRLF for this file

View File

@ -13,6 +13,17 @@
#include "git2/transport.h"
#include "buffer.h"
#include "vector.h"
#include "proxy.h"
/* This is for backwards compatibility with curl<7.45.0. */
#ifndef CURLINFO_ACTIVESOCKET
# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
# define GIT_CURL_BADSOCKET -1
# define git_activesocket_t long
#else
# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
# define git_activesocket_t curl_socket_t
#endif
typedef struct {
git_stream parent;
@ -21,6 +32,8 @@ typedef struct {
char curl_error[CURL_ERROR_SIZE + 1];
git_cert_x509 cert_info;
git_strarray cert_info_strings;
git_proxy_options proxy;
git_cred *proxy_cred;
} curl_stream;
static int seterr_curl(curl_stream *s)
@ -29,21 +42,100 @@ static int seterr_curl(curl_stream *s)
return -1;
}
GIT_INLINE(int) error_no_credentials(void)
{
giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
return GIT_EAUTH;
}
static int apply_proxy_creds(curl_stream *s)
{
CURLcode res;
git_cred_userpass_plaintext *userpass;
if (!s->proxy_cred)
return GIT_ENOTFOUND;
userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
return seterr_curl(s);
if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
return seterr_curl(s);
return 0;
}
static int ask_and_apply_proxy_creds(curl_stream *s)
{
int error;
git_proxy_options *opts = &s->proxy;
if (!opts->credentials)
return error_no_credentials();
/* TODO: see if PROXYAUTH_AVAIL helps us here */
git_cred_free(s->proxy_cred);
s->proxy_cred = NULL;
giterr_clear();
error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
if (error == GIT_PASSTHROUGH)
return error_no_credentials();
if (error < 0) {
if (!giterr_last())
giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
return error;
}
if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
return -1;
}
return apply_proxy_creds(s);
}
static int curls_connect(git_stream *stream)
{
curl_stream *s = (curl_stream *) stream;
long sockextr;
int failed_cert = 0;
git_activesocket_t sockextr;
long connect_last = 0;
int failed_cert = 0, error;
bool retry_connect;
CURLcode res;
res = curl_easy_perform(s->handle);
/* Apply any credentials we've already established */
error = apply_proxy_creds(s);
if (error < 0 && error != GIT_ENOTFOUND)
return seterr_curl(s);
do {
retry_connect = 0;
res = curl_easy_perform(s->handle);
curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
/* HTTP 407 Proxy Authentication Required */
if (connect_last == 407) {
if ((error = ask_and_apply_proxy_creds(s)) < 0)
return error;
retry_connect = true;
}
} while (retry_connect);
if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
return seterr_curl(s);
if (res == CURLE_PEER_FAILED_VERIFICATION)
failed_cert = 1;
if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK)
if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
return seterr_curl(s);
}
if (sockextr == GIT_CURL_BADSOCKET) {
giterr_set(GITERR_NET, "curl socket is no longer valid");
return -1;
}
s->socket = sockextr;
@ -95,12 +187,19 @@ static int curls_certificate(git_cert **out, git_stream *stream)
return 0;
}
static int curls_set_proxy(git_stream *stream, const char *proxy_url)
static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
{
int error;
CURLcode res;
curl_stream *s = (curl_stream *) stream;
if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, proxy_url)) != CURLE_OK)
if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
return error;
if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
return seterr_curl(s);
if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
return seterr_curl(s);
return 0;
@ -115,6 +214,7 @@ static int wait_for(curl_socket_t fd, bool reading)
FD_ZERO(&outfd);
FD_ZERO(&errfd);
assert(fd >= 0);
FD_SET(fd, &errfd);
if (reading)
FD_SET(fd, &infd);

View File

@ -1,166 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "git2/odb.h"
#include "delta-apply.h"
/*
* This file was heavily cribbed from BinaryDelta.java in JGit, which
* itself was heavily cribbed from <code>patch-delta.c</code> in the
* GIT project. The original delta patching code was written by
* Nicolas Pitre <nico@cam.org>.
*/
static int hdr_sz(
size_t *size,
const unsigned char **delta,
const unsigned char *end)
{
const unsigned char *d = *delta;
size_t r = 0;
unsigned int c, shift = 0;
do {
if (d == end)
return -1;
c = *d++;
r |= (c & 0x7f) << shift;
shift += 7;
} while (c & 0x80);
*delta = d;
*size = r;
return 0;
}
int git__delta_read_header(
const unsigned char *delta,
size_t delta_len,
size_t *base_sz,
size_t *res_sz)
{
const unsigned char *delta_end = delta + delta_len;
if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
(hdr_sz(res_sz, &delta, delta_end) < 0))
return -1;
return 0;
}
#define DELTA_HEADER_BUFFER_LEN 16
int git__delta_read_header_fromstream(size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
{
static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
const unsigned char *delta, *delta_end;
size_t len;
ssize_t read;
len = read = 0;
while (len < buffer_len) {
read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
if (read == 0)
break;
if (read == GIT_EBUFS)
continue;
len += read;
}
delta = buffer;
delta_end = delta + len;
if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
(hdr_sz(res_sz, &delta, delta_end) < 0))
return -1;
return 0;
}
int git__delta_apply(
git_rawobj *out,
const unsigned char *base,
size_t base_len,
const unsigned char *delta,
size_t delta_len)
{
const unsigned char *delta_end = delta + delta_len;
size_t base_sz, res_sz, alloc_sz;
unsigned char *res_dp;
/* Check that the base size matches the data we were given;
* if not we would underflow while accessing data from the
* base object, resulting in data corruption or segfault.
*/
if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
return -1;
}
if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
return -1;
}
GITERR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1);
res_dp = git__malloc(alloc_sz);
GITERR_CHECK_ALLOC(res_dp);
res_dp[res_sz] = '\0';
out->data = res_dp;
out->len = res_sz;
while (delta < delta_end) {
unsigned char cmd = *delta++;
if (cmd & 0x80) {
/* cmd is a copy instruction; copy from the base.
*/
size_t off = 0, len = 0;
if (cmd & 0x01) off = *delta++;
if (cmd & 0x02) off |= *delta++ << 8UL;
if (cmd & 0x04) off |= *delta++ << 16UL;
if (cmd & 0x08) off |= *delta++ << 24UL;
if (cmd & 0x10) len = *delta++;
if (cmd & 0x20) len |= *delta++ << 8UL;
if (cmd & 0x40) len |= *delta++ << 16UL;
if (!len) len = 0x10000;
if (base_len < off + len || res_sz < len)
goto fail;
memcpy(res_dp, base + off, len);
res_dp += len;
res_sz -= len;
} else if (cmd) {
/* cmd is a literal insert instruction; copy from
* the delta stream itself.
*/
if (delta_end - delta < cmd || res_sz < cmd)
goto fail;
memcpy(res_dp, delta, cmd);
delta += cmd;
res_dp += cmd;
res_sz -= cmd;
} else {
/* cmd == 0 is reserved for future encodings.
*/
goto fail;
}
}
if (delta != delta_end || res_sz)
goto fail;
return 0;
fail:
git__free(out->data);
out->data = NULL;
giterr_set(GITERR_INVALID, "Failed to apply delta");
return -1;
}

View File

@ -1,62 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_delta_apply_h__
#define INCLUDE_delta_apply_h__
#include "odb.h"
#include "pack.h"
/**
* Apply a git binary delta to recover the original content.
*
* @param out the output buffer to receive the original data.
* Only out->data and out->len are populated, as this is
* the only information available in the delta.
* @param base the base to copy from during copy instructions.
* @param base_len number of bytes available at base.
* @param delta the delta to execute copy/insert instructions from.
* @param delta_len total number of bytes in the delta.
* @return
* - 0 on a successful delta unpack.
* - GIT_ERROR if the delta is corrupt or doesn't match the base.
*/
extern int git__delta_apply(
git_rawobj *out,
const unsigned char *base,
size_t base_len,
const unsigned char *delta,
size_t delta_len);
/**
* Read the header of a git binary delta.
*
* @param delta the delta to execute copy/insert instructions from.
* @param delta_len total number of bytes in the delta.
* @param base_sz pointer to store the base size field.
* @param res_sz pointer to store the result size field.
* @return
* - 0 on a successful decoding the header.
* - GIT_ERROR if the delta is corrupt.
*/
extern int git__delta_read_header(
const unsigned char *delta,
size_t delta_len,
size_t *base_sz,
size_t *res_sz);
/**
* Read the header of a git binary delta
*
* This variant reads just enough from the packfile stream to read the
* delta header.
*/
extern int git__delta_read_header_fromstream(
size_t *base_sz,
size_t *res_sz,
git_packfile_stream *stream);
#endif

View File

@ -114,7 +114,7 @@ struct index_entry {
struct git_delta_index {
unsigned long memsize;
const void *src_buf;
unsigned long src_size;
size_t src_size;
unsigned int hash_mask;
struct index_entry *hash[GIT_FLEX_ARRAY];
};
@ -142,8 +142,8 @@ static int lookup_index_alloc(
return 0;
}
struct git_delta_index *
git_delta_create_index(const void *buf, unsigned long bufsize)
int git_delta_index_init(
git_delta_index **out, const void *buf, size_t bufsize)
{
unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
const unsigned char *data, *buffer = buf;
@ -152,8 +152,10 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
void *mem;
unsigned long memsize;
*out = NULL;
if (!buf || !bufsize)
return NULL;
return 0;
/* Determine index hash size. Note that indexing skips the
first byte to allow for optimizing the rabin polynomial
@ -172,7 +174,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
hmask = hsize - 1;
if (lookup_index_alloc(&mem, &memsize, entries, hsize) < 0)
return NULL;
return -1;
index = mem;
mem = index->hash;
@ -190,7 +192,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
hash_count = git__calloc(hsize, sizeof(*hash_count));
if (!hash_count) {
git__free(index);
return NULL;
return -1;
}
/* then populate the index */
@ -243,20 +245,20 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
}
git__free(hash_count);
return index;
*out = index;
return 0;
}
void git_delta_free_index(struct git_delta_index *index)
void git_delta_index_free(git_delta_index *index)
{
git__free(index);
}
unsigned long git_delta_sizeof_index(struct git_delta_index *index)
size_t git_delta_index_size(git_delta_index *index)
{
if (index)
return index->memsize;
else
return 0;
assert(index);
return index->memsize;
}
/*
@ -265,55 +267,57 @@ unsigned long git_delta_sizeof_index(struct git_delta_index *index)
*/
#define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7)
void *
git_delta_create(
int git_delta_create_from_index(
void **out,
size_t *out_len,
const struct git_delta_index *index,
const void *trg_buf,
unsigned long trg_size,
unsigned long *delta_size,
unsigned long max_size)
size_t trg_size,
size_t max_size)
{
unsigned int i, outpos, outsize, moff, msize, val;
unsigned int i, bufpos, bufsize, moff, msize, val;
int inscnt;
const unsigned char *ref_data, *ref_top, *data, *top;
unsigned char *out;
unsigned char *buf;
*out = NULL;
*out_len = 0;
if (!trg_buf || !trg_size)
return NULL;
return 0;
outpos = 0;
outsize = 8192;
if (max_size && outsize >= max_size)
outsize = (unsigned int)(max_size + MAX_OP_SIZE + 1);
out = git__malloc(outsize);
if (!out)
return NULL;
bufpos = 0;
bufsize = 8192;
if (max_size && bufsize >= max_size)
bufsize = (unsigned int)(max_size + MAX_OP_SIZE + 1);
buf = git__malloc(bufsize);
GITERR_CHECK_ALLOC(buf);
/* store reference buffer size */
i = index->src_size;
while (i >= 0x80) {
out[outpos++] = i | 0x80;
buf[bufpos++] = i | 0x80;
i >>= 7;
}
out[outpos++] = i;
buf[bufpos++] = i;
/* store target buffer size */
i = trg_size;
while (i >= 0x80) {
out[outpos++] = i | 0x80;
buf[bufpos++] = i | 0x80;
i >>= 7;
}
out[outpos++] = i;
buf[bufpos++] = i;
ref_data = index->src_buf;
ref_top = ref_data + index->src_size;
data = trg_buf;
top = (const unsigned char *) trg_buf + trg_size;
outpos++;
bufpos++;
val = 0;
for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) {
out[outpos++] = *data;
buf[bufpos++] = *data;
val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
}
inscnt = i;
@ -350,11 +354,11 @@ git_delta_create(
if (msize < 4) {
if (!inscnt)
outpos++;
out[outpos++] = *data++;
bufpos++;
buf[bufpos++] = *data++;
inscnt++;
if (inscnt == 0x7f) {
out[outpos - inscnt - 1] = inscnt;
buf[bufpos - inscnt - 1] = inscnt;
inscnt = 0;
}
msize = 0;
@ -368,14 +372,14 @@ git_delta_create(
msize++;
moff--;
data--;
outpos--;
bufpos--;
if (--inscnt)
continue;
outpos--; /* remove count slot */
bufpos--; /* remove count slot */
inscnt--; /* make it -1 */
break;
}
out[outpos - inscnt - 1] = inscnt;
buf[bufpos - inscnt - 1] = inscnt;
inscnt = 0;
}
@ -383,22 +387,22 @@ git_delta_create(
left = (msize < 0x10000) ? 0 : (msize - 0x10000);
msize -= left;
op = out + outpos++;
op = buf + bufpos++;
i = 0x80;
if (moff & 0x000000ff)
out[outpos++] = moff >> 0, i |= 0x01;
buf[bufpos++] = moff >> 0, i |= 0x01;
if (moff & 0x0000ff00)
out[outpos++] = moff >> 8, i |= 0x02;
buf[bufpos++] = moff >> 8, i |= 0x02;
if (moff & 0x00ff0000)
out[outpos++] = moff >> 16, i |= 0x04;
buf[bufpos++] = moff >> 16, i |= 0x04;
if (moff & 0xff000000)
out[outpos++] = moff >> 24, i |= 0x08;
buf[bufpos++] = moff >> 24, i |= 0x08;
if (msize & 0x00ff)
out[outpos++] = msize >> 0, i |= 0x10;
buf[bufpos++] = msize >> 0, i |= 0x10;
if (msize & 0xff00)
out[outpos++] = msize >> 8, i |= 0x20;
buf[bufpos++] = msize >> 8, i |= 0x20;
*op = i;
@ -415,29 +419,201 @@ git_delta_create(
}
}
if (outpos >= outsize - MAX_OP_SIZE) {
void *tmp = out;
outsize = outsize * 3 / 2;
if (max_size && outsize >= max_size)
outsize = max_size + MAX_OP_SIZE + 1;
if (max_size && outpos > max_size)
if (bufpos >= bufsize - MAX_OP_SIZE) {
void *tmp = buf;
bufsize = bufsize * 3 / 2;
if (max_size && bufsize >= max_size)
bufsize = max_size + MAX_OP_SIZE + 1;
if (max_size && bufpos > max_size)
break;
out = git__realloc(out, outsize);
if (!out) {
buf = git__realloc(buf, bufsize);
if (!buf) {
git__free(tmp);
return NULL;
return -1;
}
}
}
if (inscnt)
out[outpos - inscnt - 1] = inscnt;
buf[bufpos - inscnt - 1] = inscnt;
if (max_size && outpos > max_size) {
git__free(out);
return NULL;
if (max_size && bufpos > max_size) {
giterr_set(GITERR_NOMEMORY, "delta would be larger than maximum size");
git__free(buf);
return GIT_EBUFS;
}
*delta_size = outpos;
return out;
*out_len = bufpos;
*out = buf;
return 0;
}
/*
* Delta application was heavily cribbed from BinaryDelta.java in JGit, which
* itself was heavily cribbed from <code>patch-delta.c</code> in the
* GIT project. The original delta patching code was written by
* Nicolas Pitre <nico@cam.org>.
*/
static int hdr_sz(
size_t *size,
const unsigned char **delta,
const unsigned char *end)
{
const unsigned char *d = *delta;
size_t r = 0;
unsigned int c, shift = 0;
do {
if (d == end) {
giterr_set(GITERR_INVALID, "truncated delta");
return -1;
}
c = *d++;
r |= (c & 0x7f) << shift;
shift += 7;
} while (c & 0x80);
*delta = d;
*size = r;
return 0;
}
int git_delta_read_header(
size_t *base_out,
size_t *result_out,
const unsigned char *delta,
size_t delta_len)
{
const unsigned char *delta_end = delta + delta_len;
if ((hdr_sz(base_out, &delta, delta_end) < 0) ||
(hdr_sz(result_out, &delta, delta_end) < 0))
return -1;
return 0;
}
#define DELTA_HEADER_BUFFER_LEN 16
int git_delta_read_header_fromstream(
size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
{
static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
const unsigned char *delta, *delta_end;
size_t len;
ssize_t read;
len = read = 0;
while (len < buffer_len) {
read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
if (read == 0)
break;
if (read == GIT_EBUFS)
continue;
len += read;
}
delta = buffer;
delta_end = delta + len;
if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
(hdr_sz(res_sz, &delta, delta_end) < 0))
return -1;
return 0;
}
int git_delta_apply(
void **out,
size_t *out_len,
const unsigned char *base,
size_t base_len,
const unsigned char *delta,
size_t delta_len)
{
const unsigned char *delta_end = delta + delta_len;
size_t base_sz, res_sz, alloc_sz;
unsigned char *res_dp;
*out = NULL;
*out_len = 0;
/* Check that the base size matches the data we were given;
* if not we would underflow while accessing data from the
* base object, resulting in data corruption or segfault.
*/
if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
return -1;
}
if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
return -1;
}
GITERR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1);
res_dp = git__malloc(alloc_sz);
GITERR_CHECK_ALLOC(res_dp);
res_dp[res_sz] = '\0';
*out = res_dp;
*out_len = res_sz;
while (delta < delta_end) {
unsigned char cmd = *delta++;
if (cmd & 0x80) {
/* cmd is a copy instruction; copy from the base.
*/
size_t off = 0, len = 0;
if (cmd & 0x01) off = *delta++;
if (cmd & 0x02) off |= *delta++ << 8UL;
if (cmd & 0x04) off |= *delta++ << 16UL;
if (cmd & 0x08) off |= *delta++ << 24UL;
if (cmd & 0x10) len = *delta++;
if (cmd & 0x20) len |= *delta++ << 8UL;
if (cmd & 0x40) len |= *delta++ << 16UL;
if (!len) len = 0x10000;
if (base_len < off + len || res_sz < len)
goto fail;
memcpy(res_dp, base + off, len);
res_dp += len;
res_sz -= len;
}
else if (cmd) {
/* cmd is a literal insert instruction; copy from
* the delta stream itself.
*/
if (delta_end - delta < cmd || res_sz < cmd)
goto fail;
memcpy(res_dp, delta, cmd);
delta += cmd;
res_dp += cmd;
res_sz -= cmd;
}
else {
/* cmd == 0 is reserved for future encodings.
*/
goto fail;
}
}
if (delta != delta_end || res_sz)
goto fail;
return 0;
fail:
git__free(*out);
*out = NULL;
*out_len = 0;
giterr_set(GITERR_INVALID, "Failed to apply delta");
return -1;
}

View File

@ -6,12 +6,12 @@
#define INCLUDE_git_delta_h__
#include "common.h"
#include "pack.h"
/* opaque object for delta index */
struct git_delta_index;
typedef struct git_delta_index git_delta_index;
/*
* create_delta_index: compute index data from given buffer
* git_delta_index_init: compute index data from given buffer
*
* This returns a pointer to a struct delta_index that should be passed to
* subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
@ -19,22 +19,18 @@ struct git_delta_index;
* before free_delta_index() is called. The returned pointer must be freed
* using free_delta_index().
*/
extern struct git_delta_index *
git_delta_create_index(const void *buf, unsigned long bufsize);
extern int git_delta_index_init(
git_delta_index **out, const void *buf, size_t bufsize);
/*
* free_delta_index: free the index created by create_delta_index()
*
* Given pointer must be what create_delta_index() returned, or NULL.
* Free the index created by git_delta_index_init()
*/
extern void git_delta_free_index(struct git_delta_index *index);
extern void git_delta_index_free(git_delta_index *index);
/*
* sizeof_delta_index: returns memory usage of delta index
*
* Given pointer must be what create_delta_index() returned, or NULL.
* Returns memory usage of delta index.
*/
extern unsigned long git_delta_sizeof_index(struct git_delta_index *index);
extern size_t git_delta_index_size(git_delta_index *index);
/*
* create_delta: create a delta from given index for the given buffer
@ -46,69 +42,94 @@ extern unsigned long git_delta_sizeof_index(struct git_delta_index *index);
* returned and *delta_size is updated with its size. The returned buffer
* must be freed by the caller.
*/
extern void *git_delta_create(
extern int git_delta_create_from_index(
void **out,
size_t *out_size,
const struct git_delta_index *index,
const void *buf,
unsigned long bufsize,
unsigned long *delta_size,
unsigned long max_delta_size);
size_t bufsize,
size_t max_delta_size);
/*
* diff_delta: create a delta from source buffer to target buffer
*
* If max_delta_size is non-zero and the resulting delta is to be larger
* than max_delta_size then NULL is returned. On success, a non-NULL
* than max_delta_size then GIT_EBUFS is returned. On success, a non-NULL
* pointer to the buffer with the delta data is returned and *delta_size is
* updated with its size. The returned buffer must be freed by the caller.
*/
GIT_INLINE(void *) git_delta(
const void *src_buf, unsigned long src_bufsize,
const void *trg_buf, unsigned long trg_bufsize,
unsigned long *delta_size,
unsigned long max_delta_size)
GIT_INLINE(int) git_delta(
void **out, size_t *out_len,
const void *src_buf, size_t src_bufsize,
const void *trg_buf, size_t trg_bufsize,
size_t max_delta_size)
{
struct git_delta_index *index = git_delta_create_index(src_buf, src_bufsize);
if (index) {
void *delta = git_delta_create(
index, trg_buf, trg_bufsize, delta_size, max_delta_size);
git_delta_free_index(index);
return delta;
}
return NULL;
}
git_delta_index *index;
int error = 0;
/*
* patch_delta: recreate target buffer given source buffer and delta data
*
* On success, a non-NULL pointer to the target buffer is returned and
* *trg_bufsize is updated with its size. On failure a NULL pointer is
* returned. The returned buffer must be freed by the caller.
*/
extern void *git_delta_patch(
const void *src_buf, unsigned long src_size,
const void *delta_buf, unsigned long delta_size,
unsigned long *dst_size);
*out = NULL;
*out_len = 0;
if ((error = git_delta_index_init(&index, src_buf, src_bufsize)) < 0)
return error;
if (index) {
error = git_delta_create_from_index(out, out_len,
index, trg_buf, trg_bufsize, max_delta_size);
git_delta_index_free(index);
}
return error;
}
/* the smallest possible delta size is 4 bytes */
#define GIT_DELTA_SIZE_MIN 4
/*
* This must be called twice on the delta data buffer, first to get the
* expected source buffer size, and again to get the target buffer size.
/**
* Apply a git binary delta to recover the original content.
* The caller is responsible for freeing the returned buffer.
*
* @param out the output buffer
* @param out_len the length of the output buffer
* @param base the base to copy from during copy instructions.
* @param base_len number of bytes available at base.
* @param delta the delta to execute copy/insert instructions from.
* @param delta_len total number of bytes in the delta.
* @return 0 on success or an error code
*/
extern int git_delta_apply(
void **out,
size_t *out_len,
const unsigned char *base,
size_t base_len,
const unsigned char *delta,
size_t delta_len);
/**
* Read the header of a git binary delta.
*
* @param base_out pointer to store the base size field.
* @param result_out pointer to store the result size field.
* @param delta the delta to execute copy/insert instructions from.
* @param delta_len total number of bytes in the delta.
* @return 0 on success or an error code
*/
extern int git_delta_read_header(
size_t *base_out,
size_t *result_out,
const unsigned char *delta,
size_t delta_len);
/**
* Read the header of a git binary delta
*
* This variant reads just enough from the packfile stream to read the
* delta header.
*/
GIT_INLINE(unsigned long) git_delta_get_hdr_size(
const unsigned char **datap, const unsigned char *top)
{
const unsigned char *data = *datap;
unsigned long cmd, size = 0;
int i = 0;
do {
cmd = *data++;
size |= (cmd & 0x7f) << i;
i += 7;
} while (cmd & 0x80 && data < top);
*datap = data;
return size;
}
extern int git_delta_read_header_fromstream(
size_t *base_out,
size_t *result_out,
git_packfile_stream *stream);
#endif

View File

@ -197,7 +197,7 @@ static int commit_name_dup(struct commit_name **out, struct commit_name *in)
name->tag = NULL;
name->path = NULL;
if (in->tag && git_object_dup((git_object **) &name->tag, (git_object *) in->tag) < 0)
if (in->tag && git_tag_dup(&name->tag, in->tag) < 0)
return -1;
name->path = git__strdup(in->path);

1586
src/diff.c

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@
#define INCLUDE_diff_h__
#include "git2/diff.h"
#include "git2/patch.h"
#include "git2/sys/diff.h"
#include "git2/oid.h"
@ -22,68 +23,32 @@
#define DIFF_OLD_PREFIX_DEFAULT "a/"
#define DIFF_NEW_PREFIX_DEFAULT "b/"
enum {
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
};
#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
enum {
GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */
GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */
GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */
GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18),
GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19),
GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20),
};
#define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF)
#define GIT_DIFF__VERBOSE (1 << 30)
typedef enum {
GIT_DIFF_TYPE_UNKNOWN = 0,
GIT_DIFF_TYPE_GENERATED = 1,
GIT_DIFF_TYPE_PARSED = 2,
} git_diff_origin_t;
struct git_diff {
git_refcount rc;
git_repository *repo;
git_diff_origin_t type;
git_diff_options opts;
git_vector pathspec;
git_vector deltas; /* vector of git_diff_delta */
git_pool pool;
git_iterator_type_t old_src;
git_iterator_type_t new_src;
uint32_t diffcaps;
git_diff_perfdata perf;
bool index_updated;
int (*strcomp)(const char *, const char *);
int (*strncomp)(const char *, const char *, size_t);
int (*pfxcomp)(const char *str, const char *pfx);
int (*entrycomp)(const void *a, const void *b);
int (*patch_fn)(git_patch **out, git_diff *diff, size_t idx);
void (*free_fn)(git_diff *diff);
};
extern void git_diff__cleanup_modes(
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
extern void git_diff_addref(git_diff *diff);
extern int git_diff_delta__cmp(const void *a, const void *b);
extern int git_diff_delta__casecmp(const void *a, const void *b);
extern const char *git_diff_delta__path(const git_diff_delta *delta);
extern bool git_diff_delta__should_skip(
const git_diff_options *opts, const git_diff_delta *delta);
extern int git_diff_delta__format_file_header(
git_buf *out,
const git_diff_delta *delta,
@ -91,84 +56,11 @@ extern int git_diff_delta__format_file_header(
const char *newpfx,
int oid_strlen);
extern int git_diff__oid_for_file(
git_oid *out, git_diff *, const char *, uint16_t, git_off_t);
extern int git_diff__oid_for_entry(
git_oid *out, git_diff *, const git_index_entry *, uint16_t, const git_oid *update);
extern int git_diff_delta__cmp(const void *a, const void *b);
extern int git_diff_delta__casecmp(const void *a, const void *b);
extern int git_diff__from_iterators(
git_diff **diff_ptr,
git_repository *repo,
git_iterator *old_iter,
git_iterator *new_iter,
const git_diff_options *opts);
extern int git_diff__paired_foreach(
git_diff *idx2head,
git_diff *wd2idx,
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
void *payload);
extern int git_diff_find_similar__hashsig_for_file(
void **out, const git_diff_file *f, const char *path, void *p);
extern int git_diff_find_similar__hashsig_for_buf(
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
extern int git_diff_find_similar__calc_similarity(
int *score, void *siga, void *sigb, void *payload);
extern int git_diff__commit(
git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
/* Merge two `git_diff`s according to the callback given by `cb`. */
typedef git_diff_delta *(*git_diff__merge_cb)(
const git_diff_delta *left,
const git_diff_delta *right,
git_pool *pool);
extern int git_diff__merge(
git_diff *onto, const git_diff *from, git_diff__merge_cb cb);
extern git_diff_delta *git_diff__merge_like_cgit(
const git_diff_delta *a,
const git_diff_delta *b,
git_pool *pool);
/* Duplicate a `git_diff_delta` out of the `git_pool` */
extern git_diff_delta *git_diff__delta_dup(
const git_diff_delta *d, git_pool *pool);
/*
* Sometimes a git_diff_file will have a zero size; this attempts to
* fill in the size without loading the blob if possible. If that is
* not possible, then it will return the git_odb_object that had to be
* loaded and the caller can use it or dispose of it as needed.
*/
GIT_INLINE(int) git_diff_file__resolve_zero_size(
git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
{
int error;
git_odb *odb;
size_t len;
git_otype type;
if ((error = git_repository_odb(&odb, repo)) < 0)
return error;
error = git_odb__read_header_or_object(
odb_obj, &len, &type, odb, &file->id);
git_odb_free(odb);
if (!error)
file->size = (git_off_t)len;
return error;
}
extern int git_diff__entry_cmp(const void *a, const void *b);
extern int git_diff__entry_icmp(const void *a, const void *b);
#endif

View File

@ -9,7 +9,6 @@
#include "git2/attr.h"
#include "diff.h"
#include "diff_patch.h"
#include "diff_driver.h"
#include "strmap.h"
#include "map.h"
@ -59,7 +58,7 @@ static git_diff_driver global_drivers[3] = {
{ DIFF_DRIVER_TEXT, GIT_DIFF_FORCE_TEXT, 0 },
};
git_diff_driver_registry *git_diff_driver_registry_new()
git_diff_driver_registry *git_diff_driver_registry_new(void)
{
git_diff_driver_registry *reg =
git__calloc(1, sizeof(git_diff_driver_registry));
@ -115,7 +114,7 @@ static int diff_driver_add_patterns(
if (error < 0)
break;
if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) != 0) {
if ((error = p_regcomp(&pat->re, buf.ptr, regex_flags)) != 0) {
/*
* TODO: issue a warning
*/
@ -211,7 +210,7 @@ static int git_diff_driver_builtin(
goto done;
if (ddef->words &&
(error = regcomp(
(error = p_regcomp(
&drv->word_pattern, ddef->words, ddef->flags | REG_EXTENDED)))
{
error = giterr_set_regex(&drv->word_pattern, error);
@ -315,7 +314,7 @@ static int git_diff_driver_load(
goto done;
if (!ce || !ce->value)
/* no diff.<driver>.wordregex, so just continue */;
else if (!(error = regcomp(&drv->word_pattern, ce->value, REG_EXTENDED)))
else if (!(error = p_regcomp(&drv->word_pattern, ce->value, REG_EXTENDED)))
found_driver = true;
else {
/* TODO: warn about bad regex instead of failure */
@ -520,4 +519,3 @@ void git_diff_find_context_clear(git_diff_find_context_payload *payload)
payload->driver = NULL;
}
}

View File

@ -8,6 +8,7 @@
#include "git2/blob.h"
#include "git2/submodule.h"
#include "diff.h"
#include "diff_generate.h"
#include "diff_file.h"
#include "odb.h"
#include "fileops.h"
@ -149,12 +150,14 @@ int git_diff_file_content__init_from_src(
if (src->blob) {
fc->file->size = git_blob_rawsize(src->blob);
git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
fc->file->id_abbrev = GIT_OID_HEXSZ;
fc->map.len = (size_t)fc->file->size;
fc->map.data = (char *)git_blob_rawcontent(src->blob);
} else {
fc->file->size = src->buflen;
git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);
fc->file->id_abbrev = GIT_OID_HEXSZ;
fc->map.len = src->buflen;
fc->map.data = (char *)src->buf;

1613
src/diff_generate.c Normal file

File diff suppressed because it is too large Load Diff

123
src/diff_generate.h Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_diff_generate_h__
#define INCLUDE_diff_generate_h__
enum {
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
};
#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
enum {
GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */
GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */
GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */
GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18),
GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19),
GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20),
};
#define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF)
#define GIT_DIFF__VERBOSE (1 << 30)
extern void git_diff_addref(git_diff *diff);
extern bool git_diff_delta__should_skip(
const git_diff_options *opts, const git_diff_delta *delta);
extern int git_diff__from_iterators(
git_diff **diff_ptr,
git_repository *repo,
git_iterator *old_iter,
git_iterator *new_iter,
const git_diff_options *opts);
extern int git_diff__commit(
git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
extern int git_diff__paired_foreach(
git_diff *idx2head,
git_diff *wd2idx,
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
void *payload);
/* Merge two `git_diff`s according to the callback given by `cb`. */
typedef git_diff_delta *(*git_diff__merge_cb)(
const git_diff_delta *left,
const git_diff_delta *right,
git_pool *pool);
extern int git_diff__merge(
git_diff *onto, const git_diff *from, git_diff__merge_cb cb);
extern git_diff_delta *git_diff__merge_like_cgit(
const git_diff_delta *a,
const git_diff_delta *b,
git_pool *pool);
/* Duplicate a `git_diff_delta` out of the `git_pool` */
extern git_diff_delta *git_diff__delta_dup(
const git_diff_delta *d, git_pool *pool);
extern int git_diff__oid_for_file(
git_oid *out,
git_diff *diff,
const char *path,
uint16_t mode,
git_off_t size);
extern int git_diff__oid_for_entry(
git_oid *out,
git_diff *diff,
const git_index_entry *src,
uint16_t mode,
const git_oid *update_match);
/*
* Sometimes a git_diff_file will have a zero size; this attempts to
* fill in the size without loading the blob if possible. If that is
* not possible, then it will return the git_odb_object that had to be
* loaded and the caller can use it or dispose of it as needed.
*/
GIT_INLINE(int) git_diff_file__resolve_zero_size(
git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
{
int error;
git_odb *odb;
size_t len;
git_otype type;
if ((error = git_repository_odb(&odb, repo)) < 0)
return error;
error = git_odb__read_header_or_object(
odb_obj, &len, &type, odb, &file->id);
git_odb_free(odb);
if (!error)
file->size = (git_off_t)len;
return error;
}
#endif

101
src/diff_parse.c Normal file
View File

@ -0,0 +1,101 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "diff.h"
#include "diff_parse.h"
#include "patch.h"
#include "patch_parse.h"
static void diff_parsed_free(git_diff *d)
{
git_diff_parsed *diff = (git_diff_parsed *)d;
git_patch *patch;
size_t i;
git_vector_foreach(&diff->patches, i, patch)
git_patch_free(patch);
git_vector_free(&diff->patches);
git_vector_free(&diff->base.deltas);
git_pool_clear(&diff->base.pool);
git__memzero(diff, sizeof(*diff));
git__free(diff);
}
static git_diff_parsed *diff_parsed_alloc(void)
{
git_diff_parsed *diff;
if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL)
return NULL;
GIT_REFCOUNT_INC(diff);
diff->base.type = GIT_DIFF_TYPE_PARSED;
diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
diff->base.strcomp = git__strcmp;
diff->base.strncomp = git__strncmp;
diff->base.pfxcomp = git__prefixcmp;
diff->base.entrycomp = git_diff__entry_cmp;
diff->base.patch_fn = git_patch_parsed_from_diff;
diff->base.free_fn = diff_parsed_free;
git_pool_init(&diff->base.pool, 1);
if (git_vector_init(&diff->patches, 0, NULL) < 0 ||
git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
git_diff_free(&diff->base);
return NULL;
}
git_vector_set_cmp(&diff->base.deltas, git_diff_delta__cmp);
return diff;
}
int git_diff_from_buffer(
git_diff **out,
const char *content,
size_t content_len)
{
git_diff_parsed *diff;
git_patch *patch;
git_patch_parse_ctx *ctx = NULL;
int error = 0;
*out = NULL;
diff = diff_parsed_alloc();
GITERR_CHECK_ALLOC(diff);
ctx = git_patch_parse_ctx_init(content, content_len, NULL);
GITERR_CHECK_ALLOC(ctx);
while (ctx->remain_len) {
if ((error = git_patch_parse(&patch, ctx)) < 0)
break;
git_vector_insert(&diff->patches, patch);
git_vector_insert(&diff->base.deltas, patch->delta);
}
if (error == GIT_ENOTFOUND && git_vector_length(&diff->patches) > 0) {
giterr_clear();
error = 0;
}
git_patch_parse_ctx_free(ctx);
if (error < 0)
git_diff_free(&diff->base);
else
*out = &diff->base;
return error;
}

18
src/diff_parse.h Normal file
View File

@ -0,0 +1,18 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_diff_parse_h__
#define INCLUDE_diff_parse_h__
#include "diff.h"
typedef struct {
struct git_diff base;
git_vector patches;
} git_diff_parsed;
#endif

View File

@ -1,83 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_diff_patch_h__
#define INCLUDE_diff_patch_h__
#include "common.h"
#include "diff.h"
#include "diff_file.h"
#include "array.h"
#include "git2/patch.h"
/* cached information about a hunk in a diff */
typedef struct diff_patch_hunk {
git_diff_hunk hunk;
size_t line_start;
size_t line_count;
} diff_patch_hunk;
enum {
GIT_DIFF_PATCH_ALLOCATED = (1 << 0),
GIT_DIFF_PATCH_INITIALIZED = (1 << 1),
GIT_DIFF_PATCH_LOADED = (1 << 2),
/* the two sides are different */
GIT_DIFF_PATCH_DIFFABLE = (1 << 3),
/* the difference between the two sides has been computed */
GIT_DIFF_PATCH_DIFFED = (1 << 4),
GIT_DIFF_PATCH_FLATTENED = (1 << 5),
};
struct git_patch {
git_refcount rc;
git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
git_diff_options diff_opts;
git_diff_delta *delta;
size_t delta_index;
git_diff_file_content ofile;
git_diff_file_content nfile;
uint32_t flags;
git_diff_binary binary;
git_array_t(diff_patch_hunk) hunks;
git_array_t(git_diff_line) lines;
size_t content_size, context_size, header_size;
git_pool flattened;
};
extern git_diff *git_patch__diff(git_patch *);
extern git_diff_driver *git_patch__driver(git_patch *);
extern void git_patch__old_data(char **, size_t *, git_patch *);
extern void git_patch__new_data(char **, size_t *, git_patch *);
extern int git_patch__invoke_callbacks(
git_patch *patch,
git_diff_file_cb file_cb,
git_diff_binary_cb binary_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb line_cb,
void *payload);
typedef struct git_diff_output git_diff_output;
struct git_diff_output {
/* these callbacks are issued with the diff data */
git_diff_file_cb file_cb;
git_diff_binary_cb binary_cb;
git_diff_hunk_cb hunk_cb;
git_diff_line_cb data_cb;
void *payload;
/* this records the actual error in cases where it may be obscured */
int error;
/* this callback is used to do the diff and drive the other callbacks.
* see diff_xdiff.h for how to use this in practice for now.
*/
int (*diff_cb)(git_diff_output *output, git_patch *patch);
};
#endif

View File

@ -6,7 +6,8 @@
*/
#include "common.h"
#include "diff.h"
#include "diff_patch.h"
#include "diff_file.h"
#include "patch_generate.h"
#include "fileops.h"
#include "zstream.h"
#include "blob.h"
@ -14,19 +15,19 @@
#include "git2/sys/diff.h"
typedef struct {
git_diff *diff;
git_diff_format_t format;
git_diff_line_cb print_cb;
void *payload;
git_buf *buf;
uint32_t flags;
int oid_strlen;
git_diff_line line;
unsigned int
content_loaded : 1,
content_allocated : 1;
git_diff_file_content *ofile;
git_diff_file_content *nfile;
const char *old_prefix;
const char *new_prefix;
uint32_t flags;
int id_strlen;
int (*strcomp)(const char *, const char *);
} diff_print_info;
static int diff_print_info_init__common(
@ -42,17 +43,15 @@ static int diff_print_info_init__common(
pi->payload = payload;
pi->buf = out;
if (!pi->oid_strlen) {
if (!pi->id_strlen) {
if (!repo)
pi->oid_strlen = GIT_ABBREV_DEFAULT;
else if (git_repository__cvar(&pi->oid_strlen, repo, GIT_CVAR_ABBREV) < 0)
pi->id_strlen = GIT_ABBREV_DEFAULT;
else if (git_repository__cvar(&pi->id_strlen, repo, GIT_CVAR_ABBREV) < 0)
return -1;
}
pi->oid_strlen += 1; /* for NUL byte */
if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
pi->oid_strlen = GIT_OID_HEXSZ + 1;
if (pi->id_strlen > GIT_OID_HEXSZ)
pi->id_strlen = GIT_OID_HEXSZ;
memset(&pi->line, 0, sizeof(pi->line));
pi->line.old_lineno = -1;
@ -74,11 +73,13 @@ static int diff_print_info_init_fromdiff(
memset(pi, 0, sizeof(diff_print_info));
pi->diff = diff;
if (diff) {
pi->flags = diff->opts.flags;
pi->oid_strlen = diff->opts.id_abbrev;
pi->id_strlen = diff->opts.id_abbrev;
pi->old_prefix = diff->opts.old_prefix;
pi->new_prefix = diff->opts.new_prefix;
pi->strcomp = diff->strcomp;
}
return diff_print_info_init__common(pi, out, repo, format, cb, payload);
@ -92,24 +93,16 @@ static int diff_print_info_init_frompatch(
git_diff_line_cb cb,
void *payload)
{
git_repository *repo;
assert(patch);
repo = patch->diff ? patch->diff->repo : NULL;
memset(pi, 0, sizeof(diff_print_info));
pi->diff = patch->diff;
pi->flags = patch->diff_opts.flags;
pi->oid_strlen = patch->diff_opts.id_abbrev;
pi->id_strlen = patch->diff_opts.id_abbrev;
pi->old_prefix = patch->diff_opts.old_prefix;
pi->new_prefix = patch->diff_opts.new_prefix;
pi->content_loaded = 1;
pi->ofile = &patch->ofile;
pi->nfile = &patch->nfile;
return diff_print_info_init__common(pi, out, repo, format, cb, payload);
return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload);
}
static char diff_pick_suffix(int mode)
@ -173,8 +166,8 @@ static int diff_print_one_name_status(
diff_print_info *pi = data;
git_buf *out = pi->buf;
char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
int (*strcomp)(const char *, const char *) =
pi->diff ? pi->diff->strcomp : git__strcmp;
int(*strcomp)(const char *, const char *) = pi->strcomp ?
pi->strcomp : git__strcmp;
GIT_UNUSED(progress);
@ -213,6 +206,7 @@ static int diff_print_one_raw(
{
diff_print_info *pi = data;
git_buf *out = pi->buf;
int id_abbrev;
char code = git_diff_status_char(delta->status);
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
@ -223,11 +217,21 @@ static int diff_print_one_raw(
git_buf_clear(out);
git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id);
git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id);
id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
delta->new_file.id_abbrev;
if (pi->id_strlen > id_abbrev) {
giterr_set(GITERR_PATCH,
"The patch input contains %d id characters (cannot print %d)",
id_abbrev, pi->id_strlen);
return -1;
}
git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id);
git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id);
git_buf_printf(
out, (pi->oid_strlen <= GIT_OID_HEXSZ) ?
out, (pi->id_strlen <= GIT_OID_HEXSZ) ?
":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
@ -252,53 +256,140 @@ static int diff_print_one_raw(
return pi->print_cb(delta, NULL, &pi->line, pi->payload);
}
static int diff_print_modes(
git_buf *out, const git_diff_delta *delta)
{
git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
return git_buf_oom(out) ? -1 : 0;
}
static int diff_print_oid_range(
git_buf *out, const git_diff_delta *delta, int oid_strlen)
git_buf *out, const git_diff_delta *delta, int id_strlen)
{
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id);
git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id);
if (delta->old_file.mode &&
id_strlen > delta->old_file.id_abbrev) {
giterr_set(GITERR_PATCH,
"The patch input contains %d id characters (cannot print %d)",
delta->old_file.id_abbrev, id_strlen);
return -1;
}
if ((delta->new_file.mode &&
id_strlen > delta->new_file.id_abbrev)) {
giterr_set(GITERR_PATCH,
"The patch input contains %d id characters (cannot print %d)",
delta->new_file.id_abbrev, id_strlen);
return -1;
}
git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id);
git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id);
/* TODO: Match git diff more closely */
if (delta->old_file.mode == delta->new_file.mode) {
git_buf_printf(out, "index %s..%s %o\n",
start_oid, end_oid, delta->old_file.mode);
} else {
if (delta->old_file.mode == 0) {
if (delta->old_file.mode == 0)
git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
} else if (delta->new_file.mode == 0) {
else if (delta->new_file.mode == 0)
git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
} else {
git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
}
else
diff_print_modes(out, delta);
git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
}
return git_buf_oom(out) ? -1 : 0;
}
static int diff_delta_format_path(
git_buf *out, const char *prefix, const char *filename)
{
if (git_buf_joinpath(out, prefix, filename) < 0)
return -1;
return git_buf_quote(out);
}
static int diff_delta_format_with_paths(
git_buf *out,
const git_diff_delta *delta,
const char *oldpfx,
const char *newpfx,
const char *template)
const char *template,
const char *oldpath,
const char *newpath)
{
const char *oldpath = delta->old_file.path;
const char *newpath = delta->new_file.path;
if (git_oid_iszero(&delta->old_file.id)) {
oldpfx = "";
if (git_oid_iszero(&delta->old_file.id))
oldpath = "/dev/null";
}
if (git_oid_iszero(&delta->new_file.id)) {
newpfx = "";
if (git_oid_iszero(&delta->new_file.id))
newpath = "/dev/null";
return git_buf_printf(out, template, oldpath, newpath);
}
int diff_delta_format_similarity_header(
git_buf *out,
const git_diff_delta *delta)
{
git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
const char *type;
int error = 0;
if (delta->similarity > 100) {
giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity);
error = -1;
goto done;
}
return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
if (delta->status == GIT_DELTA_RENAMED)
type = "rename";
else if (delta->status == GIT_DELTA_COPIED)
type = "copy";
else
abort();
if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 ||
(error = git_buf_puts(&new_path, delta->new_file.path)) < 0 ||
(error = git_buf_quote(&old_path)) < 0 ||
(error = git_buf_quote(&new_path)) < 0)
goto done;
git_buf_printf(out,
"similarity index %d%%\n"
"%s from %s\n"
"%s to %s\n",
delta->similarity,
type, old_path.ptr,
type, new_path.ptr);
if (git_buf_oom(out))
error = -1;
done:
git_buf_free(&old_path);
git_buf_free(&new_path);
return error;
}
static bool delta_is_unchanged(const git_diff_delta *delta)
{
if (git_oid_iszero(&delta->old_file.id) &&
git_oid_iszero(&delta->new_file.id))
return true;
if (delta->old_file.mode == GIT_FILEMODE_COMMIT ||
delta->new_file.mode == GIT_FILEMODE_COMMIT)
return false;
if (git_oid_equal(&delta->old_file.id, &delta->new_file.id))
return true;
return false;
}
int git_diff_delta__format_file_header(
@ -306,27 +397,56 @@ int git_diff_delta__format_file_header(
const git_diff_delta *delta,
const char *oldpfx,
const char *newpfx,
int oid_strlen)
int id_strlen)
{
git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
bool unchanged = delta_is_unchanged(delta);
int error = 0;
if (!oldpfx)
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
if (!newpfx)
newpfx = DIFF_NEW_PREFIX_DEFAULT;
if (!oid_strlen)
oid_strlen = GIT_ABBREV_DEFAULT + 1;
if (!id_strlen)
id_strlen = GIT_ABBREV_DEFAULT;
if ((error = diff_delta_format_path(
&old_path, oldpfx, delta->old_file.path)) < 0 ||
(error = diff_delta_format_path(
&new_path, newpfx, delta->new_file.path)) < 0)
goto done;
git_buf_clear(out);
git_buf_printf(out, "diff --git %s%s %s%s\n",
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
git_buf_printf(out, "diff --git %s %s\n",
old_path.ptr, new_path.ptr);
GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen));
if (delta->status == GIT_DELTA_RENAMED ||
(delta->status == GIT_DELTA_COPIED && unchanged)) {
if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
goto done;
}
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
diff_delta_format_with_paths(
out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
if (!unchanged) {
if ((error = diff_print_oid_range(out, delta, id_strlen)) < 0)
goto done;
return git_buf_oom(out) ? -1 : 0;
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
diff_delta_format_with_paths(out, delta,
"--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
}
if (unchanged && delta->old_file.mode != delta->new_file.mode)
diff_print_modes(out, delta);
if (git_buf_oom(out))
error = -1;
done:
git_buf_free(&old_path);
git_buf_free(&new_path);
return error;
}
static int format_binary(
@ -367,37 +487,29 @@ static int format_binary(
return 0;
}
static int diff_print_load_content(
diff_print_info *pi,
git_diff_delta *delta)
static int diff_print_patch_file_binary_noshow(
diff_print_info *pi, git_diff_delta *delta,
const char *old_pfx, const char *new_pfx)
{
git_diff_file_content *ofile, *nfile;
git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
int error;
assert(pi->diff);
if ((error = diff_delta_format_path(
&old_path, old_pfx, delta->old_file.path)) < 0 ||
(error = diff_delta_format_path(
&new_path, new_pfx, delta->new_file.path)) < 0)
goto done;
ofile = git__calloc(1, sizeof(git_diff_file_content));
nfile = git__calloc(1, sizeof(git_diff_file_content));
pi->line.num_lines = 1;
error = diff_delta_format_with_paths(
pi->buf, delta, "Binary files %s and %s differ\n",
old_path.ptr, new_path.ptr);
GITERR_CHECK_ALLOC(ofile);
GITERR_CHECK_ALLOC(nfile);
done:
git_buf_free(&old_path);
git_buf_free(&new_path);
if ((error = git_diff_file_content__init_from_diff(
ofile, pi->diff, delta, true)) < 0 ||
(error = git_diff_file_content__init_from_diff(
nfile, pi->diff, delta, true)) < 0) {
git__free(ofile);
git__free(nfile);
return error;
}
pi->content_loaded = 1;
pi->content_allocated = 1;
pi->ofile = ofile;
pi->nfile = nfile;
return 0;
return error;
}
static int diff_print_patch_file_binary(
@ -408,12 +520,12 @@ static int diff_print_patch_file_binary(
size_t pre_binary_size;
int error;
if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0)
goto noshow;
if (delta->status == GIT_DELTA_UNMODIFIED)
return 0;
if (!pi->content_loaded &&
(error = diff_print_load_content(pi, delta)) < 0)
return error;
if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data)
return diff_print_patch_file_binary_noshow(
pi, delta, old_pfx, new_pfx);
pre_binary_size = pi->buf->size;
git_buf_printf(pi->buf, "GIT binary patch\n");
@ -427,18 +539,14 @@ static int diff_print_patch_file_binary(
if (error == GIT_EBUFS) {
giterr_clear();
git_buf_truncate(pi->buf, pre_binary_size);
goto noshow;
return diff_print_patch_file_binary_noshow(
pi, delta, old_pfx, new_pfx);
}
}
pi->line.num_lines++;
return error;
noshow:
pi->line.num_lines = 1;
return diff_delta_format_with_paths(
pi->buf, delta, old_pfx, new_pfx,
"Binary files %s%s and %s%s differ\n");
}
static int diff_print_patch_file(
@ -447,15 +555,18 @@ static int diff_print_patch_file(
int error;
diff_print_info *pi = data;
const char *oldpfx =
pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
const char *newpfx =
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
(pi->flags & GIT_DIFF_FORCE_BINARY);
bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
int oid_strlen = binary && show_binary ?
GIT_OID_HEXSZ + 1 : pi->oid_strlen;
int id_strlen = pi->id_strlen;
if (binary && show_binary)
id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev :
delta->new_file.id_abbrev;
GIT_UNUSED(progress);
@ -468,7 +579,7 @@ static int diff_print_patch_file(
return 0;
if ((error = git_diff_delta__format_file_header(
pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0)
pi->buf, delta, oldpfx, newpfx, id_strlen)) < 0)
return error;
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
@ -485,9 +596,9 @@ static int diff_print_patch_binary(
{
diff_print_info *pi = data;
const char *old_pfx =
pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
const char *new_pfx =
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
int error;
git_buf_clear(pi->buf);
@ -582,43 +693,11 @@ int git_diff_print(
giterr_set_after_callback_function(error, "git_diff_print");
}
git__free(pi.nfile);
git__free(pi.ofile);
git_buf_free(&buf);
return error;
}
/* print a git_patch to an output callback */
int git_patch_print(
git_patch *patch,
git_diff_line_cb print_cb,
void *payload)
{
int error;
git_buf temp = GIT_BUF_INIT;
diff_print_info pi;
assert(patch && print_cb);
if (!(error = diff_print_info_init_frompatch(
&pi, &temp, patch,
GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
{
error = git_patch__invoke_callbacks(
patch, diff_print_patch_file, diff_print_patch_binary,
diff_print_patch_hunk, diff_print_patch_line, &pi);
if (error) /* make sure error message is set */
giterr_set_after_callback_function(error, "git_patch_print");
}
git_buf_free(&temp);
return error;
}
int git_diff_print_callback__to_buf(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
@ -659,6 +738,46 @@ int git_diff_print_callback__to_file_handle(
return 0;
}
/* print a git_diff to a git_buf */
int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
{
assert(out && diff);
git_buf_sanitize(out);
return git_diff_print(
diff, format, git_diff_print_callback__to_buf, out);
}
/* print a git_patch to an output callback */
int git_patch_print(
git_patch *patch,
git_diff_line_cb print_cb,
void *payload)
{
int error;
git_buf temp = GIT_BUF_INIT;
diff_print_info pi;
assert(patch && print_cb);
if (!(error = diff_print_info_init_frompatch(
&pi, &temp, patch,
GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
{
error = git_patch__invoke_callbacks(
patch,
diff_print_patch_file, diff_print_patch_binary,
diff_print_patch_hunk, diff_print_patch_line,
&pi);
if (error) /* make sure error message is set */
giterr_set_after_callback_function(error, "git_patch_print");
}
git_buf_free(&temp);
return error;
}
/* print a git_patch to a git_buf */
int git_patch_to_buf(git_buf *out, git_patch *patch)
{

View File

@ -7,7 +7,7 @@
#include "common.h"
#include "vector.h"
#include "diff.h"
#include "diff_patch.h"
#include "patch_generate.h"
#define DIFF_RENAME_FILE_SEPARATOR " => "
#define STATS_FULL_MIN_SCALE 7
@ -190,8 +190,9 @@ int git_diff_get_stats(
break;
/* keep a count of renames because it will affect formatting */
delta = git_patch_get_delta(patch);
delta = patch->delta;
/* TODO ugh */
namelen = strlen(delta->new_file.path);
if (strcmp(delta->old_file.path, delta->new_file.path) != 0) {
namelen += strlen(delta->old_file.path);
@ -299,15 +300,24 @@ int git_diff_stats_to_buf(
}
if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) {
error = git_buf_printf(
out, " %" PRIuZ " file%s changed, %" PRIuZ
" insertion%s(+), %" PRIuZ " deletion%s(-)\n",
stats->files_changed, stats->files_changed != 1 ? "s" : "",
stats->insertions, stats->insertions != 1 ? "s" : "",
stats->deletions, stats->deletions != 1 ? "s" : "");
git_buf_printf(
out, " %" PRIuZ " file%s changed",
stats->files_changed, stats->files_changed != 1 ? "s" : "");
if (error < 0)
return error;
if (stats->insertions || stats->deletions == 0)
git_buf_printf(
out, ", %" PRIuZ " insertion%s(+)",
stats->insertions, stats->insertions != 1 ? "s" : "");
if (stats->deletions || stats->insertions == 0)
git_buf_printf(
out, ", %" PRIuZ " deletion%s(-)",
stats->deletions, stats->deletions != 1 ? "s" : "");
git_buf_putc(out, '\n');
if (git_buf_oom(out))
return -1;
}
if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) {
@ -333,4 +343,3 @@ void git_diff_stats_free(git_diff_stats *stats)
git__free(stats->filestats);
git__free(stats);
}

View File

@ -11,6 +11,7 @@
#include "git2/sys/hashsig.h"
#include "diff.h"
#include "diff_generate.h"
#include "path.h"
#include "fileops.h"
#include "config.h"

22
src/diff_tform.h Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_diff_tform_h__
#define INCLUDE_diff_tform_h__
extern int git_diff_find_similar__hashsig_for_file(
void **out, const git_diff_file *f, const char *path, void *p);
extern int git_diff_find_similar__hashsig_for_buf(
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
extern int git_diff_find_similar__calc_similarity(
int *score, void *siga, void *sigb, void *payload);
#endif

View File

@ -8,8 +8,8 @@
#include "common.h"
#include "diff.h"
#include "diff_driver.h"
#include "diff_patch.h"
#include "diff_xdiff.h"
#include "patch_generate.h"
static int git_xdiff_scan_int(const char **str, int *value)
{
@ -56,7 +56,7 @@ fail:
typedef struct {
git_xdiff_output *xo;
git_patch *patch;
git_patch_generated *patch;
git_diff_hunk hunk;
int old_lineno, new_lineno;
mmfile_t xd_old_data, xd_new_data;
@ -110,9 +110,9 @@ static int diff_update_lines(
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
{
git_xdiff_info *info = priv;
git_patch *patch = info->patch;
const git_diff_delta *delta = git_patch_get_delta(patch);
git_diff_output *output = &info->xo->output;
git_patch_generated *patch = info->patch;
const git_diff_delta *delta = patch->base.delta;
git_patch_generated_output *output = &info->xo->output;
git_diff_line line;
if (len == 1) {
@ -181,7 +181,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
return output->error;
}
static int git_xdiff(git_diff_output *output, git_patch *patch)
static int git_xdiff(git_patch_generated_output *output, git_patch_generated *patch)
{
git_xdiff_output *xo = (git_xdiff_output *)output;
git_xdiff_info info;
@ -194,7 +194,7 @@ static int git_xdiff(git_diff_output *output, git_patch *patch)
xo->callback.priv = &info;
git_diff_find_context_init(
&xo->config.find_func, &findctxt, git_patch__driver(patch));
&xo->config.find_func, &findctxt, git_patch_generated_driver(patch));
xo->config.find_func_priv = &findctxt;
if (xo->config.find_func != NULL)
@ -206,8 +206,8 @@ static int git_xdiff(git_diff_output *output, git_patch *patch)
* updates are needed to xo->params.flags
*/
git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
git_patch__new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
git_patch_generated_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE ||
info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) {

View File

@ -8,20 +8,20 @@
#define INCLUDE_diff_xdiff_h__
#include "diff.h"
#include "diff_patch.h"
#include "xdiff/xdiff.h"
#include "patch_generate.h"
/* xdiff cannot cope with large files. these files should not be passed to
* xdiff. callers should treat these large files as binary.
*/
#define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023)
/* A git_xdiff_output is a git_diff_output with extra fields necessary
* to use libxdiff. Calling git_xdiff_init() will set the diff_cb field
* of the output to use xdiff to generate the diffs.
/* A git_xdiff_output is a git_patch_generate_output with extra fields
* necessary to use libxdiff. Calling git_xdiff_init() will set the diff_cb
* field of the output to use xdiff to generate the diffs.
*/
typedef struct {
git_diff_output output;
git_patch_generated_output output;
xdemitconf_t config;
xpparam_t params;

View File

@ -149,7 +149,7 @@ static int fetchhead_ref_parse(
if (!*line) {
giterr_set(GITERR_FETCHHEAD,
"Empty line in FETCH_HEAD line %d", line_num);
"Empty line in FETCH_HEAD line %"PRIuZ, line_num);
return -1;
}
@ -163,7 +163,7 @@ static int fetchhead_ref_parse(
if (strlen(oid_str) != GIT_OID_HEXSZ) {
giterr_set(GITERR_FETCHHEAD,
"Invalid object ID in FETCH_HEAD line %d", line_num);
"Invalid object ID in FETCH_HEAD line %"PRIuZ, line_num);
return -1;
}
@ -171,7 +171,7 @@ static int fetchhead_ref_parse(
const git_error *oid_err = giterr_last();
const char *err_msg = oid_err ? oid_err->message : "Invalid object ID";
giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %d",
giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ,
err_msg, line_num);
return -1;
}
@ -180,7 +180,7 @@ static int fetchhead_ref_parse(
if (*line) {
if ((is_merge_str = git__strsep(&line, "\t")) == NULL) {
giterr_set(GITERR_FETCHHEAD,
"Invalid description data in FETCH_HEAD line %d", line_num);
"Invalid description data in FETCH_HEAD line %"PRIuZ, line_num);
return -1;
}
@ -190,13 +190,13 @@ static int fetchhead_ref_parse(
*is_merge = 0;
else {
giterr_set(GITERR_FETCHHEAD,
"Invalid for-merge entry in FETCH_HEAD line %d", line_num);
"Invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num);
return -1;
}
if ((desc = line) == NULL) {
giterr_set(GITERR_FETCHHEAD,
"Invalid description in FETCH_HEAD line %d", line_num);
"Invalid description in FETCH_HEAD line %"PRIuZ, line_num);
return -1;
}
@ -213,7 +213,7 @@ static int fetchhead_ref_parse(
if ((desc = strstr(name, "' ")) == NULL ||
git__prefixcmp(desc, "' of ") != 0) {
giterr_set(GITERR_FETCHHEAD,
"Invalid description in FETCH_HEAD line %d", line_num);
"Invalid description in FETCH_HEAD line %"PRIuZ, line_num);
return -1;
}
@ -277,7 +277,7 @@ int git_repository_fetchhead_foreach(git_repository *repo,
}
if (*buffer) {
giterr_set(GITERR_FETCHHEAD, "No EOL at line %d", line_num+1);
giterr_set(GITERR_FETCHHEAD, "No EOL at line %"PRIuZ, line_num+1);
error = -1;
goto done;
}

View File

@ -272,6 +272,11 @@ cleanup:
}
int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
{
return git_filebuf_open_withsize(file, path, flags, mode, WRITE_BUFFER_SIZE);
}
int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size)
{
int compression, error = -1;
size_t path_len, alloc_len;
@ -286,7 +291,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
file->do_not_buffer = true;
file->buf_size = WRITE_BUFFER_SIZE;
file->buf_size = size;
file->buf_pos = 0;
file->fd = -1;
file->last_error = BUFERR_OK;

View File

@ -79,6 +79,7 @@ int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
int git_filebuf_open(git_filebuf *lock, const char *path, int flags, mode_t mode);
int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size);
int git_filebuf_commit(git_filebuf *lock);
int git_filebuf_commit_at(git_filebuf *lock, const char *path);
void git_filebuf_cleanup(git_filebuf *lock);

View File

@ -72,8 +72,16 @@ int git_futils_creat_locked(const char *path, const mode_t mode)
O_EXCL | O_BINARY | O_CLOEXEC, mode);
if (fd < 0) {
int error = errno;
giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
return errno == EEXIST ? GIT_ELOCKED : -1;
switch (error) {
case EEXIST:
return GIT_ELOCKED;
case ENOENT:
return GIT_ENOTFOUND;
default:
return -1;
}
}
return fd;
@ -837,6 +845,19 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode)
return cp_by_fd(ifd, ofd, true);
}
int git_futils_touch(const char *path, time_t *when)
{
struct p_timeval times[2];
int ret;
times[0].tv_sec = times[1].tv_sec = when ? *when : time(NULL);
times[0].tv_usec = times[1].tv_usec = 0;
ret = p_utimes(path, times);
return (ret < 0) ? git_path_set_error(errno, path, "touch") : 0;
}
static int cp_link(const char *from, const char *to, size_t link_size)
{
int error = 0;

View File

@ -45,12 +45,12 @@ extern int git_futils_writebuffer(
extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode);
/**
* Create an open a process-locked file
* Create and open a process-locked file
*/
extern int git_futils_creat_locked(const char *path, const mode_t mode);
/**
* Create an open a process-locked file, while
* Create and open a process-locked file, while
* also creating all the folders in its path
*/
extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode);
@ -184,6 +184,12 @@ extern int git_futils_cp(
const char *to,
mode_t filemode);
/**
* Set the files atime and mtime to the given time, or the current time
* if `ts` is NULL.
*/
extern int git_futils_touch(const char *path, time_t *when);
/**
* Flags that can be passed to `git_futils_cp_r`.
*

View File

@ -93,11 +93,24 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs)
* It will be restored if/when we recurse below.
*/
if (c == '*') {
flags &= ~FNM_PATHNAME;
while (c == '*')
c = *++pattern;
if (c == '/')
c = *++pattern;
c = *++pattern;
/* star-star-slash is at the end, match by default */
if (c == EOS)
return 0;
/* Double-star must be at end or between slashes */
if (c != '/')
return (FNM_NOMATCH);
c = *++pattern;
do {
int e = p_fnmatchx(pattern, string, recurs_flags, recurs);
if (e != FNM_NOMATCH)
return e;
string = strchr(string, '/');
} while (string++);
/* If we get here, we didn't find a match */
return FNM_NOMATCH;
}
if (*string == '.' && (flags & FNM_PERIOD) &&

View File

@ -9,6 +9,7 @@
#include "hash.h"
#include "sysdir.h"
#include "filter.h"
#include "merge_driver.h"
#include "openssl_stream.h"
#include "thread-utils.h"
#include "git2/global.h"
@ -59,6 +60,7 @@ static int init_common(void)
if ((ret = git_hash_global_init()) == 0 &&
(ret = git_sysdir_global_init()) == 0 &&
(ret = git_filter_global_init()) == 0 &&
(ret = git_merge_driver_global_init()) == 0 &&
(ret = git_transport_ssh_global_init()) == 0 &&
(ret = git_openssl_stream_global_init()) == 0)
ret = git_mwindow_global_init();
@ -245,6 +247,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
static pthread_key_t _tls_key;
static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
int init_error = 0;
@ -266,12 +269,19 @@ static void init_once(void)
int git_libgit2_init(void)
{
int ret;
int ret, err;
ret = git_atomic_inc(&git__n_inits);
pthread_once(&_once_init, init_once);
return init_error ? init_error : ret;
if ((err = pthread_mutex_lock(&_init_mutex)) != 0)
return err;
err = pthread_once(&_once_init, init_once);
err |= pthread_mutex_unlock(&_init_mutex);
if (err || init_error)
return err | init_error;
return ret;
}
int git_libgit2_shutdown(void)
@ -283,6 +293,9 @@ int git_libgit2_shutdown(void)
if ((ret = git_atomic_dec(&git__n_inits)) != 0)
return ret;
if ((ret = pthread_mutex_lock(&_init_mutex)) != 0)
return ret;
/* Shut down any subsystems that have global state */
shutdown_common();
@ -296,6 +309,9 @@ int git_libgit2_shutdown(void)
git_mutex_free(&git__mwindow_mutex);
_once_init = new_once;
if ((ret = pthread_mutex_unlock(&_init_mutex)) != 0)
return ret;
return 0;
}
@ -325,7 +341,7 @@ int git_libgit2_init(void)
{
int ret;
/* Only init SSL the first time */
/* Only init subsystems the first time */
if ((ret = git_atomic_inc(&git__n_inits)) != 1)
return ret;
@ -343,6 +359,7 @@ int git_libgit2_shutdown(void)
if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
shutdown_common();
git__global_state_cleanup(&__state);
memset(&__state, 0, sizeof(__state));
}
return ret;

View File

@ -16,6 +16,12 @@ typedef struct {
git_error error_t;
git_buf error_buf;
char oid_fmt[GIT_OID_HEXSZ+1];
/* On Windows, this is the current child thread that was started by
* `git_thread_create`. This is used to set the thread's exit code
* when terminated by `git_thread_exit`. It is unused on POSIX.
*/
git_thread *current_thread;
} git_global_st;
#ifdef GIT_OPENSSL

View File

@ -59,7 +59,7 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
/* as long as there are non-STALE commits */
while (interesting(&list, roots)) {
git_commit_list_node *commit = git_pqueue_pop(&list);
int flags;
unsigned int flags;
if (commit == NULL)
break;

View File

@ -19,6 +19,7 @@
#include "blob.h"
#include "idxmap.h"
#include "diff.h"
#include "varint.h"
#include "git2/odb.h"
#include "git2/oid.h"
@ -65,8 +66,11 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr
static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
static const size_t INDEX_HEADER_SIZE = 12;
static const unsigned int INDEX_VERSION_NUMBER = 2;
static const unsigned int INDEX_VERSION_NUMBER_DEFAULT = 2;
static const unsigned int INDEX_VERSION_NUMBER_LB = 2;
static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
static const unsigned int INDEX_VERSION_NUMBER_COMP = 4;
static const unsigned int INDEX_VERSION_NUMBER_UB = 4;
static const unsigned int INDEX_HEADER_SIG = 0x44495243;
static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
@ -434,6 +438,7 @@ int git_index_open(git_index **index_out, const char *index_path)
index->entries_search = git_index_entry_srch;
index->entries_search_path = index_entry_srch_path;
index->reuc_search = reuc_srch;
index->version = INDEX_VERSION_NUMBER_DEFAULT;
if (index_path != NULL && (error = git_index_read(index, true)) < 0)
goto fail;
@ -547,7 +552,7 @@ int git_index_clear(git_index *index)
static int create_index_error(int error, const char *msg)
{
giterr_set(GITERR_INDEX, msg);
giterr_set_str(GITERR_INDEX, msg);
return error;
}
@ -747,6 +752,28 @@ done:
return 0;
}
unsigned git_index_version(git_index *index)
{
assert(index);
return index->version;
}
int git_index_set_version(git_index *index, unsigned int version)
{
assert(index);
if (version < INDEX_VERSION_NUMBER_LB ||
version > INDEX_VERSION_NUMBER_UB) {
giterr_set(GITERR_INDEX, "Invalid version number");
return -1;
}
index->version = version;
return 0;
}
int git_index_write(git_index *index)
{
git_indexwriter writer = GIT_INDEXWRITER_INIT;
@ -2160,12 +2187,12 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
if (git__strtol64(&tmp, buffer, &endptr, 8) < 0 ||
!endptr || endptr == buffer || *endptr ||
tmp < 0) {
tmp < 0 || tmp > UINT32_MAX) {
index_entry_reuc_free(lost);
return index_error_invalid("reading reuc entry stage");
}
lost->mode[i] = tmp;
lost->mode[i] = (uint32_t)tmp;
len = (endptr + 1) - buffer;
if (size <= len) {
@ -2262,12 +2289,15 @@ static size_t read_entry(
git_index_entry **out,
git_index *index,
const void *buffer,
size_t buffer_size)
size_t buffer_size,
const char **last)
{
size_t path_length, entry_size;
const char *path_ptr;
struct entry_short source;
git_index_entry entry = {{0}};
bool compressed = index->version >= INDEX_VERSION_NUMBER_COMP;
char *tmp_path = NULL;
if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
return 0;
@ -2302,33 +2332,56 @@ static size_t read_entry(
} else
path_ptr = (const char *) buffer + offsetof(struct entry_short, path);
path_length = entry.flags & GIT_IDXENTRY_NAMEMASK;
if (!compressed) {
path_length = entry.flags & GIT_IDXENTRY_NAMEMASK;
/* if this is a very long string, we must find its
* real length without overflowing */
if (path_length == 0xFFF) {
const char *path_end;
/* if this is a very long string, we must find its
* real length without overflowing */
if (path_length == 0xFFF) {
const char *path_end;
path_end = memchr(path_ptr, '\0', buffer_size);
if (path_end == NULL)
path_end = memchr(path_ptr, '\0', buffer_size);
if (path_end == NULL)
return 0;
path_length = path_end - path_ptr;
}
if (entry.flags & GIT_IDXENTRY_EXTENDED)
entry_size = long_entry_size(path_length);
else
entry_size = short_entry_size(path_length);
if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
return 0;
path_length = path_end - path_ptr;
entry.path = (char *)path_ptr;
} else {
size_t varint_len;
size_t shared = git_decode_varint((const unsigned char *)path_ptr,
&varint_len);
size_t len = strlen(path_ptr + varint_len);
size_t last_len = strlen(*last);
size_t tmp_path_len;
if (varint_len == 0)
return index_error_invalid("incorrect prefix length");
GITERR_CHECK_ALLOC_ADD(&tmp_path_len, shared, len + 1);
tmp_path = git__malloc(tmp_path_len);
GITERR_CHECK_ALLOC(tmp_path);
memcpy(tmp_path, last, last_len);
memcpy(tmp_path + last_len, path_ptr + varint_len, len);
entry_size = long_entry_size(shared + len);
entry.path = tmp_path;
}
if (entry.flags & GIT_IDXENTRY_EXTENDED)
entry_size = long_entry_size(path_length);
else
entry_size = short_entry_size(path_length);
if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
return 0;
entry.path = (char *)path_ptr;
if (index_entry_dup(out, index, &entry) < 0)
if (index_entry_dup(out, index, &entry) < 0) {
git__free(tmp_path);
return 0;
}
git__free(tmp_path);
return entry_size;
}
@ -2341,8 +2394,8 @@ static int read_header(struct index_header *dest, const void *buffer)
return index_error_invalid("incorrect header signature");
dest->version = ntohl(source->version);
if (dest->version != INDEX_VERSION_NUMBER_EXT &&
dest->version != INDEX_VERSION_NUMBER)
if (dest->version < INDEX_VERSION_NUMBER_LB ||
dest->version > INDEX_VERSION_NUMBER_UB)
return index_error_invalid("incorrect header version");
dest->entry_count = ntohl(source->entry_count);
@ -2395,6 +2448,8 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
unsigned int i;
struct index_header header = { 0 };
git_oid checksum_calculated, checksum_expected;
const char **last = NULL;
const char *empty = "";
#define seek_forward(_increase) { \
if (_increase >= buffer_size) { \
@ -2415,6 +2470,10 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
if ((error = read_header(&header, buffer)) < 0)
return error;
index->version = header.version;
if (index->version >= INDEX_VERSION_NUMBER_COMP)
last = &empty;
seek_forward(INDEX_HEADER_SIZE);
assert(!index->entries.length);
@ -2427,7 +2486,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
/* Parse all the entries */
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
git_index_entry *entry;
size_t entry_size = read_entry(&entry, index, buffer, buffer_size);
size_t entry_size = read_entry(&entry, index, buffer, buffer_size, last);
/* 0 bytes read means an object corruption */
if (entry_size == 0) {
@ -2518,15 +2577,31 @@ static bool is_index_extended(git_index *index)
return (extended > 0);
}
static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char **last)
{
void *mem = NULL;
struct entry_short *ondisk;
size_t path_len, disk_size;
char *path;
const char *path_start = entry->path;
size_t same_len = 0;
path_len = ((struct entry_internal *)entry)->pathlen;
if (last) {
const char *last_c = *last;
while (*path_start == *last_c) {
if (!*path_start || !*last_c)
break;
++path_start;
++last_c;
++same_len;
}
path_len -= same_len;
*last = entry->path;
}
if (entry->flags & GIT_IDXENTRY_EXTENDED)
disk_size = long_entry_size(path_len);
else
@ -2574,7 +2649,12 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
else
path = ondisk->path;
memcpy(path, entry->path, path_len);
if (last) {
path += git_encode_varint((unsigned char *) path,
disk_size,
path_len - same_len);
}
memcpy(path, path_start, path_len);
return 0;
}
@ -2585,6 +2665,8 @@ static int write_entries(git_index *index, git_filebuf *file)
size_t i;
git_vector case_sorted, *entries;
git_index_entry *entry;
const char **last = NULL;
const char *empty = "";
/* If index->entries is sorted case-insensitively, then we need
* to re-sort it case-sensitively before writing */
@ -2596,8 +2678,11 @@ static int write_entries(git_index *index, git_filebuf *file)
entries = &index->entries;
}
if (index->version >= INDEX_VERSION_NUMBER_COMP)
last = &empty;
git_vector_foreach(entries, i, entry)
if ((error = write_disk_entry(file, entry)) < 0)
if ((error = write_disk_entry(file, entry, last)) < 0)
break;
if (index->ignore_case)
@ -2762,8 +2847,12 @@ static int write_index(git_oid *checksum, git_index *index, git_filebuf *file)
assert(index && file);
is_extended = is_index_extended(index);
index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER;
if (index->version <= INDEX_VERSION_NUMBER_EXT) {
is_extended = is_index_extended(index);
index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER_LB;
} else {
index_version_number = index->version;
}
header.signature = htonl(INDEX_HEADER_SIG);
header.version = htonl(index_version_number);
@ -2925,38 +3014,39 @@ cleanup:
return error;
}
int git_index_read_index(
static int git_index_read_iterator(
git_index *index,
const git_index *new_index)
git_iterator *new_iterator,
size_t new_length_hint)
{
git_vector new_entries = GIT_VECTOR_INIT,
remove_entries = GIT_VECTOR_INIT;
git_idxmap *new_entries_map = NULL;
git_iterator *index_iterator = NULL;
git_iterator *new_iterator = NULL;
git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
const git_index_entry *old_entry, *new_entry;
git_index_entry *entry;
size_t i;
int error;
if ((error = git_vector_init(&new_entries, new_index->entries.length, index->entries._cmp)) < 0 ||
assert((new_iterator->flags & GIT_ITERATOR_DONT_IGNORE_CASE));
if ((error = git_vector_init(&new_entries, new_length_hint, index->entries._cmp)) < 0 ||
(error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 ||
(error = git_idxmap_alloc(&new_entries_map)) < 0)
goto done;
if (index->ignore_case)
kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_index->entries.length);
else
kh_resize(idx, new_entries_map, new_index->entries.length);
if (index->ignore_case && new_length_hint)
kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_length_hint);
else if (new_length_hint)
kh_resize(idx, new_entries_map, new_length_hint);
opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE |
GIT_ITERATOR_INCLUDE_CONFLICTS;
if ((error = git_iterator_for_index(&index_iterator, git_index_owner(index), index, &opts)) < 0 ||
(error = git_iterator_for_index(&new_iterator, git_index_owner(new_index), (git_index *)new_index, &opts)) < 0)
goto done;
if (((error = git_iterator_current(&old_entry, index_iterator)) < 0 &&
if ((error = git_iterator_for_index(&index_iterator,
git_index_owner(index), index, &opts)) < 0 ||
((error = git_iterator_current(&old_entry, index_iterator)) < 0 &&
error != GIT_ITEROVER) ||
((error = git_iterator_current(&new_entry, new_iterator)) < 0 &&
error != GIT_ITEROVER))
@ -3050,6 +3140,8 @@ int git_index_read_index(
index_entry_free(entry);
}
clear_uptodate(index);
error = 0;
done:
@ -3057,6 +3149,27 @@ done:
git_vector_free(&new_entries);
git_vector_free(&remove_entries);
git_iterator_free(index_iterator);
return error;
}
int git_index_read_index(
git_index *index,
const git_index *new_index)
{
git_iterator *new_iterator = NULL;
git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
int error;
opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE |
GIT_ITERATOR_INCLUDE_CONFLICTS;
if ((error = git_iterator_for_index(&new_iterator,
git_index_owner(new_index), (git_index *)new_index, &opts)) < 0 ||
(error = git_index_read_iterator(index, new_iterator,
new_index->entries.length)) < 0)
goto done;
done:
git_iterator_free(new_iterator);
return error;
}

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