mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-02 16:34:37 +00:00
Imported Upstream version 0.25.1
This commit is contained in:
commit
9c87194ea8
14
.github/ISSUE_TEMPLATE
vendored
Normal file
14
.github/ISSUE_TEMPLATE
vendored
Normal 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
|
@ -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
|
||||
|
158
CHANGELOG.md
158
CHANGELOG.md
@ -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
|
||||
------
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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
|
||||
|
21
README.md
21
README.md
@ -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>
|
||||
|
||||
|
44
THREADING.md
44
THREADING.md
@ -62,33 +62,38 @@ general case still affects you if you use ssh.
|
||||
General Case
|
||||
------------
|
||||
|
||||
By default we use libcurl, which has its own .
|
||||
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)
|
||||
|
||||
|
18
appveyor.yml
18
appveyor.yml
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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") &&
|
||||
|
1180
examples/general.c
1180
examples/general.c
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -98,7 +98,8 @@ typedef enum {
|
||||
GITERR_CHERRYPICK,
|
||||
GITERR_DESCRIBE,
|
||||
GITERR_REBASE,
|
||||
GITERR_FILESYSTEM
|
||||
GITERR_FILESYSTEM,
|
||||
GITERR_PATCH,
|
||||
} git_error_t;
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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
92
include/git2/proxy.h
Normal 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
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
177
include/git2/sys/merge.h
Normal 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
|
@ -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
16
include/git2/sys/remote.h
Normal 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
|
@ -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
31
include/git2/sys/time.h
Normal 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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
75
script/user_model.c
Normal 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;
|
||||
}
|
@ -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);
|
||||
|
@ -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
377
src/apply.c
Normal 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
21
src/apply.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#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
|
@ -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,
|
||||
|
118
src/blob.c
118
src/blob.c
@ -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;
|
||||
|
12
src/branch.c
12
src/branch.c
@ -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)
|
||||
|
272
src/buffer.c
272
src/buffer.c
@ -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("ed, '"');
|
||||
git_buf_put("ed, 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("ed, '\\');
|
||||
git_buf_putc("ed, whitespace[buf->ptr[i] - '\a']);
|
||||
}
|
||||
|
||||
/* double quote and backslash must be escaped */
|
||||
else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
|
||||
git_buf_putc("ed, '\\');
|
||||
git_buf_putc("ed, buf->ptr[i]);
|
||||
}
|
||||
|
||||
/* escape anything unprintable as octal */
|
||||
else if (buf->ptr[i] != ' ' &&
|
||||
(buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
|
||||
git_buf_printf("ed, "\\%03o", (unsigned char)buf->ptr[i]);
|
||||
}
|
||||
|
||||
/* yay, printable! */
|
||||
else {
|
||||
git_buf_putc("ed, buf->ptr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
git_buf_putc("ed, '"');
|
||||
|
||||
if (git_buf_oom("ed)) {
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
git_buf_swap("ed, buf);
|
||||
|
||||
done:
|
||||
git_buf_free("ed);
|
||||
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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
111
src/checkout.c
111
src/checkout.c
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
248
src/commit.c
248
src/commit.c
@ -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 **) ¤t, (git_object *) commit) < 0)
|
||||
if (git_commit_dup(¤t, (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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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(®ex, regexp, REG_EXTENDED)) != 0) {
|
||||
if ((error = p_regcomp(®ex, regexp, REG_EXTENDED)) != 0) {
|
||||
giterr_set_regex(®ex, error);
|
||||
regfree(®ex);
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
300
src/delta.c
300
src/delta.c
@ -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;
|
||||
}
|
||||
|
141
src/delta.h
141
src/delta.h
@ -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
|
||||
|
@ -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
1586
src/diff.c
File diff suppressed because it is too large
Load Diff
136
src/diff.h
136
src/diff.h
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
1613
src/diff_generate.c
Normal file
File diff suppressed because it is too large
Load Diff
123
src/diff_generate.h
Normal file
123
src/diff_generate.h
Normal 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
101
src/diff_parse.c
Normal 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
18
src/diff_parse.h
Normal 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
|
@ -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
|
407
src/diff_print.c
407
src/diff_print.c
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
22
src/diff_tform.h
Normal 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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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`.
|
||||
*
|
||||
|
@ -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) &&
|
||||
|
25
src/global.c
25
src/global.c
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
205
src/index.c
205
src/index.c
@ -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 = ∅
|
||||
|
||||
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 = ∅
|
||||
|
||||
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
Loading…
Reference in New Issue
Block a user