Merge tag 'upstream/0.24.0'

Upstream version 0.24.0
This commit is contained in:
Andreas Henriksson 2016-04-03 14:22:53 +02:00
commit f92de12b3d
528 changed files with 13058 additions and 3636 deletions

View File

@ -16,6 +16,7 @@ Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.tk>
Sascha Cunz <sascha@babbelbox.org> <Sascha@BabbelBox.org>
Authmillenon <authmillenon@googlemail.com> <martin@ucsmail.de>
Authmillenon <authmillenon@googlemail.com> <authmillenon@googlemail.com>
Edward Thomson <ethomson@microsoft.com> <ethomson@edwardthomson.com>
Edward Thomson <ethomson@github.com> <ethomson@microsoft.com>
Edward Thomson <ethomson@github.com> <ethomson@edwardthomson.com>
J. David Ibáñez <jdavid.ibp@gmail.com> <jdavid@itaapy.com>
Russell Belfer <rb@github.com> <arrbee@arrbee.com>

View File

@ -46,13 +46,13 @@ matrix:
- compiler: gcc
env:
- VALGRIND=1
OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug"
OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
os: linux
allow_failures:
- env: COVERITY=1
- env:
- VALGRIND=1
OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug"
OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi

View File

@ -1,4 +1,4 @@
v0.23 + 1
v0.24 + 1
-------
### Changes or improvements
@ -7,6 +7,116 @@ v0.23 + 1
### API removals
### Breaking API changes
v0.24
-------
### Changes or improvements
* Custom filters can now be registered with wildcard attributes, for
example `filter=*`. Consumers should examine the attributes parameter
of the `check` function for details.
* Symlinks are now followed when locking a file, which can be
necessary when multiple worktrees share a base repository.
* You can now set your own user-agent to be sent for HTTP requests by
using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`.
* You can set custom HTTP header fields to be sent along with requests
by passing them in the fetch and push options.
* Tree objects are now assumed to be sorted. If a tree is not
correctly formed, it will give bad results. This is the git approach
and cuts a significant amount of time when reading the trees.
* Filter registration is now protected against concurrent
registration.
* Filenames which are not valid on Windows in an index no longer cause
to fail to parse it on that OS.
* Rebases can now be performed purely in-memory, without touching the
repository's workdir.
* When adding objects to the index, or when creating new tree or commit
objects, the inputs are validated to ensure that the dependent objects
exist and are of the correct type. This object validation can be
disabled with the GIT_OPT_ENABLE_STRICT_OBJECT_CREATION option.
* The WinHTTP transport's handling of bad credentials now behaves like
the others, asking for credentials again.
### API additions
* `git_config_lock()` has been added, which allow for
transactional/atomic complex updates to the configuration, removing
the opportunity for concurrent operations and not committing any
changes until the unlock.
* `git_diff_options` added a new callback `progress_cb` to report on the
progress of the diff as files are being compared. The documentation of
the existing callback `notify_cb` was updated to reflect that it only
gets called when new deltas are added to the diff.
* `git_fetch_options` and `git_push_options` have gained a `custom_headers`
field to set the extra HTTP header fields to send.
* `git_stream_register_tls()` lets you register a callback to be used
as the constructor for a TLS stream instead of the libgit2 built-in
one.
* `git_commit_header_field()` allows you to look up a specific header
field in a commit.
* `git_commit_extract_signature()` extracts the signature from a
commit and gives you both the signature and the signed data so you
can verify it.
### API removals
* No APIs were removed in this version.
### Breaking API changes
* 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
`git_merge_options` structure is now named `flags`.
* The `git_merge_file_flags_t` enum is now `git_merge_file_flag_t` for
consistency with other enum type names.
* `git_cert` descendent types now have a proper `parent` member
* It is the responsibility of the refdb backend to decide what to do
with the reflog on ref deletion. The file-based backend must delete
it, a database-backed one may wish to archive it.
* `git_config_backend` has gained two entries. `lock` and `unlock`
with which to implement the transactional/atomic semantics for the
configuration backend.
* `git_index_add` and `git_index_conflict_add()` will now use the case
as provided by the caller on case insensitive systems. Previous
versions would keep the case as it existed in the index. This does
not affect the higher-level `git_index_add_bypath` or
`git_index_add_frombuffer` functions.
* The `notify_payload` field of `git_diff_options` was renamed to `payload`
to reflect that it's also the payload for the new progress callback.
* The `git_config_level_t` enum has gained a higher-priority value
`GIT_CONFIG_LEVEL_PROGRAMDATA` which represent a rough Windows equivalent
to the system level configuration.
* `git_rebase_init()` not also takes a merge options.
* 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.
v0.23
------
@ -239,8 +349,8 @@ v0.23
* `git_rebase_options` now contains a `git_checkout_options` struct
that will be used for functions that modify the working directory,
namely `git_checkout_init`, `git_checkout_next` and
`git_checkout_abort`. As a result, `git_rebase_open` now also takes
namely `git_rebase_init`, `git_rebase_next` and
`git_rebase_abort`. As a result, `git_rebase_open` now also takes
a `git_rebase_options` and only the `git_rebase_init` and
`git_rebase_open` functions take a `git_rebase_options`, where they
will persist the options to subsequent `git_rebase` calls.

View File

@ -19,6 +19,8 @@ CMAKE_POLICY(SET CMP0015 NEW)
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
INCLUDE(CheckLibraryExists)
INCLUDE(CheckFunctionExists)
INCLUDE(CheckStructHasMember)
INCLUDE(AddCFlagIfSupported)
INCLUDE(FindPkgConfig)
@ -39,6 +41,11 @@ OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF )
OPTION( VALGRIND "Configure build for valgrind" OFF )
OPTION( CURL "User curl for HTTP if available" ON)
OPTION( DEBUG_POOL "Enable debug pool allocator" OFF )
IF(DEBUG_POOL)
ADD_DEFINITIONS(-DGIT_DEBUG_POOL)
ENDIF()
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
SET( USE_ICONV ON )
@ -59,6 +66,10 @@ IF(MSVC)
# are linking statically
OPTION( STATIC_CRT "Link the static CRT libraries" ON )
# If you want to embed a copy of libssh2 into libgit2, pass a
# path to libssh2
OPTION( EMBED_SSH_PATH "Path to libssh2 to embed (Windows)" OFF )
ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
@ -80,6 +91,27 @@ IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
OPTION( USE_OPENSSL "Link with and use openssl library" ON )
ENDIF()
CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtim "sys/types.h;sys/stat.h"
HAVE_STRUCT_STAT_ST_MTIM LANGUAGE C)
CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtimespec "sys/types.h;sys/stat.h"
HAVE_STRUCT_STAT_ST_MTIMESPEC LANGUAGE C)
CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_nsec sys/stat.h
HAVE_STRUCT_STAT_MTIME_NSEC LANGUAGE C)
IF (HAVE_STRUCT_STAT_ST_MTIM)
CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h
HAVE_STRUCT_STAT_NSEC LANGUAGE C)
ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC)
CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec sys/stat.h
HAVE_STRUCT_STAT_NSEC LANGUAGE C)
ELSE ()
SET( HAVE_STRUCT_STAT_NSEC ON )
ENDIF()
IF (HAVE_STRUCT_STAT_NSEC OR WIN32)
OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF )
ENDIF()
# This variable will contain the libraries we need to put into
# libgit2.pc's Requires.private. That is, what we're linking to or
# what someone who's statically linking us needs to link to.
@ -95,6 +127,23 @@ SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.")
SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.")
SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.")
# Set a couple variables to be substituted inside the .pc file.
# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue
# or relative paths is both valid and supported by cmake.
SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX})
IF(IS_ABSOLUTE ${LIB_INSTALL_DIR})
SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR})
ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR})
SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}")
ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR})
IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR})
ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}")
ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
FUNCTION(TARGET_OS_LIBRARIES target)
IF(WIN32)
TARGET_LINK_LIBRARIES(${target} ws2_32)
@ -115,13 +164,13 @@ FUNCTION(TARGET_OS_LIBRARIES target)
ENDIF()
ENDFUNCTION()
# For the MSVC IDE, this function splits up the source files like windows
# explorer does. This is esp. useful with the libgit2_clar project, were
# usually 2 or more files share the same name. Sadly, this file grouping
# is a per-directory option in cmake and not per-target, resulting in
# empty virtual folders "tests" for the git2.dll
FUNCTION(MSVC_SPLIT_SOURCES target)
IF(MSVC_IDE)
# This function splits the sources files up into their appropriate
# subdirectories. This is especially useful for IDEs like Xcode and
# Visual Studio, so that you can navigate into the libgit2_clar project,
# and see the folders within the tests folder (instead of just seeing all
# source and tests in a single folder.)
FUNCTION(IDE_SPLIT_SOURCES target)
IF(MSVC_IDE OR CMAKE_GENERATOR STREQUAL Xcode)
GET_TARGET_PROPERTY(sources ${target} SOURCES)
FOREACH(source ${sources})
IF(source MATCHES ".*/")
@ -152,8 +201,18 @@ STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "$
INCLUDE_DIRECTORIES(src include)
IF (SECURITY_FOUND)
MESSAGE("-- Found Security ${SECURITY_DIRS}")
LIST(APPEND LIBGIT2_PC_LIBS "-framework Security")
# OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there
CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY)
IF (HAVE_NEWER_SECURITY)
MESSAGE("-- Found Security ${SECURITY_DIRS}")
LIST(APPEND LIBGIT2_PC_LIBS "-framework Security")
ELSE()
MESSAGE("-- Security framework is too old, falling back to OpenSSL")
SET(SECURITY_FOUND "NO")
SET(SECURITY_DIRS "")
SET(SECURITY_DIR "")
SET(USE_OPENSSL "ON")
ENDIF()
ENDIF()
IF (COREFOUNDATION_FOUND)
@ -162,6 +221,13 @@ IF (COREFOUNDATION_FOUND)
ENDIF()
IF (WIN32 AND EMBED_SSH_PATH)
FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
INCLUDE_DIRECTORIES("${EMBED_SSH_PATH}/include")
FILE(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
ADD_DEFINITIONS(-DGIT_SSH)
ENDIF()
IF (WIN32 AND WINHTTP)
ADD_DEFINITIONS(-DGIT_WINHTTP)
INCLUDE_DIRECTORIES(deps/http-parser)
@ -178,7 +244,7 @@ IF (WIN32 AND WINHTTP)
SET(LIBWINHTTP_PATH "${CMAKE_CURRENT_BINARY_DIR}/deps/winhttp")
FILE(MAKE_DIRECTORY ${LIBWINHTTP_PATH})
IF ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
set(WINHTTP_DEF "${CMAKE_CURRENT_SOURCE_DIR}/deps/winhttp/winhttp64.def")
ELSE()
set(WINHTTP_DEF "${CMAKE_CURRENT_SOURCE_DIR}/deps/winhttp/winhttp.def")
@ -200,7 +266,8 @@ IF (WIN32 AND WINHTTP)
LINK_DIRECTORIES(${LIBWINHTTP_PATH})
ENDIF ()
LINK_LIBRARIES(winhttp rpcrt4 crypt32)
LINK_LIBRARIES(winhttp rpcrt4 crypt32 ole32)
LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp" "-lrpcrt4" "-lcrypt32" "-lole32")
ELSE ()
IF (CURL)
PKG_CHECK_MODULES(CURL libcurl)
@ -286,7 +353,7 @@ IF (LIBSSH2_FOUND)
#SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}")
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS)
ADD_DEFINITIONS(-DGIT_SSH_MEMORY_CREDENTIALS)
ENDIF()
@ -336,6 +403,7 @@ IF (MSVC)
IF (MSVC_CRTDBG)
SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG} /DGIT_MSVC_CRTDBG")
SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}" "Dbghelp.lib")
ENDIF()
# /Zi - Create debugging information
@ -430,6 +498,21 @@ ELSE ()
ENDIF ()
ENDIF()
CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
IF (HAVE_FUTIMENS)
ADD_DEFINITIONS(-DHAVE_FUTIMENS)
ENDIF ()
CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R)
IF (HAVE_QSORT_R)
ADD_DEFINITIONS(-DHAVE_QSORT_R)
ENDIF ()
CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S)
IF (HAVE_QSORT_S)
ADD_DEFINITIONS(-DHAVE_QSORT_S)
ENDIF ()
IF( NOT CMAKE_CONFIGURATION_TYPES )
# Build Debug by default
IF (NOT CMAKE_BUILD_TYPE)
@ -461,6 +544,18 @@ IF (THREADSAFE)
ADD_DEFINITIONS(-DGIT_THREADS)
ENDIF()
IF (USE_NSEC)
ADD_DEFINITIONS(-DGIT_USE_NSEC)
ENDIF()
IF (HAVE_STRUCT_STAT_ST_MTIM)
ADD_DEFINITIONS(-DGIT_USE_STAT_MTIM)
ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC)
ADD_DEFINITIONS(-DGIT_USE_STAT_MTIMESPEC)
ELSEIF (HAVE_STRUCT_STAT_ST_MTIME_NSEC)
ADD_DEFINITIONS(-DGIT_USE_STAT_MTIME_NSEC)
ENDIF()
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
# Collect sourcefiles
@ -490,7 +585,7 @@ ELSE()
ENDIF()
# Compile and link libgit2
ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1} ${WIN_RC})
TARGET_LINK_LIBRARIES(git2 ${SECURITY_DIRS})
TARGET_LINK_LIBRARIES(git2 ${COREFOUNDATION_DIRS})
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
@ -505,7 +600,7 @@ IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
ENDIF()
MSVC_SPLIT_SOURCES(git2)
IDE_SPLIT_SOURCES(git2)
IF (SONAME)
SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
@ -536,7 +631,12 @@ INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
# Tests
IF (BUILD_CLAR)
FIND_PACKAGE(PythonInterp REQUIRED)
FIND_PACKAGE(PythonInterp)
IF(NOT PYTHONINTERP_FOUND)
MESSAGE(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. "
"Make sure python is available, or pass -DBUILD_CLAR=OFF to skip building the tests")
ENDIF()
SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/")
SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests")
@ -560,7 +660,7 @@ IF (BUILD_CLAR)
${CLAR_PATH}/clar.c
PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite)
ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1})
TARGET_LINK_LIBRARIES(libgit2_clar ${COREFOUNDATION_DIRS})
TARGET_LINK_LIBRARIES(libgit2_clar ${SECURITY_DIRS})
@ -569,7 +669,7 @@ IF (BUILD_CLAR)
TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(libgit2_clar)
MSVC_SPLIT_SOURCES(libgit2_clar)
IDE_SPLIT_SOURCES(libgit2_clar)
IF (MSVC_IDE)
# Precompiled headers
@ -582,6 +682,10 @@ IF (BUILD_CLAR)
ELSE ()
ADD_TEST(libgit2_clar libgit2_clar -v)
ENDIF ()
# 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)
ENDIF ()
IF (TAGS)

75
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,75 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [libgit2@gmail.com][email]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[email]: mailto:libgit2@gmail.com
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -3,6 +3,38 @@
We like to keep the source consistent and readable. Herein are some
guidelines that should help with that.
## External API
We have a few rules to avoid surprising ways of calling functions and
some rules for consumers of the library to avoid stepping on each
other's toes.
- Property accessors return the value directly (e.g. an `int` or
`const char *`) but if a function can fail, we return a `int` value
and the output parameters go first in the parameter list, followed
by the object that a function is operating on, and then any other
arguments the function may need.
- If a function returns an object as a return value, that function is
a getter and the object's lifetime is tied to the parent
object. Objects which are returned as the first argument as a
pointer-to-pointer are owned by the caller and it is repsponsible
for freeing it. Strings are returned via `git_buf` in order to
allow for re-use and safe freeing.
- Most of what libgit2 does relates to I/O so you as a general rule
you should assume that any function can fail due to errors as even
getting data from the filesystem can result in all sorts of errors
and complex failure cases.
- Paths inside the Git system are separated by a slash (0x2F). If a
function accepts a path on disk, then backslashes (0x5C) are also
accepted on Windows.
- Do not mix allocators. If something has been allocated by libgit2,
you do not know which is the right free function in the general
case. Use the free functions provided for each object type.
## Compatibility
`libgit2` runs on many different platforms with many different compilers.

46
COPYING
View File

@ -407,6 +407,52 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
----------------------------------------------------------------------
The regex library (deps/regex/) is licensed under the GNU LGPL
(available at the end of this file).
Definitions for data structures and routines for the regular
expression library.
Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008
Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.
----------------------------------------------------------------------
The bundled winhttp definition files (deps/winhttp/) are licensed under
the GNU LGPL (available at the end of this file).
Copyright (C) 2007 Francois Gouget
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
----------------------------------------------------------------------
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999

View File

@ -48,7 +48,7 @@ These are good small projects to get started with libgit2.
a new example that mirrors the behavior. Examples don't have to be
perfect emulations, but should demonstrate how to use the libgit2 APIs
to get results that are similar to Git commands. This lets you (and us)
easily exercise a particular facet of the API and measure compatability
easily exercise a particular facet of the API and measure compatibility
and feature parity with core git.
* Submit a PR to clarify documentation! While we do try to document all of
the APIs, your fresh eyes on the documentation will find areas that are
@ -75,8 +75,6 @@ might make good smaller projects by themselves.
* Extract the Git tests that exercise that command
* Convert the tests to call our emulation
* These tests could go in examples/tests/...
* Fix symlink support for files in the .git directory (i.e. don't overwrite
the symlinks when writing the file contents back out)
* 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
@ -85,8 +83,6 @@ might make good smaller projects by themselves.
executes the action in question
* Isolate logic of ignore evaluation into a standalone API
* Upgrade internal libxdiff code to latest from core Git
* Improve index internals with hashtable lookup for files instead of
using binary search every time
* Tree builder improvements:
* Extend to allow building a tree hierarchy
* Apply-patch API

View File

@ -18,7 +18,7 @@ Additionally, the example code has been released to the public domain (see the
* Website: [libgit2.github.com](http://libgit2.github.com)
* StackOverflow Tag: [libgit2](http://stackoverflow.com/questions/tagged/libgit2)
* Issues: [GitHub Issues](https://github.com/libgit2/libgit2/issues) (Right here!)
* API documentation: <http://libgit2.github.com/libgit2>
* API documentation: <http://libgit2.github.com/libgit2/>
* IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net.
* Mailing list: The libgit2 mailing list was
traditionally hosted in Librelist but has been deprecated. We encourage you to
@ -80,6 +80,12 @@ Threading
See [THREADING](THREADING.md) for information
Conventions
===========
See [CONVENTIONS](CONVENTIONS.md) for an overview of the external
and internal API/coding conventions we use.
Building libgit2 - Using CMake
==============================
@ -88,7 +94,7 @@ Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthrea
they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
for threading.
The `libgit2` library is built using [CMake](<http://www.cmake.org>) (version 2.8 or newer) on all platforms.
The `libgit2` library is built using [CMake](<https://cmake.org/>) (version 2.8 or newer) on all platforms.
On most systems you can build the library using the following commands
@ -103,7 +109,7 @@ To install the library you can specify the install prefix by setting:
$ cmake .. -DCMAKE_INSTALL_PREFIX=/install/prefix
$ cmake --build . --target install
For more advanced use or questions about CMake please read <http://www.cmake.org/Wiki/CMake_FAQ>.
For more advanced use or questions about CMake please read <https://cmake.org/Wiki/CMake_FAQ>.
The following CMake variables are declared:
@ -141,7 +147,7 @@ You need to run the CMake commands from the Visual Studio command
prompt, not the regular or Windows SDK one. Select the right generator
for your version with the `-G "Visual Studio X" option.
See [the website](https://libgit2.github.com/docs/guides/build-and-link)
See [the website](http://libgit2.github.com/docs/guides/build-and-link/)
for more detailed instructions.
Android
@ -184,9 +190,9 @@ Here are the bindings to libgit2 that are currently available:
* Go
* git2go <https://github.com/libgit2/git2go>
* GObject
* libgit2-glib <https://live.gnome.org/Libgit2-glib>
* libgit2-glib <https://wiki.gnome.org/Projects/Libgit2-glib>
* Haskell
* hgit2 <https://github.com/fpco/gitlib>
* hgit2 <https://github.com/jwiegley/gitlib>
* Java
* Jagged <https://github.com/ethomson/jagged>
* Julia
@ -197,7 +203,7 @@ Here are the bindings to libgit2 that are currently available:
* libgit2sharp <https://github.com/libgit2/libgit2sharp>
* Node.js
* node-gitteh <https://github.com/libgit2/node-gitteh>
* nodegit <https://github.com/tbranyen/nodegit>
* nodegit <https://github.com/nodegit/nodegit>
* Objective-C
* objective-git <https://github.com/libgit2/objective-git>
* OCaml
@ -230,7 +236,7 @@ How Can I Contribute?
==================================
Check the [contribution guidelines](CONTRIBUTING.md) to understand our
workflow, the libgit2 [coding conventions](CONVENTIONS.md), and out list of
workflow, the libgit2 [coding conventions](CONVENTIONS.md), and our list of
[good starting projects](PROJECTS.md).
License

View File

@ -72,13 +72,19 @@ 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.
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.
libgit2 does provide 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
safe to use OpenSSL multi-threaded after libgit2's shutdown function
has been called.
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.
If your programming language offers a package/bindings for OpenSSL,
you should very strongly prefer to use that in order to set up
@ -87,14 +93,14 @@ when using this function.
See the
[OpenSSL documentation](https://www.openssl.org/docs/crypto/threads.html)
on threading for more details.
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, you only need to set up threading for OpenSSL once and the
above paragraphs are enough. If it uses libgcrypt, then you need to
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
it (but libgcrypt has macros). Read libgcrypt's

View File

@ -36,4 +36,8 @@ build_script:
- cmd: |
if "%GENERATOR%"=="MSYS Makefiles" (C:\MinGW\msys\1.0\bin\sh --login /c/projects/libgit2/script/appveyor-mingw.sh)
test_script:
- ps: ctest -V .
- ps: |
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

View File

@ -128,7 +128,7 @@ The public error API
bugs, but in the meantime, please code defensively and check for NULL
when calling this function.
- `void geterr_clear(void)`: This function clears the last error. The
- `void giterr_clear(void)`: This function clears the last error. The
library will call this when an error is generated by low level function
and the higher level function handles the error.

View File

@ -23,32 +23,6 @@ static int progress_cb(const char *str, int len, void *data)
return 0;
}
static void *download(void *ptr)
{
struct dl_data *data = (struct dl_data *)ptr;
// Connect to the remote end specifying that we want to fetch
// information from it.
if (git_remote_connect(data->remote, GIT_DIRECTION_FETCH, &data->fetch_opts->callbacks) < 0) {
data->ret = -1;
goto exit;
}
// Download the packfile and index it. This function updates the
// amount of received data and the indexer stats which lets you
// inform the user about progress.
if (git_remote_download(data->remote, NULL, data->fetch_opts) < 0) {
data->ret = -1;
goto exit;
}
data->ret = 0;
exit:
data->finished = 1;
return &data->ret;
}
/**
* This function gets called for each remote-tracking branch that gets
* updated. The message we output depends on whether it's a new one or
@ -73,6 +47,25 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo
return 0;
}
/**
* This gets called during the download and indexing. Here we show
* processed and total objects in the pack and the amount of received
* data. Most frontends will probably want to show a percentage and
* the download rate.
*/
static int transfer_progress_cb(const git_transfer_progress *stats, void *payload)
{
if (stats->received_objects == stats->total_objects) {
printf("Resolving deltas %d/%d\r",
stats->indexed_deltas, stats->total_deltas);
} else if (stats->total_objects > 0) {
printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
stats->received_objects, stats->total_objects,
stats->indexed_objects, stats->received_bytes);
}
return 0;
}
/** Entry point for this command */
int fetch(git_repository *repo, int argc, char **argv)
{
@ -80,9 +73,6 @@ int fetch(git_repository *repo, int argc, char **argv)
const git_transfer_progress *stats;
struct dl_data data;
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
#ifndef _WIN32
pthread_t worker;
#endif
if (argc < 2) {
fprintf(stderr, "usage: %s fetch <repo>\n", argv[-1]);
@ -99,49 +89,23 @@ int fetch(git_repository *repo, int argc, char **argv)
// 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;
fetch_opts.callbacks.credentials = cred_acquire_cb;
// Set up the information for the background worker thread
data.remote = remote;
data.fetch_opts = &fetch_opts;
data.ret = 0;
data.finished = 0;
stats = git_remote_stats(remote);
#ifdef _WIN32
download(&data);
#else
pthread_create(&worker, NULL, download, &data);
// Loop while the worker thread is still running. Here we show processed
// and total objects in the pack and the amount of received
// data. Most frontends will probably want to show a percentage and
// the download rate.
do {
usleep(10000);
if (stats->received_objects == stats->total_objects) {
printf("Resolving deltas %d/%d\r",
stats->indexed_deltas, stats->total_deltas);
} else if (stats->total_objects > 0) {
printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
stats->received_objects, stats->total_objects,
stats->indexed_objects, stats->received_bytes);
}
} while (!data.finished);
if (data.ret < 0)
goto on_error;
pthread_join(worker, NULL);
#endif
/**
* Perform the fetch with the configured refspecs from the
* config. Update the reflog for the updated references with
* "fetch".
*/
if (git_remote_fetch(remote, NULL, &fetch_opts, "fetch") < 0)
return -1;
/**
* If there are local objects (we got a thin pack), then tell
* the user how many objects we saved from having to cross the
* network.
*/
stats = git_remote_stats(remote);
if (stats->local_objects > 0) {
printf("\rReceived %d/%d objects in %" PRIuZ " bytes (used %d local objects)\n",
stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
@ -150,16 +114,6 @@ int fetch(git_repository *repo, int argc, char **argv)
stats->indexed_objects, stats->total_objects, stats->received_bytes);
}
// Disconnect the underlying connection to prevent from idling.
git_remote_disconnect(remote);
// Update the references in the remote's namespace to point to the
// right commits. This may be needed even if there was no packfile
// to download, which can happen e.g. when the branches have been
// changed but all the needed objects are available locally.
if (git_remote_update_tips(remote, &fetch_opts.callbacks, 1, fetch_opts.download_tags, NULL) < 0)
return -1;
git_remote_free(remote);
return 0;

View File

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

View File

@ -39,6 +39,7 @@ ok Adam Simpkins <adam@adamsimpkins.net> (http transport)
ok Adrian Johnson <ajohnson@redneon.com>
ok Alexey Shumkin <alex.crezoff@gmail.com>
ok Andreas Ericsson <ae@op5.se>
ok Antoine Pelisse <apelisse@gmail.com>
ok Boyd Lynn Gerber <gerberb@zenez.com>
ok Brandon Casey <drafnel@gmail.com>
ok Brian Downing <bdowning@lavos.net>

View File

@ -74,8 +74,8 @@ typedef struct git_blame_options {
uint16_t min_match_characters;
git_oid newest_commit;
git_oid oldest_commit;
uint32_t min_line;
uint32_t max_line;
size_t min_line;
size_t max_line;
} git_blame_options;
#define GIT_BLAME_OPTIONS_VERSION 1
@ -113,15 +113,15 @@ GIT_EXTERN(int) git_blame_init_options(
* root, or the commit specified in git_blame_options.oldest_commit)
*/
typedef struct git_blame_hunk {
uint16_t lines_in_hunk;
size_t lines_in_hunk;
git_oid final_commit_id;
uint16_t final_start_line_number;
size_t final_start_line_number;
git_signature *final_signature;
git_oid orig_commit_id;
const char *orig_path;
uint16_t orig_start_line_number;
size_t orig_start_line_number;
git_signature *orig_signature;
char boundary;
@ -156,7 +156,7 @@ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex(
*/
GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
git_blame *blame,
uint32_t lineno);
size_t lineno);
/**
* Get the blame for a single file.

View File

@ -171,8 +171,8 @@ typedef int (*git_blob_chunk_cb)(char *content, size_t max_length, void *payload
* - 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.
* - 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.

View File

@ -127,6 +127,19 @@ GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
*/
GIT_EXTERN(const char *) git_commit_summary(git_commit *commit);
/**
* Get the long "body" of the git commit message.
*
* The returned message is the body of the commit, comprising
* everything but the first paragraph of the message. Leading and
* trailing whitespaces are trimmed.
*
* @param commit a previously loaded commit.
* @return the body of a commit or NULL when no the message only
* consists of a summary
*/
GIT_EXTERN(const char *) git_commit_body(git_commit *commit);
/**
* Get the commit time (i.e. committer time) of a commit.
*
@ -250,6 +263,24 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
*/
GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field);
/**
* Extract the signature from a commit
*
* If the id is not for a commit, the error class will be
* `GITERR_INVALID`. If the commit does not have a signature, the
* error class will be `GITERR_OBJECT`.
*
* @param signature the signature block
* @param signed_data signed data; this is the commit contents minus the signature block
* @param repo the repository in which the commit exists
* @param commit_id the commit from which to extract the data
* @param field the name of the header field containing the signature
* block; pass `NULL` to extract the default 'gpgsig'
* @return 0 on success, GIT_ENOTFOUND if the id is not for a commit
* or the commit does not have a signature.
*/
GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field);
/**
* Create new commit in the repository from a list of `git_object` pointers
*

View File

@ -10,12 +10,6 @@
#include <time.h>
#include <stdlib.h>
#ifdef _MSC_VER
# include "inttypes.h"
#else
# include <inttypes.h>
#endif
#ifdef __cplusplus
# define GIT_BEGIN_DECL extern "C" {
# define GIT_END_DECL }
@ -26,6 +20,14 @@
# define GIT_END_DECL /* empty */
#endif
#if defined(_MSC_VER) && _MSC_VER < 1800
GIT_BEGIN_DECL
# include "inttypes.h"
GIT_END_DECL
#else
# include <inttypes.h>
#endif
/** Declare a public function exported for application use. */
#if __GNUC__ >= 4
# define GIT_EXTERN(type) extern \
@ -99,8 +101,9 @@ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev);
*/
typedef enum {
GIT_FEATURE_THREADS = (1 << 0),
GIT_FEATURE_HTTPS = (1 << 1),
GIT_FEATURE_SSH = (1 << 2),
GIT_FEATURE_HTTPS = (1 << 1),
GIT_FEATURE_SSH = (1 << 2),
GIT_FEATURE_NSEC = (1 << 3),
} git_feature_t;
/**
@ -143,6 +146,8 @@ typedef enum {
GIT_OPT_GET_TEMPLATE_PATH,
GIT_OPT_SET_TEMPLATE_PATH,
GIT_OPT_SET_SSL_CERT_LOCATIONS,
GIT_OPT_SET_USER_AGENT,
GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
} git_libgit2_opt_t;
/**
@ -170,9 +175,9 @@ typedef enum {
* * opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf)
*
* > Get the search path for a given level of config data. "level" must
* > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, or
* > `GIT_CONFIG_LEVEL_XDG`. The search path is written to the `out`
* > buffer.
* > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`,
* > `GIT_CONFIG_LEVEL_XDG`, or `GIT_CONFIG_LEVEL_PROGRAMDATA`.
* > The search path is written to the `out` buffer.
*
* * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
*
@ -184,8 +189,9 @@ typedef enum {
* > variables). Use magic path `$PATH` to include the old value
* > of the path (if you want to prepend or append, for instance).
* >
* > - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL,
* > or GIT_CONFIG_LEVEL_XDG.
* > - `level` must be `GIT_CONFIG_LEVEL_SYSTEM`,
* > `GIT_CONFIG_LEVEL_GLOBAL`, `GIT_CONFIG_LEVEL_XDG`, or
* > `GIT_CONFIG_LEVEL_PROGRAMDATA`.
*
* * opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, git_otype type, size_t size)
*
@ -238,6 +244,22 @@ typedef enum {
* >
* > Either parameter may be `NULL`, but not both.
*
* * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent)
*
* > Set the value of the User-Agent header. This value will be
* > appended to "git/1.0", for compatibility with other git clients.
* >
* > - `user_agent` is the value that will be delivered as the
* > User-Agent header on HTTP requests.
*
* * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled)
*
* > Enable strict input validation when creating new objects
* > 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.
*
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure

View File

@ -29,25 +29,28 @@ GIT_BEGIN_DECL
* priority levels as well.
*/
typedef enum {
/** System-wide on Windows, for compatibility with portable git */
GIT_CONFIG_LEVEL_PROGRAMDATA = 1,
/** System-wide configuration file; /etc/gitconfig on Linux systems */
GIT_CONFIG_LEVEL_SYSTEM = 1,
GIT_CONFIG_LEVEL_SYSTEM = 2,
/** XDG compatible configuration file; typically ~/.config/git/config */
GIT_CONFIG_LEVEL_XDG = 2,
GIT_CONFIG_LEVEL_XDG = 3,
/** User-specific configuration file (also called Global configuration
* file); typically ~/.gitconfig
*/
GIT_CONFIG_LEVEL_GLOBAL = 3,
GIT_CONFIG_LEVEL_GLOBAL = 4,
/** Repository specific configuration file; $WORK_DIR/.git/config on
* non-bare repos
*/
GIT_CONFIG_LEVEL_LOCAL = 4,
GIT_CONFIG_LEVEL_LOCAL = 5,
/** Application specific configuration file; freely defined by applications
*/
GIT_CONFIG_LEVEL_APP = 5,
GIT_CONFIG_LEVEL_APP = 6,
/** Represents the highest level available config file (i.e. the most
* specific config file available that actually is loaded)
@ -141,6 +144,17 @@ GIT_EXTERN(int) git_config_find_xdg(git_buf *out);
*/
GIT_EXTERN(int) git_config_find_system(git_buf *out);
/**
* Locate the path to the configuration file in ProgramData
*
* Look for the file in %PROGRAMDATA%\Git\config used by portable git.
*
* @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a ProgramData configuration file has been
* found. Its path will be stored in `out`.
*/
GIT_EXTERN(int) git_config_find_programdata(git_buf *out);
/**
* Open the global, XDG and system configuration files
*
@ -171,6 +185,9 @@ GIT_EXTERN(int) git_config_new(git_config **out);
* parsed; it's expected to be a native Git config file following
* the default Git config syntax (see man git-config).
*
* If the file does not exist, the file will still be added and it
* will be created the first time we write to it.
*
* Note that the configuration object will free the file
* automatically.
*
@ -202,8 +219,7 @@ GIT_EXTERN(int) git_config_add_file_ondisk(
*
* @param out The configuration instance to create
* @param path Path to the on-disk file to open
* @return 0 on success, GIT_ENOTFOUND when the file doesn't exist
* or an error code
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path);
@ -689,6 +705,24 @@ GIT_EXTERN(int) git_config_backend_foreach_match(
void *payload);
/**
* Lock the backend with the highest priority
*
* Locking disallows anybody else from writing to that backend. Any
* updates made after locking will not be visible to a reader until
* the file is unlocked.
*
* You can apply the changes by calling `git_transaction_commit()`
* before freeing the transaction. Either of these actions will unlock
* the config.
*
* @param tx the resulting transaction, use this to commit or undo the
* changes
* @param cfg the configuration in which to lock
* @return 0 or an error code
*/
GIT_EXTERN(int) git_config_lock(git_transaction **tx, git_config *cfg);
/** @} */
GIT_END_DECL
#endif

View File

@ -34,7 +34,7 @@ typedef struct git_cred_userpass_payload {
*
* @param cred The newly created credential object.
* @param url The resource for which we are demanding a credential.
* @param user_from_url The username that was embedded in a "user@host"
* @param user_from_url The username that was embedded in a "user\@host"
* remote url, or NULL if not included.
* @param allowed_types A bitmask stating which cred types are OK to return.
* @param payload The payload provided when specifying this callback. (This is

View File

@ -129,8 +129,12 @@ typedef enum {
*/
GIT_DIFF_INCLUDE_CASECHANGE = (1u << 11),
/** If the pathspec is set in the diff options, this flags means to
* apply it as an exact match instead of as an fnmatch pattern.
/** If the pathspec is set in the diff options, this flags indicates
* that the paths will be treated as literal paths instead of
* fnmatch patterns. Each path in the list must either be a full
* path to a file or a directory. (A trailing slash indicates that
* the path will _only_ match a directory). If a directory is
* specified, all children will be included.
*/
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1u << 12),
@ -346,6 +350,22 @@ typedef int (*git_diff_notify_cb)(
const char *matched_pathspec,
void *payload);
/**
* Diff progress callback.
*
* Called before each file comparison.
*
* @param diff_so_far The diff being generated.
* @param old_path The path to the old file or NULL.
* @param new_path The path to the new file or NULL.
* @return Non-zero to abort the diff.
*/
typedef int (*git_diff_progress_cb)(
const git_diff *diff_so_far,
const char *old_path,
const char *new_path,
void *payload);
/**
* Structure describing options about how the diff should be executed.
*
@ -366,8 +386,10 @@ typedef int (*git_diff_notify_cb)(
* - `max_size` is a file size (in bytes) above which a blob will be marked
* as binary automatically; pass a negative value to disable.
* - `notify_cb` is an optional callback function, notifying the consumer of
* which files are being examined as the diff is generated
* - `notify_payload` is the payload data to pass to the `notify_cb` function
* changes to the diff as new deltas are added.
* - `progress_cb` is an optional callback function, notifying the consumer of
* which files are being examined as the diff is generated.
* - `payload` is the payload to pass to the callback functions.
* - `ignore_submodules` overrides the submodule ignore setting for all
* submodules in the diff.
*/
@ -379,8 +401,9 @@ typedef struct {
git_submodule_ignore_t ignore_submodules; /**< submodule ignore rule */
git_strarray pathspec; /**< defaults to include all paths */
git_diff_notify_cb notify_cb;
void *notify_payload;
git_diff_notify_cb notify_cb;
git_diff_progress_cb progress_cb;
void *payload;
/* options controlling how to diff text is generated */
@ -399,7 +422,7 @@ typedef struct {
* `git_diff_options_init` programmatic initialization.
*/
#define GIT_DIFF_OPTIONS_INIT \
{GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, 3}
{GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3}
/**
* Initializes a `git_diff_options` with default values. Equivalent to
@ -835,6 +858,25 @@ GIT_EXTERN(int) git_diff_tree_to_workdir_with_index(
git_tree *old_tree,
const git_diff_options *opts); /**< can be NULL for defaults */
/**
* Create a diff with the difference between two index objects.
*
* The first index will be used for the "old_file" side of the delta and the
* second index will be used for the "new_file" side of the delta.
*
* @param diff Output pointer to a git_diff pointer to be allocated.
* @param repo The repository containing the indexes.
* @param old_index A git_index object to diff from.
* @param new_index A git_index object to diff to.
* @param opts Structure with options to influence diff or NULL for defaults.
*/
GIT_EXTERN(int) git_diff_index_to_index(
git_diff **diff,
git_repository *repo,
git_index *old_index,
git_index *new_index,
const git_diff_options *opts); /**< can be NULL for defaults */
/**
* Merge one diff into another.
*
@ -1152,7 +1194,7 @@ typedef enum {
} git_diff_stats_format_t;
/**
* Accumlate diff statistics for all patches.
* Accumulate diff statistics for all patches.
*
* @param out Structure containg the diff statistics.
* @param diff A git_diff generated by one of the above functions.
@ -1244,12 +1286,15 @@ typedef struct {
/** Summary of the change */
const char *summary;
/** Commit message's body */
const char *body;
/** Author of the change */
const git_signature *author;
} git_diff_format_email_options;
#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1
#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL}
#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL}
/**
* Create an e-mail ready patch from a diff.

View File

@ -49,6 +49,7 @@ typedef enum {
GIT_EINVALID = -21, /**< Invalid operation or input */
GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */
GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */
GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */
GIT_PASSTHROUGH = -30, /**< Internal only */
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
@ -113,18 +114,6 @@ GIT_EXTERN(const git_error *) giterr_last(void);
*/
GIT_EXTERN(void) giterr_clear(void);
/**
* Get the last error data and clear it.
*
* This copies the last error into the given `git_error` struct
* and returns 0 if the copy was successful, leaving the error
* cleared as if `giterr_clear` had been called.
*
* If there was no existing error in the library, -1 will be returned
* and the contents of `cpy` will be left unmodified.
*/
GIT_EXTERN(int) giterr_detach(git_error *cpy);
/**
* Set the error message string for this thread.
*
@ -137,11 +126,6 @@ GIT_EXTERN(int) giterr_detach(git_error *cpy);
* This error message is stored in thread-local storage and only applies
* to the particular thread that this libgit2 call is made from.
*
* NOTE: Passing the `error_class` as GITERR_OS has a special behavior: we
* attempt to append the system default error message for the last OS error
* that occurred and then clear the last error. The specific implementation
* of looking up and clearing this last OS error will vary by platform.
*
* @param error_class One of the `git_error_t` enum above describing the
* general subsystem that is responsible for the error.
* @param string The formatted error message to keep

View File

@ -154,13 +154,27 @@ typedef enum {
GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2),
} git_index_add_option_t;
/**
* Match any index stage.
*
* Some index APIs take a stage to match; pass this value to match
* any entry matching the path regardless of stage.
*/
#define GIT_INDEX_STAGE_ANY -1
typedef enum {
/**
* Match any index stage.
*
* Some index APIs take a stage to match; pass this value to match
* any entry matching the path regardless of stage.
*/
GIT_INDEX_STAGE_ANY = -1,
/** A normal staged file in the index. */
GIT_INDEX_STAGE_NORMAL = 0,
/** The ancestor side of a conflict. */
GIT_INDEX_STAGE_ANCESTOR = 1,
/** The "ours" side of a conflict. */
GIT_INDEX_STAGE_OURS = 2,
/** The "theirs" side of a conflict. */
GIT_INDEX_STAGE_THEIRS = 3,
} git_index_stage_t;
/** @name Index File Functions
*
@ -643,6 +657,17 @@ GIT_EXTERN(int) git_index_update_all(
*/
GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path);
/**
* Find the first position of any entries matching a prefix. To find the first position
* of a path inside a given folder, suffix the prefix with a '/'.
*
* @param at_pos the address to which the position of the index entry is written (optional)
* @param index an existing index object
* @param prefix the prefix to search for
* @return 0 with valid value in at_pos; an error code otherwise
*/
GIT_EXTERN(int) git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix);
/**@}*/
/** @name Conflict Index Entry Functions

View File

@ -62,8 +62,8 @@ GIT_EXTERN(int) git_merge_file_init_input(
unsigned int version);
/**
* Flags for `git_merge_tree` options. A combination of these flags can be
* passed in via the `tree_flags` value in the `git_merge_options`.
* Flags for `git_merge` options. A combination of these flags can be
* passed in via the `flags` value in the `git_merge_options`.
*/
typedef enum {
/**
@ -71,8 +71,28 @@ typedef enum {
* side or the common ancestor and the "theirs" side. This will enable
* the ability to merge between a modified and renamed file.
*/
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
} git_merge_tree_flag_t;
GIT_MERGE_FIND_RENAMES = (1 << 0),
/**
* If a conflict occurs, exit immediately instead of attempting to
* continue resolving conflicts. The merge operation will fail with
* GIT_EMERGECONFLICT and no index will be returned.
*/
GIT_MERGE_FAIL_ON_CONFLICT = (1 << 1),
/**
* Do not write the REUC extension on the generated index
*/
GIT_MERGE_SKIP_REUC = (1 << 2),
/**
* If the commits being merged have multiple merge bases, do not build
* a recursive merge base (by merging the multiple merge bases),
* instead simply use the first base. This flag provides a similar
* merge base to `git-merge-resolve`.
*/
GIT_MERGE_NO_RECURSIVE = (1 << 3),
} git_merge_flag_t;
/**
* Merge file favor options for `git_merge_options` instruct the file-level
@ -140,7 +160,7 @@ typedef enum {
/** Take extra time to find minimal diff */
GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7),
} git_merge_file_flags_t;
} git_merge_file_flag_t;
/**
* Options for merging a file
@ -169,8 +189,8 @@ typedef struct {
/** The file to favor in region conflicts. */
git_merge_file_favor_t favor;
/** see `git_merge_file_flags_t` above */
unsigned int flags;
/** see `git_merge_file_flag_t` above */
git_merge_file_flag_t flags;
} git_merge_file_options;
#define GIT_MERGE_FILE_OPTIONS_VERSION 1
@ -220,11 +240,13 @@ typedef struct {
*/
typedef struct {
unsigned int version;
git_merge_tree_flag_t tree_flags;
/** See `git_merge_flag_t` above */
git_merge_flag_t flags;
/**
* Similarity to consider a file renamed (default 50). If
* `GIT_MERGE_TREE_FIND_RENAMES` is enabled, added files will be compared
* `GIT_MERGE_FIND_RENAMES` is enabled, added files will be compared
* with deleted files to determine their similarity. Files that are
* more similar than the rename threshold (percentage-wise) will be
* treated as a rename.
@ -243,11 +265,19 @@ typedef struct {
/** Pluggable similarity metric; pass NULL to use internal metric */
git_diff_similarity_metric *metric;
/**
* Maximum number of times to merge common ancestors to build a
* virtual merge base when faced with criss-cross merges. When this
* limit is reached, the next ancestor will simply be used instead of
* attempting to merge it. The default is unlimited.
*/
unsigned int recursion_limit;
/** Flags for handling conflicting content. */
git_merge_file_favor_t file_favor;
/** see `git_merge_file_flags_t` above */
unsigned int file_flags;
/** see `git_merge_file_flag_t` above */
git_merge_file_flag_t file_flags;
} git_merge_options;
#define GIT_MERGE_OPTIONS_VERSION 1
@ -497,10 +527,6 @@ GIT_EXTERN(int) git_merge_trees(
* or checked out. If the index is to be converted to a tree, the caller
* should resolve any conflicts that arose as part of the merge.
*
* The merge performed uses the first common ancestor, unlike the
* `git-merge-recursive` strategy, which may produce an artificial common
* ancestor tree when there are multiple ancestors.
*
* The returned index must be freed explicitly with `git_index_free`.
*
* @param out pointer to store the index result in
@ -523,10 +549,6 @@ GIT_EXTERN(int) git_merge_commits(
* to the index. Callers should inspect the repository's index after this
* completes, resolve any conflicts and prepare a commit.
*
* The merge performed uses the first common ancestor, unlike the
* `git-merge-recursive` strategy, which may produce an artificial common
* ancestor tree when there are multiple ancestors.
*
* For compatibility with git, the repository is put into a merging
* state. Once the commit is done (or if the uses wishes to abort),
* you should clear this state by calling

View File

@ -38,19 +38,33 @@ typedef struct {
*/
int quiet;
/**
* Used by `git_rebase_init`, this will begin an in-memory rebase,
* which will allow callers to step through the rebase operations and
* commit the rebased changes, but will not rewind HEAD or update the
* repository to be in a rebasing state. This will not interfere with
* the working directory (if there is one).
*/
int inmemory;
/**
* Used by `git_rebase_finish`, this is the name of the notes reference
* used to rewrite notes for rebased commits when finishing the rebase;
* if NULL, the contents of the coniguration option `notes.rewriteRef`
* if NULL, the contents of the configuration option `notes.rewriteRef`
* is examined, unless the configuration option `notes.rewrite.rebase`
* is set to false. If `notes.rewriteRef` is also NULL, notes will
* not be rewritten.
*/
const char *rewrite_notes_ref;
/**
* Options to control how trees are merged during `git_rebase_next`.
*/
git_merge_options merge_options;
/**
* Options to control how files are written during `git_rebase_init`,
* `git_checkout_next` and `git_checkout_abort`. Note that a minimum
* `git_rebase_next` and `git_rebase_abort`. Note that a minimum
* strategy of `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`,
* and a minimum strategy of `GIT_CHECKOUT_FORCE` is defaulted in
* `abort` to match git semantics.
@ -101,7 +115,8 @@ typedef enum {
#define GIT_REBASE_OPTIONS_VERSION 1
#define GIT_REBASE_OPTIONS_INIT \
{GIT_REBASE_OPTIONS_VERSION, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT}
{ GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \
GIT_CHECKOUT_OPTIONS_INIT}
/** Indicates that a rebase operation is not (yet) in progress. */
#define GIT_REBASE_NO_OPERATION SIZE_MAX
@ -226,6 +241,21 @@ GIT_EXTERN(int) git_rebase_next(
git_rebase_operation **operation,
git_rebase *rebase);
/**
* Gets the index produced by the last operation, which is the result
* of `git_rebase_next` and which will be committed by the next
* invocation of `git_rebase_commit`. This is useful for resolving
* conflicts in an in-memory rebase before committing them. You must
* call `git_index_free` when you are finished with this.
*
* This is only applicable for in-memory rebases; for rebases within
* a working directory, the changes were applied to the repository's
* index.
*/
GIT_EXTERN(int) git_rebase_inmemory_index(
git_index **index,
git_rebase *rebase);
/**
* Commits the current patch. You must have resolved any conflicts that
* were introduced during the patch application from the `git_rebase_next`

View File

@ -241,9 +241,10 @@ 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 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);
GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers);
/**
* Get the remote repository's reference advertisement list
@ -546,6 +547,11 @@ typedef struct {
* The default is to auto-follow tags.
*/
git_remote_autotag_option_t download_tags;
/**
* Extra headers for this fetch operation
*/
git_strarray custom_headers;
} git_fetch_options;
#define GIT_FETCH_OPTIONS_VERSION 1
@ -585,6 +591,11 @@ typedef struct {
* Callbacks to use for this push operation
*/
git_remote_callbacks callbacks;
/**
* Extra headers for this push operation
*/
git_strarray custom_headers;
} git_push_options;
#define GIT_PUSH_OPTIONS_VERSION 1

View File

@ -675,7 +675,9 @@ typedef enum {
GIT_REPOSITORY_STATE_NONE,
GIT_REPOSITORY_STATE_MERGE,
GIT_REPOSITORY_STATE_REVERT,
GIT_REPOSITORY_STATE_REVERT_SEQUENCE,
GIT_REPOSITORY_STATE_CHERRYPICK,
GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE,
GIT_REPOSITORY_STATE_BISECT,
GIT_REPOSITORY_STATE_REBASE,
GIT_REPOSITORY_STATE_REBASE_INTERACTIVE,

View File

@ -68,7 +68,7 @@ GIT_EXTERN(int) git_stash_save(
git_repository *repo,
const git_signature *stasher,
const char *message,
unsigned int flags);
uint32_t flags);
/** Stash application flags. */
typedef enum {
@ -150,7 +150,7 @@ typedef struct git_stash_apply_options {
* `GIT_STASH_APPLY_OPTIONS_INIT` here.
* @return Zero on success; -1 on failure.
*/
int git_stash_apply_init_options(
GIT_EXTERN(int) git_stash_apply_init_options(
git_stash_apply_options *opts, unsigned int version);
/**

View File

@ -107,6 +107,17 @@ typedef enum {
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED | \
GIT_SUBMODULE_STATUS_WD_UNTRACKED)) != 0)
/**
* Function pointer to receive each submodule
*
* @param sm git_submodule currently being visited
* @param name name of the submodule
* @param payload value you passed to the foreach function as payload
* @return 0 on success or error code
*/
typedef int (*git_submodule_cb)(
git_submodule *sm, const char *name, void *payload);
/**
* Submodule update options structure
*
@ -239,7 +250,7 @@ GIT_EXTERN(void) git_submodule_free(git_submodule *submodule);
*/
GIT_EXTERN(int) git_submodule_foreach(
git_repository *repo,
int (*callback)(git_submodule *sm, const char *name, void *payload),
git_submodule_cb callback,
void *payload);
/**

View File

@ -67,6 +67,20 @@ struct git_config_backend {
int (*iterator)(git_config_iterator **, struct git_config_backend *);
/** Produce a read-only version of this backend */
int (*snapshot)(struct git_config_backend **, struct git_config_backend *);
/**
* Lock this backend.
*
* Prevent any writes to the data store backing this
* backend. Any updates must not be visible to any other
* readers.
*/
int (*lock)(struct git_config_backend *);
/**
* Unlock the data store backing this backend. If success is
* true, the changes should be committed, otherwise rolled
* back.
*/
int (*unlock)(struct git_config_backend *, int success);
void (*free)(struct git_config_backend *);
};
#define GIT_CONFIG_BACKEND_VERSION 1

View File

@ -127,17 +127,6 @@ GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *sr
*/
GIT_EXTERN(uint32_t) git_filter_source_flags(const git_filter_source *src);
/*
* struct git_filter
*
* The filter lifecycle:
* - initialize - first use of filter
* - shutdown - filter removed/unregistered from system
* - check - considering filter for file
* - apply - apply filter to file contents
* - cleanup - done with file
*/
/**
* Initialize callback on filter
*
@ -233,28 +222,51 @@ typedef void (*git_filter_cleanup_fn)(
* To associate extra data with a filter, allocate extra data and put the
* `git_filter` struct at the start of your data buffer, then cast the
* `self` pointer to your larger structure when your callback is invoked.
*
* `version` should be set to GIT_FILTER_VERSION
*
* `attributes` is a whitespace-separated list of attribute names to check
* for this filter (e.g. "eol crlf text"). If the attribute name is bare,
* it will be simply loaded and passed to the `check` callback. If it has
* a value (i.e. "name=value"), the attribute must match that value for
* the filter to be applied.
*
* The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks
* are all documented above with the respective function pointer typedefs.
*/
struct git_filter {
/** The `version` field should be set to `GIT_FILTER_VERSION`. */
unsigned int version;
/**
* A whitespace-separated list of attribute names to check for this
* filter (e.g. "eol crlf text"). If the attribute name is bare, it
* will be simply loaded and passed to the `check` callback. If it
* has a value (i.e. "name=value"), the attribute must match that
* value for the filter to be applied. The value may be a wildcard
* (eg, "name=*"), in which case the filter will be invoked for any
* value for the given attribute name. See the attribute parameter
* of the `check` callback for the attribute value that was specified.
*/
const char *attributes;
/** Called when the filter is first used for any file. */
git_filter_init_fn initialize;
/** Called when the filter is removed or unregistered from the system. */
git_filter_shutdown_fn shutdown;
/**
* Called to determine whether the filter should be invoked for a
* given file. If this function returns `GIT_PASSTHROUGH` then the
* `apply` function will not be invoked and the contents will be passed
* through unmodified.
*/
git_filter_check_fn check;
/**
* Called to actually apply the filter to file contents. If this
* function returns `GIT_PASSTHROUGH` then the contents will be passed
* through unmodified.
*/
git_filter_apply_fn apply;
/**
* Called to apply the filter in a streaming manner. If this is not
* specified then the system will call `apply` with the whole buffer.
*/
git_filter_stream_fn stream;
/** Called when the system is done filtering for a file. */
git_filter_cleanup_fn cleanup;
};

View File

@ -25,7 +25,7 @@ typedef struct git_index_name_entry {
/** Representation of a resolve undo entry in the index. */
typedef struct git_index_reuc_entry {
unsigned int mode[3];
uint32_t mode[3];
git_oid oid[3];
char *path;
} git_index_reuc_entry;

View File

@ -83,6 +83,10 @@ struct git_odb_backend {
git_odb_writepack **, git_odb_backend *, git_odb *odb,
git_transfer_progress_cb progress_cb, void *progress_payload);
/**
* Frees any resources held by the odb (including the `git_odb_backend`
* itself). An odb backend implementation must provide this function.
*/
void (* free)(git_odb_backend *);
};

View File

@ -103,8 +103,9 @@ struct git_refdb_backend {
const git_signature *who, const char *message);
/**
* Deletes the given reference from the refdb. A refdb implementation
* must provide this function.
* Deletes the given reference (and if necessary its reflog)
* from the refdb. A refdb implementation must provide this
* function.
*/
int (*del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target);
@ -129,8 +130,8 @@ struct git_refdb_backend {
int (*ensure_log)(git_refdb_backend *backend, const char *refname);
/**
* Frees any resources held by the refdb. A refdb implementation may
* provide this function; if it is not provided, nothing will be done.
* Frees any resources held by the refdb (including the `git_refdb_backend`
* itself). A refdb backend implementation must provide this function.
*/
void (*free)(git_refdb_backend *backend);

View File

@ -39,6 +39,19 @@ typedef struct git_stream {
void (*free)(struct git_stream *);
} git_stream;
typedef int (*git_stream_cb)(git_stream **out, const char *host, const char *port);
/**
* Register a TLS stream constructor for the library to use
*
* If a constructor is already set, it will be overwritten. Pass
* `NULL` in order to deregister the current constructor.
*
* @param ctor the constructor to use
* @return 0 or an error code
*/
GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor);
GIT_END_DECL
#endif

View File

@ -10,6 +10,7 @@
#include "git2/net.h"
#include "git2/types.h"
#include "git2/strarray.h"
/**
* @file git2/sys/transport.h
@ -40,6 +41,11 @@ struct git_transport {
git_transport_certificate_check_cb certificate_check_cb,
void *payload);
/* Set custom headers for HTTP requests */
int (*set_custom_headers)(
git_transport *transport,
const git_strarray *custom_headers);
/* Connect the transport to the remote repository, using the given
* direction. */
int (*connect)(
@ -211,6 +217,28 @@ GIT_EXTERN(int) git_transport_smart(
git_remote *owner,
/* (git_smart_subtransport_definition *) */ void *payload);
/**
* Call the certificate check for this transport.
*
* @param transport a smart transport
* @param cert the certificate to pass to the caller
* @param valid whether we believe the certificate is valid
* @param hostname the hostname we connected to
* @return the return value of the callback
*/
GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname);
/**
* Call the credentials callback for this transport
*
* @param out the pointer where the creds are to be stored
* @param transport a smart transport
* @param user the user we saw on the url (if any)
* @param methods available methods for authentication
* @return the return value of the callback
*/
GIT_EXTERN(int) git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods);
/*
*** End of base transport interface ***
*** Begin interface for subtransports for the smart transport ***

View File

@ -37,39 +37,32 @@ typedef enum {
* Hostkey information taken from libssh2
*/
typedef struct {
git_cert parent;
/**
* Type of certificate. Here to share the header with
* `git_cert`.
* A hostkey type from libssh2, either
* `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1`
*/
git_cert_t cert_type;
/**
* A hostkey type from libssh2, either
* `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1`
*/
git_cert_ssh_t type;
/**
* Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will
* have the MD5 hash of the hostkey.
*/
/**
* Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will
* have the MD5 hash of the hostkey.
*/
unsigned char hash_md5[16];
/**
* Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will
* have the SHA-1 hash of the hostkey.
*/
unsigned char hash_sha1[20];
/**
* Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will
* have the SHA-1 hash of the hostkey.
*/
unsigned char hash_sha1[20];
} git_cert_hostkey;
/**
* X.509 certificate information
*/
typedef struct {
/**
* Type of certificate. Here to share the header with
* `git_cert`.
*/
git_cert_t cert_type;
git_cert parent;
/**
* Pointer to the X.509 certificate data
*/
@ -314,6 +307,17 @@ GIT_EXTERN(int) git_cred_ssh_key_memory_new(
const char *privatekey,
const char *passphrase);
/**
* Free a credential.
*
* This is only necessary if you own the object; that is, if you are a
* transport.
*
* @param cred the object to free
*/
GIT_EXTERN(void) git_cred_free(git_cred *cred);
/**
* Signature of a function which acquires a credential object.
*

View File

@ -7,12 +7,12 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
#define LIBGIT2_VERSION "0.23.1"
#define LIBGIT2_VERSION "0.24.0"
#define LIBGIT2_VER_MAJOR 0
#define LIBGIT2_VER_MINOR 23
#define LIBGIT2_VER_REVISION 1
#define LIBGIT2_VER_MINOR 24
#define LIBGIT2_VER_REVISION 0
#define LIBGIT2_VER_PATCH 0
#define LIBGIT2_SOVERSION 23
#define LIBGIT2_SOVERSION 24
#endif

View File

@ -1,11 +1,12 @@
libdir=@CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@
includedir=@CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@
prefix=@PKGCONFIG_PREFIX@
libdir=@PKGCONFIG_LIBDIR@
includedir=@PKGCONFIG_INCLUDEDIR@
Name: libgit2
Description: The git library, take 2
Version: @LIBGIT2_VERSION_STRING@
Libs: -L${libdir} -lgit2
Libs: -L"${libdir}" -lgit2
Libs.private: @LIBGIT2_PC_LIBS@
Requires.private: @LIBGIT2_PC_REQUIRES@

View File

@ -25,7 +25,7 @@ git daemon --listen=localhost --export-all --enable=receive-pack --base-path="$H
export GITTEST_REMOTE_URL="git://localhost/test.git"
# Run the test suite
ctest -V . || exit $?
ctest -V -R libgit2_clar || exit $?
# Now that we've tested the raw git protocol, let's set up ssh to we
# can do the push tests over it
@ -56,3 +56,7 @@ if [ -e ./libgit2_clar ]; then
./libgit2_clar -sonline::clone::cred_callback || exit $?
fi
fi
export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
export GITTEST_REMOTE_USER="libgit2test"
ctest -V -R libgit2_clar-cred_callback

View File

@ -33,6 +33,8 @@ if [ ! -d "$TOOL_BASE" ]; then
ln -s "$TOOL_DIR" "$TOOL_BASE"/cov-analysis
fi
cp script/user_nodefs.h "$TOOL_BASE"/cov-analysis/config/user_nodefs.h
COV_BUILD="$TOOL_BASE/cov-analysis/bin/cov-build"
# Configure and build
@ -48,10 +50,9 @@ COVERITY_UNSUPPORTED=1 \
tar czf libgit2.tgz cov-int
SHA=$(git rev-parse --short HEAD)
curl \
--form project=libgit2 \
--form token="$COVERITY_TOKEN" \
--form email=bs@github.com \
--form file=@libgit2.tgz \
--form version="$SHA" \
--form description="Travis build" \
http://scan5.coverity.com/cgi-bin/upload.py
https://scan.coverity.com/builds?project=libgit2

View File

@ -2,4 +2,5 @@
set -x
brew install libssh2 cmake
brew update
brew install libssh2

34
script/user_nodefs.h Normal file
View File

@ -0,0 +1,34 @@
/*
* 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.
*/
#nodef GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { __coverity_panic__(); }
#nodef GITERR_CHECK_ALLOC_BUF(buf) if (buf == NULL || git_buf_oom(buf)) { __coverity_panic__(); }
#nodef GITERR_CHECK_ALLOC_ADD(out, one, two) \
if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { __coverity_panic__(); }
#nodef GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \
if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { __coverity_panic__(); }
#nodef GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \
if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { __coverity_panic__(); }
#nodef GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { __coverity_panic__(); }
#nodef GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) { __coverity_panic__(); }
#nodef LOOKS_LIKE_DRIVE_PREFIX(S) (strlen(S) >= 2 && git__isalpha((S)[0]) && (S)[1] == ':')
#nodef git_vector_foreach(v, iter, elem) \
for ((iter) = 0; (v)->contents != NULL && (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )
#nodef git_vector_rforeach(v, iter, elem) \
for ((iter) = (v)->length - 1; (v)->contents != NULL && (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- )

View File

@ -7,12 +7,16 @@
#include "common.h"
#include "annotated_commit.h"
#include "refs.h"
#include "cache.h"
#include "git2/commit.h"
#include "git2/refs.h"
#include "git2/repository.h"
#include "git2/annotated_commit.h"
#include "git2/revparse.h"
#include "git2/tree.h"
#include "git2/index.h"
static int annotated_commit_init(
git_annotated_commit **out,
@ -22,14 +26,17 @@ static int annotated_commit_init(
const char *remote_url)
{
git_annotated_commit *annotated_commit;
git_commit *commit = NULL;
int error = 0;
assert(out && id);
*out = NULL;
annotated_commit = git__calloc(1, sizeof(git_annotated_commit));
GITERR_CHECK_ALLOC(annotated_commit);
if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
(error = git_annotated_commit_from_commit(&annotated_commit,
commit)) < 0)
goto done;
if (ref_name) {
annotated_commit->ref_name = git__strdup(ref_name);
@ -41,15 +48,10 @@ static int annotated_commit_init(
GITERR_CHECK_ALLOC(annotated_commit->remote_url);
}
git_oid_fmt(annotated_commit->id_str, id);
annotated_commit->id_str[GIT_OID_HEXSZ] = '\0';
if ((error = git_commit_lookup(&annotated_commit->commit, repo, id)) < 0) {
git_annotated_commit_free(annotated_commit);
return error;
}
*out = annotated_commit;
done:
git_commit_free(commit);
return error;
}
@ -75,6 +77,51 @@ int git_annotated_commit_from_ref(
return error;
}
int git_annotated_commit_from_head(
git_annotated_commit **out,
git_repository *repo)
{
git_reference *head;
int error;
assert(out && repo);
*out = NULL;
if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
return -1;
error = git_annotated_commit_from_ref(out, repo, head);
git_reference_free(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,
@ -136,14 +183,20 @@ void git_annotated_commit_free(git_annotated_commit *annotated_commit)
if (annotated_commit == NULL)
return;
if (annotated_commit->commit != NULL)
git_commit_free(annotated_commit->commit);
if (annotated_commit->ref_name != NULL)
git__free(annotated_commit->ref_name);
if (annotated_commit->remote_url != NULL)
git__free(annotated_commit->remote_url);
switch (annotated_commit->type) {
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);
break;
case GIT_ANNOTATED_COMMIT_VIRTUAL:
git_index_free(annotated_commit->index);
git_array_clear(annotated_commit->parents);
break;
default:
abort();
}
git__free(annotated_commit);
}

View File

@ -7,11 +7,31 @@
#ifndef INCLUDE_annotated_commit_h__
#define INCLUDE_annotated_commit_h__
#include "oidarray.h"
#include "git2/oid.h"
/** Internal structure for merge inputs */
typedef enum {
GIT_ANNOTATED_COMMIT_REAL = 1,
GIT_ANNOTATED_COMMIT_VIRTUAL = 2,
} git_annotated_commit_t;
/**
* Internal structure for merge inputs. An annotated commit is generally
* "real" and backed by an actual commit in the repository, but merge will
* internally create "virtual" commits that are in-memory intermediate
* commits backed by an index.
*/
struct git_annotated_commit {
git_annotated_commit_t type;
/* real commit */
git_commit *commit;
git_tree *tree;
/* virtual commit structure */
git_index *index;
git_array_oid_t parents;
char *ref_name;
char *remote_url;
@ -19,4 +39,9 @@ struct git_annotated_commit {
char id_str[GIT_OID_HEXSZ+1];
};
extern int git_annotated_commit_from_head(git_annotated_commit **out,
git_repository *repo);
extern int git_annotated_commit_from_commit(git_annotated_commit **out,
git_commit *commit);
#endif

View File

@ -35,11 +35,7 @@ int git_attr_file__new(
return -1;
}
if (git_pool_init(&attrs->pool, 1, 0) < 0) {
attr_file_free(attrs);
return -1;
}
git_pool_init(&attrs->pool, 1);
GIT_REFCOUNT_INC(attrs);
attrs->entry = entry;
attrs->source = source;
@ -127,7 +123,7 @@ int git_attr_file__load(
break;
}
case GIT_ATTR_FILE__FROM_FILE: {
int fd;
int fd = -1;
/* For open or read errors, pretend that we got ENOTFOUND. */
/* TODO: issue warning when warning API is available */
@ -137,7 +133,8 @@ int git_attr_file__load(
(fd = git_futils_open_ro(entry->fullpath)) < 0 ||
(error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0)
nonexistent = true;
else
if (fd >= 0)
p_close(fd);
break;

View File

@ -388,10 +388,11 @@ int git_attr_cache__do_init(git_repository *repo)
* hashtable for attribute macros, and string pool
*/
if ((ret = git_strmap_alloc(&cache->files)) < 0 ||
(ret = git_strmap_alloc(&cache->macros)) < 0 ||
(ret = git_pool_init(&cache->pool, 1, 0)) < 0)
(ret = git_strmap_alloc(&cache->macros)) < 0)
goto cancel;
git_pool_init(&cache->pool, 1);
cache = git__compare_and_swap(&repo->attrcache, NULL, cache);
if (cache)
goto cancel; /* raced with another thread, free this but no error */

View File

@ -23,8 +23,8 @@ static int hunk_byfinalline_search_cmp(const void *key, const void *entry)
git_blame_hunk *hunk = (git_blame_hunk*)entry;
size_t lineno = *(size_t*)key;
size_t lines_in_hunk = (size_t)hunk->lines_in_hunk;
size_t final_start_line_number = (size_t)hunk->final_start_line_number;
size_t lines_in_hunk = hunk->lines_in_hunk;
size_t final_start_line_number = hunk->final_start_line_number;
if (lineno < final_start_line_number)
return -1;
@ -44,7 +44,7 @@ static int hunk_cmp(const void *_a, const void *_b)
static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line)
{
return line >= (size_t)(hunk->final_start_line_number + hunk->lines_in_hunk - 1);
return line >= (hunk->final_start_line_number + hunk->lines_in_hunk - 1);
}
static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
@ -53,9 +53,9 @@ static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
}
static git_blame_hunk* new_hunk(
uint16_t start,
uint16_t lines,
uint16_t orig_start,
size_t start,
size_t lines,
size_t orig_start,
const char *path)
{
git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk));
@ -166,9 +166,9 @@ const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t inde
return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
}
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno)
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno)
{
size_t i, new_lineno = (size_t)lineno;
size_t i, new_lineno = lineno;
assert(blame);
if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) {
@ -223,8 +223,8 @@ static git_blame_hunk *split_hunk_in_vector(
}
new_line_count = hunk->lines_in_hunk - rel_line;
nh = new_hunk((uint16_t)(hunk->final_start_line_number+rel_line), (uint16_t)new_line_count,
(uint16_t)(hunk->orig_start_line_number+rel_line), hunk->orig_path);
nh = new_hunk(hunk->final_start_line_number + rel_line, new_line_count,
hunk->orig_start_line_number + rel_line, hunk->orig_path);
if (!nh)
return NULL;
@ -233,7 +233,7 @@ static git_blame_hunk *split_hunk_in_vector(
git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
/* Adjust hunk that was split */
hunk->lines_in_hunk -= (uint16_t)new_line_count;
hunk->lines_in_hunk -= new_line_count;
git_vector_insert_sorted(vec, nh, NULL);
{
git_blame_hunk *ret = return_new ? nh : hunk;
@ -331,7 +331,7 @@ static int blame_internal(git_blame *blame)
blame->ent = ent;
git_blame__like_git(blame, blame->options.flags);
error = git_blame__like_git(blame, blame->options.flags);
cleanup:
for (ent = blame->ent; ent; ) {
@ -442,7 +442,7 @@ static int buffer_line_cb(
} else {
/* Create a new buffer-blame hunk with this line */
shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
blame->current_hunk = new_hunk((uint16_t)blame->current_diff_line, 1, 0, blame->path);
blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path);
GITERR_CHECK_ALLOC(blame->current_hunk);
git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);

View File

@ -31,10 +31,10 @@ typedef struct git_blame__entry {
/* the first line of this group in the final image;
* internally all line numbers are 0 based.
*/
int lno;
size_t lno;
/* how many lines this group has */
int num_lines;
size_t num_lines;
/* the commit that introduced this group into the final image */
git_blame__origin *suspect;
@ -51,7 +51,7 @@ typedef struct git_blame__entry {
/* the line number of the first line of this group in the
* suspect's file; internally all line numbers are 0 based.
*/
int s_lno;
size_t s_lno;
/* how significant this entry is -- cached to avoid
* scanning the lines over and over.

View File

@ -9,6 +9,7 @@
#include "commit.h"
#include "blob.h"
#include "xdiff/xinclude.h"
#include "diff_xdiff.h"
/*
* Origin is refcounted and usually we keep the blob contents to be
@ -92,18 +93,25 @@ static bool same_suspect(git_blame__origin *a, git_blame__origin *b)
}
/* find the line number of the last line the target is suspected for */
static int find_last_in_target(git_blame *blame, git_blame__origin *target)
static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target)
{
git_blame__entry *e;
int last_in_target = -1;
size_t last_in_target = 0;
bool found = false;
*out = 0;
for (e=blame->ent; e; e=e->next) {
if (e->guilty || !same_suspect(e->suspect, target))
continue;
if (last_in_target < e->s_lno + e->num_lines)
if (last_in_target < e->s_lno + e->num_lines) {
found = true;
last_in_target = e->s_lno + e->num_lines;
}
}
return last_in_target;
*out = last_in_target;
return found;
}
/*
@ -121,9 +129,9 @@ static int find_last_in_target(git_blame *blame, git_blame__origin *target)
* to be blamed for the parent, and after that portion.
*/
static void split_overlap(git_blame__entry *split, git_blame__entry *e,
int tlno, int plno, int same, git_blame__origin *parent)
size_t tlno, size_t plno, size_t same, git_blame__origin *parent)
{
int chunk_end_lno;
size_t chunk_end_lno;
if (e->s_lno < tlno) {
/* there is a pre-chunk part not blamed on the parent */
@ -264,9 +272,9 @@ static void decref_split(git_blame__entry *split)
static void blame_overlap(
git_blame *blame,
git_blame__entry *e,
int tlno,
int plno,
int same,
size_t tlno,
size_t plno,
size_t same,
git_blame__origin *parent)
{
git_blame__entry split[3] = {{0}};
@ -284,9 +292,9 @@ static void blame_overlap(
*/
static void blame_chunk(
git_blame *blame,
int tlno,
int plno,
int same,
size_t tlno,
size_t plno,
size_t same,
git_blame__origin *target,
git_blame__origin *parent)
{
@ -313,7 +321,7 @@ static int my_emit(
blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
d->plno = start_a + count_a;
d->tlno = start_b + count_b;
return 0;
}
@ -351,6 +359,13 @@ static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data)
ecb.priv = cb_data;
trim_common_tail(&file_a, &file_b, 0);
if (file_a.size > GIT_XDIFF_MAX_SIZE ||
file_b.size > GIT_XDIFF_MAX_SIZE) {
giterr_set(GITERR_INVALID, "file too large to blame");
return -1;
}
return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb);
}
@ -368,18 +383,19 @@ static int pass_blame_to_parent(
git_blame__origin *target,
git_blame__origin *parent)
{
int last_in_target;
size_t last_in_target;
mmfile_t file_p, file_o;
blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
last_in_target = find_last_in_target(blame, target);
if (last_in_target < 0)
if (!find_last_in_target(&last_in_target, blame, target))
return 1; /* nothing remains for this target */
fill_origin_blob(parent, &file_p);
fill_origin_blob(target, &file_o);
diff_hunks(file_p, file_o, &d);
if (diff_hunks(file_p, file_o, &d) < 0)
return -1;
/* The reset (i.e. anything after tlno) are the same as the parent */
blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent);
@ -477,12 +493,13 @@ static void pass_whole_blame(git_blame *blame,
}
}
static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
{
git_commit *commit = origin->commit;
int i, num_parents;
git_blame__origin *sg_buf[16];
git_blame__origin *porigin, **sg_origin = sg_buf;
int ret, error = 0;
num_parents = git_commit_parentcount(commit);
if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
@ -540,8 +557,13 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt
origin_incref(porigin);
origin->previous = porigin;
}
if (pass_blame_to_parent(blame, origin, porigin))
if ((ret = pass_blame_to_parent(blame, origin, porigin)) != 0) {
if (ret < 0)
error = -1;
goto finish;
}
}
/* TODO: optionally find moves in parents' files */
@ -554,7 +576,7 @@ finish:
origin_decref(sg_origin[i]);
if (sg_origin != sg_buf)
git__free(sg_origin);
return;
return error;
}
/*
@ -583,7 +605,7 @@ static void coalesce(git_blame *blame)
}
}
void git_blame__like_git(git_blame *blame, uint32_t opt)
int git_blame__like_git(git_blame *blame, uint32_t opt)
{
while (true) {
git_blame__entry *ent;
@ -594,11 +616,13 @@ void git_blame__like_git(git_blame *blame, uint32_t opt)
if (!ent->guilty)
suspect = ent->suspect;
if (!suspect)
return; /* all done */
return 0; /* all done */
/* We'll use this suspect later in the loop, so hold on to it for now. */
origin_incref(suspect);
pass_blame(blame, suspect, opt);
if (pass_blame(blame, suspect, opt) < 0)
return -1;
/* Take responsibility for the remaining entries */
for (ent = blame->ent; ent; ent = ent->next) {
@ -613,6 +637,8 @@ void git_blame__like_git(git_blame *blame, uint32_t opt)
}
coalesce(blame);
return 0;
}
void git_blame__free_entry(git_blame__entry *ent)

View File

@ -15,6 +15,6 @@ int git_blame__get_origin(
git_commit *commit,
const char *path);
void git_blame__free_entry(git_blame__entry *ent);
void git_blame__like_git(git_blame *sb, uint32_t flags);
int git_blame__like_git(git_blame *sb, uint32_t flags);
#endif

View File

@ -155,18 +155,7 @@ int git_branch_delete(git_reference *branch)
git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0)
goto on_error;
if (git_reference_delete(branch) < 0)
goto on_error;
if ((error = git_reflog_delete(git_reference_owner(branch), git_reference_name(branch))) < 0) {
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
}
goto on_error;
}
error = 0;
error = git_reference_delete(branch);
on_error:
git_buf_free(&config_section);

View File

@ -18,6 +18,7 @@
#include "git2/submodule.h"
#include "git2/sys/index.h"
#include "git2/sys/filter.h"
#include "git2/merge.h"
#include "refs.h"
#include "repository.h"
@ -27,7 +28,7 @@
#include "diff.h"
#include "pathspec.h"
#include "buf_text.h"
#include "merge_file.h"
#include "diff_xdiff.h"
#include "path.h"
#include "attr.h"
#include "pool.h"
@ -199,8 +200,7 @@ static bool checkout_is_workdir_modified(
* out.)
*/
if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
if (wditem->mtime.seconds == ie->mtime.seconds &&
wditem->mtime.nanoseconds == ie->mtime.nanoseconds &&
if (git_index_time_eq(&wditem->mtime, &ie->mtime) &&
wditem->file_size == ie->file_size)
return !is_workdir_base_or_new(&ie->id, baseitem, newitem);
}
@ -243,6 +243,12 @@ static int checkout_action_common(
if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL)
*action |= CHECKOUT_ACTION__REMOVE;
/* if the file is on disk and doesn't match our mode, force update */
if (wd &&
GIT_PERMS_IS_EXEC(wd->mode) !=
GIT_PERMS_IS_EXEC(delta->new_file.mode))
*action |= CHECKOUT_ACTION__REMOVE;
notify = GIT_CHECKOUT_NOTIFY_UPDATED;
}
@ -1220,7 +1226,7 @@ static int checkout_verify_paths(
int action,
git_diff_delta *delta)
{
unsigned int flags = GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT;
unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS;
if (action & CHECKOUT_ACTION__REMOVE) {
if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
@ -1248,11 +1254,13 @@ static int checkout_get_actions(
int error = 0, act;
const git_index_entry *wditem;
git_vector pathspec = GIT_VECTOR_INIT, *deltas;
git_pool pathpool = GIT_POOL_INIT_STRINGPOOL;
git_pool pathpool;
git_diff_delta *delta;
size_t i, *counts = NULL;
uint32_t *actions = NULL;
git_pool_init(&pathpool, 1);
if (data->opts.paths.count > 0 &&
git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0)
return -1;
@ -1360,7 +1368,7 @@ static int checkout_mkdir(
mkdir_opts.dir_map = data->mkdir_map;
mkdir_opts.pool = &data->pool;
error = git_futils_mkdir_ext(
error = git_futils_mkdir_relative(
path, base, mode, flags, &mkdir_opts);
data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls;
@ -1479,8 +1487,10 @@ static int blob_content_to_file(
if (!data->opts.disable_filters &&
(error = git_filter_list__load_ext(
&fl, data->repo, blob, hint_path,
GIT_FILTER_TO_WORKTREE, &filter_opts)))
GIT_FILTER_TO_WORKTREE, &filter_opts))) {
p_close(fd);
return error;
}
/* setup the writer */
memset(&writer, 0, sizeof(struct checkout_stream));
@ -1500,15 +1510,6 @@ static int blob_content_to_file(
if (error < 0)
return error;
if (GIT_PERMS_IS_EXEC(mode)) {
data->perfdata.chmod_calls++;
if ((error = p_chmod(path, mode)) < 0) {
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
return error;
}
}
if (st) {
data->perfdata.stat_calls++;
@ -2441,10 +2442,11 @@ static int checkout_data_init(
git_config_entry_free(conflict_style);
}
git_pool_init(&data->pool, 1);
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_pool_init(&data->pool, 1, 0)) < 0 ||
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
(error = git_path_to_dir(&data->path)) < 0 ||
(error = git_strmap_alloc(&data->mkdir_map)) < 0)
@ -2471,11 +2473,12 @@ int git_checkout_iterator(
{
int error = 0;
git_iterator *baseline = NULL, *workdir = NULL;
git_iterator_options baseline_opts = GIT_ITERATOR_OPTIONS_INIT,
workdir_opts = GIT_ITERATOR_OPTIONS_INIT;
checkout_data data = {0};
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
uint32_t *actions = NULL;
size_t *counts = NULL;
git_iterator_flag_t iterflags = 0;
/* initialize structures and options */
error = checkout_data_init(&data, target, opts);
@ -2499,25 +2502,31 @@ int git_checkout_iterator(
/* set up iterators */
iterflags = git_iterator_ignore_case(target) ?
workdir_opts.flags = git_iterator_ignore_case(target) ?
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
workdir_opts.flags |= GIT_ITERATOR_DONT_AUTOEXPAND;
workdir_opts.start = data.pfx;
workdir_opts.end = data.pfx;
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_workdir_ext(
&workdir, data.repo, data.opts.target_directory, index, NULL,
iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
data.pfx, data.pfx)) < 0)
&workdir_opts)) < 0)
goto cleanup;
baseline_opts.flags = git_iterator_ignore_case(target) ?
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
baseline_opts.start = data.pfx;
baseline_opts.end = data.pfx;
if (data.opts.baseline_index) {
if ((error = git_iterator_for_index(
&baseline, data.opts.baseline_index,
iterflags, data.pfx, data.pfx)) < 0)
&baseline, git_index_owner(data.opts.baseline_index),
data.opts.baseline_index, &baseline_opts)) < 0)
goto cleanup;
} else {
if ((error = git_iterator_for_tree(
&baseline, data.opts.baseline,
iterflags, data.pfx, data.pfx)) < 0)
&baseline, data.opts.baseline, &baseline_opts)) < 0)
goto cleanup;
}
@ -2625,7 +2634,7 @@ int git_checkout_index(
return error;
GIT_REFCOUNT_INC(index);
if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL)))
if (!(error = git_iterator_for_index(&index_i, repo, index, NULL)))
error = git_checkout_iterator(index_i, index, opts);
if (owned)
@ -2646,6 +2655,7 @@ int git_checkout_tree(
git_index *index;
git_tree *tree = NULL;
git_iterator *tree_i = NULL;
git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
if (!treeish && !repo) {
giterr_set(GITERR_CHECKOUT,
@ -2681,7 +2691,12 @@ int git_checkout_tree(
if ((error = git_repository_index(&index, repo)) < 0)
return error;
if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL)))
if ((opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) {
iter_opts.pathlist.count = opts->paths.count;
iter_opts.pathlist.strings = opts->paths.strings;
}
if (!(error = git_iterator_for_tree(&tree_i, tree, &iter_opts)))
error = git_checkout_iterator(tree_i, index, opts);
git_iterator_free(tree_i);

View File

@ -440,14 +440,14 @@ int git_clone(
if (error != 0) {
git_error_state last_error = {0};
giterr_capture(&last_error, error);
giterr_state_capture(&last_error, error);
git_repository_free(repo);
repo = NULL;
(void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
giterr_restore(&last_error);
giterr_state_restore(&last_error);
}
*out = repo;

View File

@ -17,6 +17,7 @@
#include "signature.h"
#include "message.h"
#include "refs.h"
#include "object.h"
void git_commit__free(void *_commit)
{
@ -31,11 +32,12 @@ void git_commit__free(void *_commit)
git__free(commit->raw_message);
git__free(commit->message_encoding);
git__free(commit->summary);
git__free(commit->body);
git__free(commit);
}
int git_commit_create_from_callback(
static int git_commit__create_internal(
git_oid *id,
git_repository *repo,
const char *update_ref,
@ -45,7 +47,8 @@ int git_commit_create_from_callback(
const char *message,
const git_oid *tree,
git_commit_parent_callback parent_cb,
void *parent_payload)
void *parent_payload,
bool validate)
{
git_reference *ref = NULL;
int error = 0, matched_parent = 0;
@ -57,6 +60,9 @@ int git_commit_create_from_callback(
assert(id && repo && tree && parent_cb);
if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
return -1;
if (update_ref) {
error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
if (error < 0 && error != GIT_ENOTFOUND)
@ -70,6 +76,11 @@ int git_commit_create_from_callback(
git_oid__writebuf(&commit, "tree ", tree);
while ((parent = parent_cb(i, parent_payload)) != NULL) {
if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
error = -1;
goto on_error;
}
git_oid__writebuf(&commit, "parent ", parent);
if (i == 0 && current_id && git_oid_equal(current_id, parent))
matched_parent = 1;
@ -113,10 +124,26 @@ int git_commit_create_from_callback(
on_error:
git_buf_free(&commit);
giterr_set(GITERR_OBJECT, "Failed to create commit.");
return -1;
}
int git_commit_create_from_callback(
git_oid *id,
git_repository *repo,
const char *update_ref,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message,
const git_oid *tree,
git_commit_parent_callback parent_cb,
void *parent_payload)
{
return git_commit__create_internal(
id, repo, update_ref, author, committer, message_encoding, message,
tree, parent_cb, parent_payload, true);
}
typedef struct {
size_t total;
va_list args;
@ -152,10 +179,10 @@ int git_commit_create_v(
data.total = parent_count;
va_start(data.args, parent_count);
error = git_commit_create_from_callback(
error = git_commit__create_internal(
id, repo, update_ref, author, committer,
message_encoding, message, git_tree_id(tree),
commit_parent_from_varargs, &data);
commit_parent_from_varargs, &data, false);
va_end(data.args);
return error;
@ -186,10 +213,10 @@ int git_commit_create_from_ids(
{
commit_parent_oids data = { parent_count, parents };
return git_commit_create_from_callback(
return git_commit__create_internal(
id, repo, update_ref, author, committer,
message_encoding, message, tree,
commit_parent_from_ids, &data);
commit_parent_from_ids, &data, true);
}
typedef struct {
@ -226,10 +253,10 @@ int git_commit_create(
assert(tree && git_tree_owner(tree) == repo);
return git_commit_create_from_callback(
return git_commit__create_internal(
id, repo, update_ref, author, committer,
message_encoding, message, git_tree_id(tree),
commit_parent_from_array, &data);
commit_parent_from_array, &data, false);
}
static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
@ -289,9 +316,9 @@ int git_commit_amend(
}
}
error = git_commit_create_from_callback(
error = git_commit__create_internal(
id, repo, NULL, author, committer, message_encoding, message,
&tree_id, commit_parent_for_amend, (void *)commit_to_amend);
&tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
if (!error && update_ref) {
error = git_reference__update_for_commit(
@ -431,22 +458,37 @@ const char *git_commit_summary(git_commit *commit)
{
git_buf summary = GIT_BUF_INIT;
const char *msg, *space;
bool space_contains_newline = false;
assert(commit);
if (!commit->summary) {
for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
char next_character = msg[0];
/* stop processing at the end of the first paragraph */
if (next_character == '\n' && (!msg[1] || msg[1] == '\n'))
break;
else if (msg[0] == '\n')
git_buf_putc(&summary, ' ');
else if (git__isspace(msg[0]))
space = space ? space : msg;
else if (space) {
git_buf_put(&summary, space, (msg - space) + 1);
space = NULL;
} else
git_buf_putc(&summary, *msg);
/* record the beginning of contiguous whitespace runs */
else if (git__isspace(next_character)) {
if(space == NULL) {
space = msg;
space_contains_newline = false;
}
space_contains_newline |= next_character == '\n';
}
/* the next character is non-space */
else {
/* process any recorded whitespace */
if (space) {
if(space_contains_newline)
git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
else
git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */
space = NULL;
}
/* copy the next character */
git_buf_putc(&summary, next_character);
}
}
commit->summary = git_buf_detach(&summary);
@ -457,6 +499,33 @@ const char *git_commit_summary(git_commit *commit)
return commit->summary;
}
const char *git_commit_body(git_commit *commit)
{
const char *msg, *end;
assert(commit);
if (!commit->body) {
/* search for end of summary */
for (msg = git_commit_message(commit); *msg; ++msg)
if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
break;
/* trim leading and trailing whitespace */
for (; *msg; ++msg)
if (!git__isspace(*msg))
break;
for (end = msg + strlen(msg) - 1; msg <= end; --end)
if (!git__isspace(*end))
break;
if (*msg)
commit->body = git__strndup(msg, end - msg + 1);
}
return commit->body;
}
int git_commit_tree(git_tree **tree_out, const git_commit *commit)
{
assert(commit);
@ -521,17 +590,103 @@ int git_commit_nth_gen_ancestor(
int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
{
const char *buf = commit->raw_header;
const char *h, *eol;
const char *eol, *buf = commit->raw_header;
git_buf_sanitize(out);
while ((eol = strchr(buf, '\n'))) {
/* We can skip continuations here */
if (buf[0] == ' ') {
buf = eol + 1;
continue;
}
/* Skip until we find the field we're after */
if (git__prefixcmp(buf, field)) {
buf = eol + 1;
continue;
}
buf += strlen(field);
/* Check that we're not matching a prefix but the field itself */
if (buf[0] != ' ') {
buf = eol + 1;
continue;
}
buf++; /* skip the SP */
git_buf_put(out, buf, eol - buf);
if (git_buf_oom(out))
goto oom;
/* If the next line starts with SP, it's multi-line, we must continue */
while (eol[1] == ' ') {
git_buf_putc(out, '\n');
buf = eol + 2;
eol = strchr(buf, '\n');
if (!eol)
goto malformed;
git_buf_put(out, buf, eol - buf);
}
if (git_buf_oom(out))
goto oom;
return 0;
}
giterr_set(GITERR_OBJECT, "no such field '%s'", field);
return GIT_ENOTFOUND;
malformed:
giterr_set(GITERR_OBJECT, "malformed header");
return -1;
oom:
giterr_set_oom();
return -1;
}
int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field)
{
git_odb_object *obj;
git_odb *odb;
const char *buf;
const char *h, *eol;
int error;
git_buf_sanitize(signature);
git_buf_sanitize(signed_data);
if (!field)
field = "gpgsig";
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
return error;
if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
return error;
if (obj->cached.type != GIT_OBJ_COMMIT) {
giterr_set(GITERR_INVALID, "the requested type does not match the type in ODB");
error = GIT_ENOTFOUND;
goto cleanup;
}
buf = git_odb_object_data(obj);
while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') {
h++;
if (git__prefixcmp(h, field)) {
if (git__prefixcmp(buf, field)) {
if (git_buf_put(signed_data, buf, h - buf) < 0)
return -1;
buf = h;
continue;
}
h = buf;
h += strlen(field);
eol = strchr(h, '\n');
if (h[0] != ' ') {
@ -543,33 +698,44 @@ int git_commit_header_field(git_buf *out, const git_commit *commit, const char *
h++; /* skip the SP */
git_buf_put(out, h, eol - h);
if (git_buf_oom(out))
git_buf_put(signature, h, eol - h);
if (git_buf_oom(signature))
goto oom;
/* If the next line starts with SP, it's multi-line, we must continue */
while (eol[1] == ' ') {
git_buf_putc(out, '\n');
git_buf_putc(signature, '\n');
h = eol + 2;
eol = strchr(h, '\n');
if (!eol)
goto malformed;
git_buf_put(out, h, eol - h);
git_buf_put(signature, h, eol - h);
}
if (git_buf_oom(out))
if (git_buf_oom(signature))
goto oom;
return 0;
git_odb_object_free(obj);
return git_buf_puts(signed_data, eol+1);
}
return GIT_ENOTFOUND;
giterr_set(GITERR_OBJECT, "this commit is not signed");
error = GIT_ENOTFOUND;
goto cleanup;
malformed:
giterr_set(GITERR_OBJECT, "malformed header");
return -1;
error = -1;
goto cleanup;
oom:
giterr_set_oom();
return -1;
error = -1;
goto cleanup;
cleanup:
git_odb_object_free(obj);
git_buf_clear(signature);
git_buf_clear(signed_data);
return error;
}

View File

@ -28,6 +28,7 @@ struct git_commit {
char *raw_header;
char *summary;
char *body;
};
void git_commit__free(void *commit);

View File

@ -47,7 +47,7 @@ git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_
git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk)
{
return (git_commit_list_node *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);
return (git_commit_list_node *)git_pool_mallocz(&walk->commit_pool, 1);
}
static int commit_error(git_commit_list_node *commit, const char *msg)
@ -110,7 +110,7 @@ static int commit_quick_parse(
const uint8_t *buffer_end = buffer + buffer_len;
const uint8_t *parents_start, *committer_start;
int i, parents = 0;
int commit_time;
int64_t commit_time;
buffer += strlen("tree ") + GIT_OID_HEXSZ + 1;
@ -166,10 +166,10 @@ static int commit_quick_parse(
buffer--;
}
if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
if ((buffer == committer_start) || (git__strtol64(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
return commit_error(commit, "cannot parse commit time");
commit->time = (time_t)commit_time;
commit->time = commit_time;
commit->parsed = 1;
return 0;
}

View File

@ -13,6 +13,7 @@
#define PARENT2 (1 << 1)
#define RESULT (1 << 2)
#define STALE (1 << 3)
#define ALL_FLAGS (PARENT1 | PARENT2 | STALE | RESULT)
#define PARENTS_PER_COMMIT 2
#define COMMIT_ALLOC \
@ -22,7 +23,7 @@
typedef struct git_commit_list_node {
git_oid oid;
uint32_t time;
int64_t time;
unsigned int seen:1,
uninteresting:1,
topo_delay:1,

View File

@ -41,11 +41,16 @@
# include <ws2tcpip.h>
# include "win32/msvc-compat.h"
# include "win32/mingw-compat.h"
# include "win32/win32-compat.h"
# include "win32/error.h"
# include "win32/version.h"
# ifdef GIT_THREADS
# include "win32/pthread.h"
# endif
# if defined(GIT_MSVC_CRTDBG)
# include "win32/w32_stack.h"
# include "win32/w32_crtdbg_stacktrace.h"
# endif
#else
@ -57,6 +62,12 @@
# endif
#define GIT_STDLIB_CALL
#ifdef GIT_USE_STAT_ATIMESPEC
# define st_atim st_atimespec
# define st_ctim st_ctimespec
# define st_mtim st_mtimespec
#endif
# include <arpa/inet.h>
#endif
@ -78,6 +89,11 @@
*/
#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
/**
* Check a buffer allocation result, returning -1 if it failed.
*/
#define GITERR_CHECK_ALLOC_BUF(buf) if ((void *)(buf) == NULL || git_buf_oom(buf)) { return -1; }
/**
* Check a return value and propagate result if non-zero.
*/
@ -137,20 +153,25 @@ void giterr_system_set(int code);
* Structure to preserve libgit2 error state
*/
typedef struct {
int error_code;
int error_code;
unsigned int oom : 1;
git_error error_msg;
} git_error_state;
/**
* Capture current error state to restore later, returning error code.
* If `error_code` is zero, this does nothing and returns zero.
* If `error_code` is zero, this does not clear the current error state.
* You must either restore this error state, or free it.
*/
int giterr_capture(git_error_state *state, int error_code);
extern int giterr_state_capture(git_error_state *state, int error_code);
/**
* Restore error state to a previous value, returning saved error code.
*/
int giterr_restore(git_error_state *state);
extern int giterr_state_restore(git_error_state *state);
/** Free an error state. */
extern void giterr_state_free(git_error_state *state);
/**
* Check a versioned structure for validity
@ -199,6 +220,15 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
#define GITERR_CHECK_ALLOC_ADD(out, one, two) \
if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { return -1; }
#define GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \
if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { return -1; }
#define GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \
if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; }
/** Check for multiplicative overflow, failing if it would occur. */
#define GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }

View File

@ -13,6 +13,7 @@
#include "vector.h"
#include "buf_text.h"
#include "config_file.h"
#include "transaction.h"
#if GIT_WIN32
# include <windows.h>
#endif
@ -1085,6 +1086,12 @@ int git_config_find_system(git_buf *path)
return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
}
int git_config_find_programdata(git_buf *path)
{
git_buf_sanitize(path);
return git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA);
}
int git_config__global_location(git_buf *buf)
{
const git_buf *paths;
@ -1132,6 +1139,10 @@ int git_config_open_default(git_config **out)
error = git_config_add_file_ondisk(cfg, buf.ptr,
GIT_CONFIG_LEVEL_SYSTEM, 0);
if (!error && !git_config_find_programdata(&buf))
error = git_config_add_file_ondisk(cfg, buf.ptr,
GIT_CONFIG_LEVEL_PROGRAMDATA, 0);
git_buf_free(&buf);
if (error) {
@ -1144,6 +1155,41 @@ int git_config_open_default(git_config **out)
return error;
}
int git_config_lock(git_transaction **out, git_config *cfg)
{
int error;
git_config_backend *file;
file_internal *internal;
internal = git_vector_get(&cfg->files, 0);
if (!internal || !internal->file) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files");
return -1;
}
file = internal->file;
if ((error = file->lock(file)) < 0)
return error;
return git_transaction_config_new(out, cfg);
}
int git_config_unlock(git_config *cfg, int commit)
{
git_config_backend *file;
file_internal *internal;
internal = git_vector_get(&cfg->files, 0);
if (!internal || !internal->file) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files");
return -1;
}
file = internal->file;
return file->unlock(file, commit);
}
/***********
* Parsers
***********/

View File

@ -12,6 +12,7 @@
#include "vector.h"
#include "repository.h"
#define GIT_CONFIG_FILENAME_PROGRAMDATA "config"
#define GIT_CONFIG_FILENAME_SYSTEM "gitconfig"
#define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig"
#define GIT_CONFIG_FILENAME_XDG "config"
@ -88,4 +89,19 @@ extern int git_config__cvar(
*/
int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out,
const git_cvar_map *maps, size_t map_n, int enum_val);
/**
* Unlock the backend with the highest priority
*
* Unlocking will allow other writers to updat the configuration
* file. Optionally, any changes performed since the lock will be
* applied to the configuration.
*
* @param cfg the configuration
* @param commit boolean which indicates whether to commit any changes
* done since locking
* @return 0 or an error code
*/
GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit);
#endif

View File

@ -77,8 +77,7 @@ typedef struct git_config_file_iter {
(iter) = (tmp))
struct reader {
time_t file_mtime;
size_t file_size;
git_oid checksum;
char *file_path;
git_buf buffer;
char *read_ptr;
@ -105,6 +104,10 @@ typedef struct {
git_array_t(struct reader) readers;
bool locked;
git_filebuf locked_buf;
git_buf locked_content;
char *file_path;
} diskfile_backend;
@ -281,7 +284,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
git_buf_init(&reader->buffer, 0);
res = git_futils_readbuffer_updated(
&reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
&reader->buffer, b->file_path, &reader->checksum, NULL);
/* It's fine if the file doesn't exist */
if (res == GIT_ENOTFOUND)
@ -341,7 +344,7 @@ static int config_refresh(git_config_backend *cfg)
reader = git_array_get(b->readers, i);
error = git_futils_readbuffer_updated(
&reader->buffer, reader->file_path,
&reader->file_mtime, &reader->file_size, &updated);
&reader->checksum, &updated);
if (error < 0 && error != GIT_ENOTFOUND)
return error;
@ -685,6 +688,42 @@ static int config_snapshot(git_config_backend **out, git_config_backend *in)
return git_config_file__snapshot(out, b);
}
static int config_lock(git_config_backend *_cfg)
{
diskfile_backend *cfg = (diskfile_backend *) _cfg;
int error;
if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0)
return error;
error = git_futils_readbuffer(&cfg->locked_content, cfg->file_path);
if (error < 0 && error != GIT_ENOTFOUND) {
git_filebuf_cleanup(&cfg->locked_buf);
return error;
}
cfg->locked = true;
return 0;
}
static int config_unlock(git_config_backend *_cfg, int success)
{
diskfile_backend *cfg = (diskfile_backend *) _cfg;
int error = 0;
if (success) {
git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size);
error = git_filebuf_commit(&cfg->locked_buf);
}
git_filebuf_cleanup(&cfg->locked_buf);
git_buf_free(&cfg->locked_content);
cfg->locked = false;
return error;
}
int git_config_file__ondisk(git_config_backend **out, const char *path)
{
diskfile_backend *backend;
@ -706,6 +745,8 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
backend->header.parent.del_multivar = config_delete_multivar;
backend->header.parent.iterator = config_iterator_new;
backend->header.parent.snapshot = config_snapshot;
backend->header.parent.lock = config_lock;
backend->header.parent.unlock = config_unlock;
backend->header.parent.free = backend_free;
*out = (git_config_backend *)backend;
@ -750,6 +791,21 @@ static int config_delete_readonly(git_config_backend *cfg, const char *name)
return config_error_readonly();
}
static int config_lock_readonly(git_config_backend *_cfg)
{
GIT_UNUSED(_cfg);
return config_error_readonly();
}
static int config_unlock_readonly(git_config_backend *_cfg, int success)
{
GIT_UNUSED(_cfg);
GIT_UNUSED(success);
return config_error_readonly();
}
static void backend_readonly_free(git_config_backend *_backend)
{
diskfile_backend *backend = (diskfile_backend *)_backend;
@ -803,6 +859,8 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
backend->header.parent.del = config_delete_readonly;
backend->header.parent.del_multivar = config_delete_multivar_readonly;
backend->header.parent.iterator = config_iterator_new;
backend->header.parent.lock = config_lock_readonly;
backend->header.parent.unlock = config_unlock_readonly;
backend->header.parent.free = backend_readonly_free;
*out = (git_config_backend *)backend;
@ -1559,7 +1617,7 @@ static int read_on_variable(
git_buf_init(&r->buffer, 0);
result = git_futils_readbuffer_updated(
&r->buffer, r->file_path, &r->file_mtime, &r->file_size, NULL);
&r->buffer, r->file_path, &r->checksum, NULL);
if (result == 0) {
result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1);
@ -1602,7 +1660,7 @@ static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct re
return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data);
}
static int write_section(git_filebuf *file, const char *key)
static int write_section(git_buf *fbuf, const char *key)
{
int result;
const char *dot;
@ -1626,7 +1684,7 @@ static int write_section(git_filebuf *file, const char *key)
if (git_buf_oom(&buf))
return -1;
result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size);
git_buf_free(&buf);
return result;
@ -1651,7 +1709,8 @@ static const char *quotes_for_value(const char *value)
}
struct write_data {
git_filebuf *file;
git_buf *buf;
git_buf buffered_comment;
unsigned int in_section : 1,
preg_replaced : 1;
const char *section;
@ -1660,23 +1719,28 @@ struct write_data {
const char *value;
};
static int write_line(struct write_data *write_data, const char *line, size_t line_len)
static int write_line_to(git_buf *buf, const char *line, size_t line_len)
{
int result = git_filebuf_write(write_data->file, line, line_len);
int result = git_buf_put(buf, line, line_len);
if (!result && line_len && line[line_len-1] != '\n')
result = git_filebuf_printf(write_data->file, "\n");
result = git_buf_printf(buf, "\n");
return result;
}
static int write_line(struct write_data *write_data, const char *line, size_t line_len)
{
return write_line_to(write_data->buf, line, line_len);
}
static int write_value(struct write_data *write_data)
{
const char *q;
int result;
q = quotes_for_value(write_data->value);
result = git_filebuf_printf(write_data->file,
result = git_buf_printf(write_data->buf,
"\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
/* If we are updating a single name/value, we're done. Setting `value`
@ -1711,6 +1775,14 @@ static int write_on_section(
write_data->in_section = strcmp(current_section, write_data->section) == 0;
/*
* If there were comments just before this section, dump them as well.
*/
if (!result) {
result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size);
git_buf_clear(&write_data->buffered_comment);
}
if (!result)
result = write_line(write_data, line, line_len);
@ -1728,10 +1800,19 @@ static int write_on_variable(
{
struct write_data *write_data = (struct write_data *)data;
bool has_matched = false;
int error;
GIT_UNUSED(reader);
GIT_UNUSED(current_section);
/*
* If there were comments just before this variable, let's dump them as well.
*/
if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
return error;
git_buf_clear(&write_data->buffered_comment);
/* See if we are to update this name/value pair; first examine name */
if (write_data->in_section &&
strcasecmp(write_data->name, var_name) == 0)
@ -1766,7 +1847,7 @@ static int write_on_comment(struct reader **reader, const char *line, size_t lin
GIT_UNUSED(reader);
write_data = (struct write_data *)data;
return write_line(write_data, line, line_len);
return write_line_to(&write_data->buffered_comment, line, line_len);
}
static int write_on_eof(struct reader **reader, void *data)
@ -1776,13 +1857,19 @@ static int write_on_eof(struct reader **reader, void *data)
GIT_UNUSED(reader);
/*
* If we've buffered comments when reaching EOF, make sure to dump them.
*/
if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
return result;
/* If we are at the EOF and have not written our value (again, for a
* simple name/value set, not a multivar) then we have never seen the
* section in question and should create a new section and write the
* value.
*/
if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
if ((result = write_section(write_data->file, write_data->section)) == 0)
if ((result = write_section(write_data->buf, write_data->section)) == 0)
result = write_value(write_data);
}
@ -1797,18 +1884,23 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
int result;
char *section, *name, *ldot;
git_filebuf file = GIT_FILEBUF_INIT;
git_buf buf = GIT_BUF_INIT;
struct reader *reader = git_array_get(cfg->readers, 0);
struct write_data write_data;
/* Lock the file */
if ((result = git_filebuf_open(
&file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) {
if (cfg->locked) {
result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content));
} else {
/* Lock the file */
if ((result = git_filebuf_open(
&file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
git_buf_free(&reader->buffer);
return result;
}
}
/* We need to read in our own config file */
result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
/* We need to read in our own config file */
result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
}
/* Initialise the reading position */
if (result == GIT_ENOTFOUND) {
@ -1827,7 +1919,8 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
name = ldot + 1;
section = git__strndup(key, ldot - key);
write_data.file = &file;
write_data.buf = &buf;
git_buf_init(&write_data.buffered_comment, 0);
write_data.section = section;
write_data.in_section = 0;
write_data.preg_replaced = 0;
@ -1837,19 +1930,25 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
git__free(section);
git_buf_free(&write_data.buffered_comment);
if (result < 0) {
git_filebuf_cleanup(&file);
goto done;
}
/* refresh stats - if this errors, then commit will error too */
(void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
result = git_filebuf_commit(&file);
git_buf_free(&reader->buffer);
if (cfg->locked) {
size_t len = buf.asize;
/* Update our copy with the modified contents */
git_buf_free(&cfg->locked_content);
git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
} else {
git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
result = git_filebuf_commit(&file);
}
done:
git_buf_free(&buf);
git_buf_free(&reader->buffer);
return result;
}

View File

@ -55,6 +55,16 @@ GIT_INLINE(int) git_config_file_foreach_match(
return git_config_backend_foreach_match(cfg, regexp, fn, data);
}
GIT_INLINE(int) git_config_file_lock(git_config_backend *cfg)
{
return cfg->lock(cfg);
}
GIT_INLINE(int) git_config_file_unlock(git_config_backend *cfg, int success)
{
return cfg->unlock(cfg, success);
}
extern int git_config_file_normalize_section(char *start, char *end);
#endif

View File

@ -346,7 +346,7 @@ static int crlf_apply(
/* initialize payload in case `check` was bypassed */
if (!*payload) {
int error = crlf_check(self, payload, src, NULL);
if (error < 0 && error != GIT_PASSTHROUGH)
if (error < 0)
return error;
}

View File

@ -67,9 +67,9 @@ static int curls_certificate(git_cert **out, git_stream *stream)
/* No information is available, can happen with SecureTransport */
if (certinfo->num_of_certs == 0) {
s->cert_info.cert_type = GIT_CERT_NONE;
s->cert_info.data = NULL;
s->cert_info.len = 0;
s->cert_info.parent.cert_type = GIT_CERT_NONE;
s->cert_info.data = NULL;
s->cert_info.len = 0;
return 0;
}
@ -79,17 +79,18 @@ static int curls_certificate(git_cert **out, git_stream *stream)
for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
char *str = git__strdup(slist->data);
GITERR_CHECK_ALLOC(str);
git_vector_insert(&strings, str);
}
/* Copy the contents of the vector into a strarray so we can expose them */
s->cert_info_strings.strings = (char **) strings.contents;
s->cert_info_strings.count = strings.length;
s->cert_info.cert_type = GIT_CERT_STRARRAY;
s->cert_info.data = &s->cert_info_strings;
s->cert_info.len = strings.length;
s->cert_info.parent.cert_type = GIT_CERT_STRARRAY;
s->cert_info.data = &s->cert_info_strings;
s->cert_info.len = strings.length;
*out = (git_cert *) &s->cert_info;
*out = &s->cert_info.parent;
return 0;
}
@ -207,11 +208,14 @@ int git_curl_stream_new(git_stream **out, const char *host, const char *port)
handle = curl_easy_init();
if (handle == NULL) {
giterr_set(GITERR_NET, "failed to create curl handle");
git__free(st);
return -1;
}
if ((error = git__strtol32(&iport, port, NULL, 10)) < 0)
if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) {
git__free(st);
return error;
}
curl_easy_setopt(handle, CURLOPT_URL, host);
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
@ -220,6 +224,7 @@ int git_curl_stream_new(git_stream **out, const char *host, const char *port)
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
/* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */

View File

@ -56,7 +56,7 @@ static int diff_insert_delta(
if (diff->opts.notify_cb) {
error = diff->opts.notify_cb(
diff, delta, matched_pathspec, diff->opts.notify_payload);
diff, delta, matched_pathspec, diff->opts.payload);
if (error) {
git__free(delta);
@ -74,6 +74,32 @@ static int diff_insert_delta(
return error;
}
static bool diff_pathspec_match(
const char **matched_pathspec,
git_diff *diff,
const git_index_entry *entry)
{
bool disable_pathspec_match =
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH);
/* If we're disabling fnmatch, then the iterator has already applied
* the filters to the files for us and we don't have to do anything.
* However, this only applies to *files* - the iterator will include
* directories that we need to recurse into when not autoexpanding,
* so we still need to apply the pathspec match to directories.
*/
if ((S_ISLNK(entry->mode) || S_ISREG(entry->mode)) &&
disable_pathspec_match) {
*matched_pathspec = entry->path;
return true;
}
return git_pathspec__match(
&diff->pathspec, entry->path, disable_pathspec_match,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
matched_pathspec, NULL);
}
static int diff_delta__from_one(
git_diff *diff,
git_delta_t status,
@ -105,16 +131,12 @@ static int diff_delta__from_one(
if (status == GIT_DELTA_UNTRACKED &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
return 0;
if (status == GIT_DELTA_UNREADABLE &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
return 0;
if (!git_pathspec__match(
&diff->pathspec, entry->path,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
&matched_pathspec, NULL))
if (!diff_pathspec_match(&matched_pathspec, diff, entry))
return 0;
delta = diff_delta__alloc(diff, status, entry->path);
@ -408,8 +430,9 @@ static git_diff *diff_list_alloc(
diff->new_src = new_iter->type;
memcpy(&diff->opts, &dflt, sizeof(diff->opts));
if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
git_pool_init(&diff->pool, 1, 0) < 0) {
git_pool_init(&diff->pool, 1);
if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0) {
git_diff_free(diff);
return NULL;
}
@ -471,9 +494,6 @@ static int diff_list_apply_options(
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
/* Set GIT_DIFFCAPS_TRUST_NANOSECS on a platform basis */
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_NANOSECS;
/* If not given explicit `opts`, check `diff.xyz` configs */
if (!opts) {
int context = git_config__get_int_force(cfg, "diff.context", 3);
@ -674,13 +694,6 @@ int git_diff__oid_for_entry(
return error;
}
static bool diff_time_eq(
const git_index_time *a, const git_index_time *b, bool use_nanos)
{
return a->seconds == b->seconds &&
(!use_nanos || a->nanoseconds == b->nanoseconds);
}
typedef struct {
git_repository *repo;
git_iterator *old_iter;
@ -755,11 +768,7 @@ static int maybe_modified(
const char *matched_pathspec;
int error = 0;
if (!git_pathspec__match(
&diff->pathspec, oitem->path,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
&matched_pathspec, NULL))
if (!diff_pathspec_match(&matched_pathspec, diff, oitem))
return 0;
memset(&noid, 0, sizeof(noid));
@ -817,7 +826,6 @@ static int maybe_modified(
*/
} else if (git_oid_iszero(&nitem->id) && new_is_workdir) {
bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0);
git_index *index;
git_iterator_index(&index, info->new_iter);
@ -836,13 +844,12 @@ static int maybe_modified(
modified_uncertain =
(oitem->file_size <= 0 && nitem->file_size > 0);
}
else if (!diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) ||
(use_ctime &&
!diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) ||
else if (!git_index_time_eq(&oitem->mtime, &nitem->mtime) ||
(use_ctime && !git_index_time_eq(&oitem->ctime, &nitem->ctime)) ||
oitem->ino != nitem->ino ||
oitem->uid != nitem->uid ||
oitem->gid != nitem->gid ||
(index && nitem->mtime.seconds >= index->stamp.mtime))
git_index_entry_newer_than_index(nitem, index))
{
status = GIT_DELTA_MODIFIED;
modified_uncertain = true;
@ -1053,6 +1060,12 @@ static int handle_unmatched_new_item(
&info->nitem, &untracked_state, info->new_iter)) < 0)
return error;
/* if we found nothing that matched our pathlist filter, exclude */
if (untracked_state == GIT_ITERATOR_STATUS_FILTERED) {
git_vector_pop(&diff->deltas);
git__free(last);
}
/* if we found nothing or just ignored items, update the record */
if (untracked_state == GIT_ITERATOR_STATUS_IGNORED ||
untracked_state == GIT_ITERATOR_STATUS_EMPTY) {
@ -1233,7 +1246,18 @@ int git_diff__from_iterators(
/* run iterators building diffs */
while (!error && (info.oitem || info.nitem)) {
int cmp = info.oitem ?
int cmp;
/* report progress */
if (opts && opts->progress_cb) {
if ((error = opts->progress_cb(diff,
info.oitem ? info.oitem->path : NULL,
info.nitem ? info.nitem->path : NULL,
opts->payload)))
break;
}
cmp = info.oitem ?
(info.nitem ? diff->entrycomp(info.oitem, info.nitem) : -1) : 1;
/* create DELETED records for old items not matched in new */
@ -1264,11 +1288,26 @@ cleanup:
return error;
}
#define DIFF_FROM_ITERATORS(MAKE_FIRST, MAKE_SECOND) do { \
#define DIFF_FROM_ITERATORS(MAKE_FIRST, FLAGS_FIRST, MAKE_SECOND, FLAGS_SECOND) do { \
git_iterator *a = NULL, *b = NULL; \
char *pfx = opts ? git_pathspec_prefix(&opts->pathspec) : NULL; \
char *pfx = (opts && !(opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) ? \
git_pathspec_prefix(&opts->pathspec) : NULL; \
git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, \
b_opts = GIT_ITERATOR_OPTIONS_INIT; \
a_opts.flags = FLAGS_FIRST; \
a_opts.start = pfx; \
a_opts.end = pfx; \
b_opts.flags = FLAGS_SECOND; \
b_opts.start = pfx; \
b_opts.end = pfx; \
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \
if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \
if (opts && (opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) { \
a_opts.pathlist.strings = opts->pathspec.strings; \
a_opts.pathlist.count = opts->pathspec.count; \
b_opts.pathlist.strings = opts->pathspec.strings; \
b_opts.pathlist.count = opts->pathspec.count; \
} \
if (!error && !(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \
error = git_diff__from_iterators(diff, repo, a, b, opts); \
git__free(pfx); git_iterator_free(a); git_iterator_free(b); \
} while (0)
@ -1280,8 +1319,8 @@ int git_diff_tree_to_tree(
git_tree *new_tree,
const git_diff_options *opts)
{
int error = 0;
git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE;
int error = 0;
assert(diff && repo);
@ -1293,8 +1332,8 @@ int git_diff_tree_to_tree(
iflag = GIT_ITERATOR_IGNORE_CASE;
DIFF_FROM_ITERATORS(
git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx),
git_iterator_for_tree(&b, new_tree, iflag, pfx, pfx)
git_iterator_for_tree(&a, old_tree, &a_opts), iflag,
git_iterator_for_tree(&b, new_tree, &b_opts), iflag
);
return error;
@ -1318,10 +1357,10 @@ int git_diff_tree_to_index(
git_index *index,
const git_diff_options *opts)
{
int error = 0;
bool index_ignore_case = false;
git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE |
GIT_ITERATOR_INCLUDE_CONFLICTS;
bool index_ignore_case = false;
int error = 0;
assert(diff && repo);
@ -1331,8 +1370,8 @@ int git_diff_tree_to_index(
index_ignore_case = index->ignore_case;
DIFF_FROM_ITERATORS(
git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx),
git_iterator_for_index(&b, index, iflag, pfx, pfx)
git_iterator_for_tree(&a, old_tree, &a_opts), iflag,
git_iterator_for_index(&b, repo, index, &b_opts), iflag
);
/* if index is in case-insensitive order, re-sort deltas to match */
@ -1356,10 +1395,11 @@ int git_diff_index_to_workdir(
return error;
DIFF_FROM_ITERATORS(
git_iterator_for_index(
&a, index, GIT_ITERATOR_INCLUDE_CONFLICTS, pfx, pfx),
git_iterator_for_workdir(
&b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
git_iterator_for_index(&a, repo, index, &a_opts),
GIT_ITERATOR_INCLUDE_CONFLICTS,
git_iterator_for_workdir(&b, repo, index, NULL, &b_opts),
GIT_ITERATOR_DONT_AUTOEXPAND
);
if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX) && (*diff)->index_updated)
@ -1383,9 +1423,8 @@ int git_diff_tree_to_workdir(
return error;
DIFF_FROM_ITERATORS(
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
git_iterator_for_workdir(
&b, repo, index, old_tree, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
git_iterator_for_tree(&a, old_tree, &a_opts), 0,
git_iterator_for_workdir(&b, repo, index, old_tree, &b_opts), GIT_ITERATOR_DONT_AUTOEXPAND
);
return error;
@ -1421,6 +1460,29 @@ int git_diff_tree_to_workdir_with_index(
return error;
}
int git_diff_index_to_index(
git_diff **diff,
git_repository *repo,
git_index *old_index,
git_index *new_index,
const git_diff_options *opts)
{
int error = 0;
assert(diff && old_index && new_index);
DIFF_FROM_ITERATORS(
git_iterator_for_index(&a, repo, old_index, &a_opts), GIT_ITERATOR_DONT_IGNORE_CASE,
git_iterator_for_index(&b, repo, new_index, &b_opts), GIT_ITERATOR_DONT_IGNORE_CASE
);
/* if index is in case-insensitive order, re-sort deltas to match */
if (!error && (old_index->ignore_case || new_index->ignore_case))
diff_set_ignore_case(*diff, true);
return error;
}
size_t git_diff_num_deltas(const git_diff *diff)
{
assert(diff);
@ -1597,6 +1659,7 @@ int git_diff_format_email__append_header_tobuf(
const git_oid *id,
const git_signature *author,
const char *summary,
const char *body,
size_t patch_no,
size_t total_patches,
bool exclude_patchno_marker)
@ -1636,6 +1699,13 @@ int git_diff_format_email__append_header_tobuf(
error = git_buf_printf(out, "%s\n\n", summary);
if (body) {
git_buf_puts(out, body);
if (out->ptr[out->size - 1] != '\n')
git_buf_putc(out, '\n');
}
return error;
}
@ -1713,7 +1783,7 @@ int git_diff_format_email(
error = git_diff_format_email__append_header_tobuf(out,
opts->id, opts->author, summary == NULL ? opts->summary : summary,
opts->patch_no, opts->total_patches, ignore_marker);
opts->body, opts->patch_no, opts->total_patches, ignore_marker);
if (error < 0)
goto on_error;
@ -1756,6 +1826,7 @@ int git_diff_commit_as_email(
opts.total_patches = total_patches;
opts.id = git_commit_id(commit);
opts.summary = git_commit_summary(commit);
opts.body = git_commit_body(commit);
opts.author = git_commit_author(commit);
if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)

View File

@ -28,7 +28,6 @@ enum {
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? */
GIT_DIFFCAPS_TRUST_NANOSECS = (1 << 5), /* use stat time nanoseconds */
};
#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)

View File

@ -97,8 +97,7 @@ static int diff_driver_add_patterns(
for (scan = regex_str; scan; scan = end) {
/* get pattern to fill in */
if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) {
error = -1;
break;
return -1;
}
pat->flags = regex_flags;
@ -117,10 +116,9 @@ static int diff_driver_add_patterns(
break;
if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) != 0) {
/* if regex fails to compile, warn? fail? */
error = giterr_set_regex(&pat->re, error);
regfree(&pat->re);
break;
/*
* TODO: issue a warning
*/
}
}
@ -128,7 +126,8 @@ static int diff_driver_add_patterns(
(void)git_array_pop(drv->fn_patterns); /* release last item */
git_buf_free(&buf);
return error;
/* We want to ignore bad patterns, so return success regardless */
return 0;
}
static int diff_driver_xfuncname(const git_config_entry *entry, void *payload)

View File

@ -259,10 +259,35 @@ static int diff_file_content_load_blob(
return error;
}
static int diff_file_content_load_workdir_symlink_fake(
git_diff_file_content *fc, git_buf *path)
{
git_buf target = GIT_BUF_INIT;
int error;
if ((error = git_futils_readbuffer(&target, path->ptr)) < 0)
return error;
fc->map.len = git_buf_len(&target);
fc->map.data = git_buf_detach(&target);
fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
git_buf_free(&target);
return error;
}
static int diff_file_content_load_workdir_symlink(
git_diff_file_content *fc, git_buf *path)
{
ssize_t alloc_len, read_len;
int symlink_supported, error;
if ((error = git_repository__cvar(
&symlink_supported, fc->repo, GIT_CVAR_SYMLINKS)) < 0)
return -1;
if (!symlink_supported)
return diff_file_content_load_workdir_symlink_fake(fc, path);
/* link path on disk could be UTF-16, so prepare a buffer that is
* big enough to handle some UTF-8 data expansion

View File

@ -30,6 +30,10 @@ static void diff_patch_update_binary(git_patch *patch)
(patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE ||
patch->nfile.file->size > GIT_XDIFF_MAX_SIZE)
patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
(patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;

View File

@ -92,7 +92,11 @@ static int diff_print_info_init_frompatch(
git_diff_line_cb cb,
void *payload)
{
git_repository *repo = patch && patch->diff ? patch->diff->repo : NULL;
git_repository *repo;
assert(patch);
repo = patch->diff ? patch->diff->repo : NULL;
memset(pi, 0, sizeof(diff_print_info));
@ -358,6 +362,7 @@ static int format_binary(
scan += chunk_len;
pi->line.num_lines++;
}
git_buf_putc(pi->buf, '\n');
return 0;
}
@ -416,7 +421,6 @@ static int diff_print_patch_file_binary(
if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
(error = git_buf_putc(pi->buf, '\n')) < 0 ||
(error = format_binary(pi, binary->old_file.type, binary->old_file.data,
binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {

View File

@ -134,11 +134,11 @@ int git_diff__merge(
return -1;
}
if (git_vector_init(
&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 ||
git_pool_init(&onto_pool, 1, 0) < 0)
if (git_vector_init(&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0)
return -1;
git_pool_init(&onto_pool, 1);
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
@ -261,18 +261,23 @@ static int normalize_find_opts(
if (!given ||
(given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG)
{
char *rule =
git_config__get_string_force(cfg, "diff.renames", "true");
int boolval;
if (diff->repo) {
char *rule =
git_config__get_string_force(cfg, "diff.renames", "true");
int boolval;
if (!git__parse_bool(&boolval, rule) && !boolval)
/* don't set FIND_RENAMES if bool value is false */;
else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy"))
opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
else
if (!git__parse_bool(&boolval, rule) && !boolval)
/* don't set FIND_RENAMES if bool value is false */;
else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy"))
opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
else
opts->flags |= GIT_DIFF_FIND_RENAMES;
git__free(rule);
} else {
/* set default flag */
opts->flags |= GIT_DIFF_FIND_RENAMES;
git__free(rule);
}
}
/* some flags imply others */

View File

@ -4,6 +4,7 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "git2/errors.h"
#include "common.h"
#include "diff.h"
#include "diff_driver.h"
@ -208,6 +209,12 @@ static int git_xdiff(git_diff_output *output, git_patch *patch)
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);
if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE ||
info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) {
giterr_set(GITERR_INVALID, "files too large for diff");
return -1;
}
xdl_diff(&info.xd_old_data, &info.xd_new_data,
&xo->params, &xo->config, &xo->callback);

View File

@ -11,6 +11,11 @@
#include "diff_patch.h"
#include "xdiff/xdiff.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.

View File

@ -18,19 +18,30 @@ static git_error g_git_oom_error = {
GITERR_NOMEMORY
};
static void set_error(int error_class, char *string)
static void set_error_from_buffer(int error_class)
{
git_error *error = &GIT_GLOBAL->error_t;
git_buf *buf = &GIT_GLOBAL->error_buf;
if (error->message != string)
git__free(error->message);
error->message = string;
error->message = buf->ptr;
error->klass = error_class;
GIT_GLOBAL->last_error = error;
}
static void set_error(int error_class, char *string)
{
git_buf *buf = &GIT_GLOBAL->error_buf;
git_buf_clear(buf);
if (string) {
git_buf_puts(buf, string);
git__free(string);
}
set_error_from_buffer(error_class);
}
void giterr_set_oom(void)
{
GIT_GLOBAL->last_error = &g_git_oom_error;
@ -38,27 +49,28 @@ void giterr_set_oom(void)
void giterr_set(int error_class, const char *string, ...)
{
git_buf buf = GIT_BUF_INIT;
va_list arglist;
#ifdef GIT_WIN32
DWORD win32_error_code = (error_class == GITERR_OS) ? GetLastError() : 0;
#endif
int error_code = (error_class == GITERR_OS) ? errno : 0;
git_buf *buf = &GIT_GLOBAL->error_buf;
git_buf_clear(buf);
if (string) {
va_start(arglist, string);
git_buf_vprintf(&buf, string, arglist);
git_buf_vprintf(buf, string, arglist);
va_end(arglist);
if (error_class == GITERR_OS)
git_buf_PUTS(&buf, ": ");
git_buf_PUTS(buf, ": ");
}
if (error_class == GITERR_OS) {
#ifdef GIT_WIN32
char * win32_error = git_win32_get_error_message(win32_error_code);
if (win32_error) {
git_buf_puts(&buf, win32_error);
git_buf_puts(buf, win32_error);
git__free(win32_error);
SetLastError(0);
@ -66,26 +78,29 @@ void giterr_set(int error_class, const char *string, ...)
else
#endif
if (error_code)
git_buf_puts(&buf, strerror(error_code));
git_buf_puts(buf, strerror(error_code));
if (error_code)
errno = 0;
}
if (!git_buf_oom(&buf))
set_error(error_class, git_buf_detach(&buf));
if (!git_buf_oom(buf))
set_error_from_buffer(error_class);
}
void giterr_set_str(int error_class, const char *string)
{
char *message;
git_buf *buf = &GIT_GLOBAL->error_buf;
assert(string);
message = git__strdup(string);
if (!string)
return;
if (message)
set_error(error_class, message);
git_buf_clear(buf);
git_buf_puts(buf, string);
if (!git_buf_oom(buf))
set_error_from_buffer(error_class);
}
int giterr_set_regex(const regex_t *regex, int error_code)
@ -116,45 +131,65 @@ void giterr_clear(void)
#endif
}
int giterr_detach(git_error *cpy)
{
git_error *error = GIT_GLOBAL->last_error;
assert(cpy);
if (!error)
return -1;
cpy->message = error->message;
cpy->klass = error->klass;
error->message = NULL;
giterr_clear();
return 0;
}
const git_error *giterr_last(void)
{
return GIT_GLOBAL->last_error;
}
int giterr_capture(git_error_state *state, int error_code)
int giterr_state_capture(git_error_state *state, int error_code)
{
git_error *error = GIT_GLOBAL->last_error;
git_buf *error_buf = &GIT_GLOBAL->error_buf;
memset(state, 0, sizeof(git_error_state));
if (!error_code)
return 0;
state->error_code = error_code;
if (error_code)
giterr_detach(&state->error_msg);
state->oom = (error == &g_git_oom_error);
if (error) {
state->error_msg.klass = error->klass;
if (state->oom)
state->error_msg.message = g_git_oom_error.message;
else
state->error_msg.message = git_buf_detach(error_buf);
}
giterr_clear();
return error_code;
}
int giterr_restore(git_error_state *state)
int giterr_state_restore(git_error_state *state)
{
if (state && state->error_code && state->error_msg.message)
set_error(state->error_msg.klass, state->error_msg.message);
else
giterr_clear();
int ret = 0;
return state ? state->error_code : 0;
giterr_clear();
if (state && state->error_msg.message) {
if (state->oom)
giterr_set_oom();
else
set_error(state->error_msg.klass, state->error_msg.message);
ret = state->error_code;
memset(state, 0, sizeof(git_error_state));
}
return ret;
}
void giterr_state_free(git_error_state *state)
{
if (!state)
return;
if (!state->oom)
git__free(state->error_msg.message);
memset(state, 0, sizeof(git_error_state));
}
int giterr_system_last(void)

View File

@ -191,6 +191,81 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
return 0;
}
#define MAX_SYMLINK_DEPTH 5
static int resolve_symlink(git_buf *out, const char *path)
{
int i, error, root;
ssize_t ret;
struct stat st;
git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT;
if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 ||
(error = git_buf_puts(&curpath, path)) < 0)
return error;
for (i = 0; i < MAX_SYMLINK_DEPTH; i++) {
error = p_lstat(curpath.ptr, &st);
if (error < 0 && errno == ENOENT) {
error = git_buf_puts(out, curpath.ptr);
goto cleanup;
}
if (error < 0) {
giterr_set(GITERR_OS, "failed to stat '%s'", curpath.ptr);
error = -1;
goto cleanup;
}
if (!S_ISLNK(st.st_mode)) {
error = git_buf_puts(out, curpath.ptr);
goto cleanup;
}
ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX);
if (ret < 0) {
giterr_set(GITERR_OS, "failed to read symlink '%s'", curpath.ptr);
error = -1;
goto cleanup;
}
if (ret == GIT_PATH_MAX) {
giterr_set(GITERR_INVALID, "symlink target too long");
error = -1;
goto cleanup;
}
/* readlink(2) won't NUL-terminate for us */
target.ptr[ret] = '\0';
target.size = ret;
root = git_path_root(target.ptr);
if (root >= 0) {
if ((error = git_buf_puts(&curpath, target.ptr)) < 0)
goto cleanup;
} else {
git_buf dir = GIT_BUF_INIT;
if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0)
goto cleanup;
git_buf_swap(&curpath, &dir);
git_buf_free(&dir);
if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0)
goto cleanup;
}
}
giterr_set(GITERR_INVALID, "maximum symlink depth reached");
error = -1;
cleanup:
git_buf_free(&curpath);
git_buf_free(&target);
return error;
}
int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
{
int compression, error = -1;
@ -265,11 +340,14 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
file->path_lock = git_buf_detach(&tmp_path);
GITERR_CHECK_ALLOC(file->path_lock);
} else {
path_len = strlen(path);
git_buf resolved_path = GIT_BUF_INIT;
if ((error = resolve_symlink(&resolved_path, path)) < 0)
goto cleanup;
/* Save the original path of the file */
file->path_original = git__strdup(path);
GITERR_CHECK_ALLOC(file->path_original);
path_len = resolved_path.size;
file->path_original = git_buf_detach(&resolved_path);
/* create the locking path by appending ".lock" to the original */
GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
@ -279,6 +357,12 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
memcpy(file->path_lock, file->path_original, path_len);
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
if (git_path_isdir(file->path_original)) {
giterr_set(GITERR_FILESYSTEM, "path '%s' is a directory", file->path_original);
error = GIT_EDIRECTORY;
goto cleanup;
}
/* open the file for locking */
if ((error = lock_file(file, flags, mode)) < 0)
goto cleanup;

View File

@ -18,7 +18,7 @@ GIT__USE_STRMAP
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
return git_futils_mkdir(
file_path, NULL, mode,
file_path, mode,
GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
}
@ -153,13 +153,15 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
}
int git_futils_readbuffer_updated(
git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
git_buf *out, const char *path, git_oid *checksum, int *updated)
{
int error;
git_file fd;
struct stat st;
bool changed = false;
git_buf buf = GIT_BUF_INIT;
git_oid checksum_new;
assert(buf && path && *path);
assert(out && path && *path);
if (updated != NULL)
*updated = 0;
@ -178,45 +180,50 @@ int git_futils_readbuffer_updated(
return -1;
}
/*
* If we were given a time and/or a size, we only want to read the file
* if it has been modified.
*/
if (size && *size != (size_t)st.st_size)
changed = true;
if (mtime && *mtime != (time_t)st.st_mtime)
changed = true;
if (!size && !mtime)
changed = true;
if (!changed) {
return 0;
}
if (mtime != NULL)
*mtime = st.st_mtime;
if (size != NULL)
*size = (size_t)st.st_size;
if ((fd = git_futils_open_ro(path)) < 0)
return fd;
if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
if (git_futils_readbuffer_fd(&buf, fd, (size_t)st.st_size) < 0) {
p_close(fd);
return -1;
}
p_close(fd);
if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) {
git_buf_free(&buf);
return error;
}
/*
* If we were given a checksum, we only want to use it if it's different
*/
if (checksum && !git_oid__cmp(checksum, &checksum_new)) {
git_buf_free(&buf);
if (updated)
*updated = 0;
return 0;
}
/*
* If we're here, the file did change, or the user didn't have an old version
*/
if (checksum)
git_oid_cpy(checksum, &checksum_new);
if (updated != NULL)
*updated = 1;
git_buf_swap(out, &buf);
git_buf_free(&buf);
return 0;
}
int git_futils_readbuffer(git_buf *buf, const char *path)
{
return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
return git_futils_readbuffer_updated(buf, path, NULL, NULL);
}
int git_futils_writebuffer(
@ -289,96 +296,229 @@ void git_futils_mmap_free(git_map *out)
p_munmap(out);
}
GIT_INLINE(int) validate_existing(
const char *make_path,
GIT_INLINE(int) mkdir_validate_dir(
const char *path,
struct stat *st,
mode_t mode,
uint32_t flags,
struct git_futils_mkdir_perfdata *perfdata)
struct git_futils_mkdir_options *opts)
{
/* with exclusive create, existing dir is an error */
if ((flags & GIT_MKDIR_EXCL) != 0) {
giterr_set(GITERR_FILESYSTEM,
"Failed to make directory '%s': directory exists", path);
return GIT_EEXISTS;
}
if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) ||
(S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) {
if (p_unlink(make_path) < 0) {
if (p_unlink(path) < 0) {
giterr_set(GITERR_OS, "Failed to remove %s '%s'",
S_ISLNK(st->st_mode) ? "symlink" : "file", make_path);
S_ISLNK(st->st_mode) ? "symlink" : "file", path);
return GIT_EEXISTS;
}
perfdata->mkdir_calls++;
opts->perfdata.mkdir_calls++;
if (p_mkdir(make_path, mode) < 0) {
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path);
if (p_mkdir(path, mode) < 0) {
giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
return GIT_EEXISTS;
}
}
else if (S_ISLNK(st->st_mode)) {
/* Re-stat the target, make sure it's a directory */
perfdata->stat_calls++;
opts->perfdata.stat_calls++;
if (p_stat(make_path, st) < 0) {
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path);
if (p_stat(path, st) < 0) {
giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
return GIT_EEXISTS;
}
}
else if (!S_ISDIR(st->st_mode)) {
giterr_set(GITERR_FILESYSTEM,
"Failed to make directory '%s': directory exists", make_path);
"Failed to make directory '%s': directory exists", path);
return GIT_EEXISTS;
}
return 0;
}
int git_futils_mkdir_ext(
GIT_INLINE(int) mkdir_validate_mode(
const char *path,
struct stat *st,
bool terminal_path,
mode_t mode,
uint32_t flags,
struct git_futils_mkdir_options *opts)
{
if (((terminal_path && (flags & GIT_MKDIR_CHMOD) != 0) ||
(flags & GIT_MKDIR_CHMOD_PATH) != 0) && st->st_mode != mode) {
opts->perfdata.chmod_calls++;
if (p_chmod(path, mode) < 0) {
giterr_set(GITERR_OS, "failed to set permissions on '%s'", path);
return -1;
}
}
return 0;
}
GIT_INLINE(int) mkdir_canonicalize(
git_buf *path,
uint32_t flags)
{
ssize_t root_len;
if (path->size == 0) {
giterr_set(GITERR_OS, "attempt to create empty path");
return -1;
}
/* Trim trailing slashes (except the root) */
if ((root_len = git_path_root(path->ptr)) < 0)
root_len = 0;
else
root_len++;
while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/')
path->ptr[--path->size] = '\0';
/* if we are not supposed to made the last element, truncate it */
if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
git_path_dirname_r(path, path->ptr);
flags |= GIT_MKDIR_SKIP_LAST;
}
if ((flags & GIT_MKDIR_SKIP_LAST) != 0) {
git_path_dirname_r(path, path->ptr);
}
/* We were either given the root path (or trimmed it to
* the root), we don't have anything to do.
*/
if (path->size <= (size_t)root_len)
git_buf_clear(path);
return 0;
}
int git_futils_mkdir(
const char *path,
mode_t mode,
uint32_t flags)
{
git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT;
const char *relative;
struct git_futils_mkdir_options opts = { 0 };
struct stat st;
size_t depth = 0;
int len = 0, root_len, error;
if ((error = git_buf_puts(&make_path, path)) < 0 ||
(error = mkdir_canonicalize(&make_path, flags)) < 0 ||
(error = git_buf_puts(&parent_path, make_path.ptr)) < 0 ||
make_path.size == 0)
goto done;
root_len = git_path_root(make_path.ptr);
/* find the first parent directory that exists. this will be used
* as the base to dirname_relative.
*/
for (relative = make_path.ptr; parent_path.size; ) {
error = p_lstat(parent_path.ptr, &st);
if (error == 0) {
break;
} else if (errno != ENOENT) {
giterr_set(GITERR_OS, "failed to stat '%s'", parent_path.ptr);
goto done;
}
depth++;
/* examine the parent of the current path */
if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) {
error = len;
goto done;
}
assert(len);
/* we've walked all the given path's parents and it's either relative
* or rooted. either way, give up and make the entire path.
*/
if ((len == 1 && parent_path.ptr[0] == '.') || len == root_len+1) {
relative = make_path.ptr;
break;
}
relative = make_path.ptr + len + 1;
/* not recursive? just make this directory relative to its parent. */
if ((flags & GIT_MKDIR_PATH) == 0)
break;
}
/* we found an item at the location we're trying to create,
* validate it.
*/
if (depth == 0) {
error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts);
if (!error)
error = mkdir_validate_mode(
make_path.ptr, &st, true, mode, flags, &opts);
goto done;
}
/* we already took `SKIP_LAST` and `SKIP_LAST2` into account when
* canonicalizing `make_path`.
*/
flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST);
error = git_futils_mkdir_relative(relative,
parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts);
done:
git_buf_free(&make_path);
git_buf_free(&parent_path);
return error;
}
int git_futils_mkdir_r(const char *path, const mode_t mode)
{
return git_futils_mkdir(path, mode, GIT_MKDIR_PATH);
}
int git_futils_mkdir_relative(
const char *relative_path,
const char *base,
mode_t mode,
uint32_t flags,
struct git_futils_mkdir_options *opts)
{
int error = -1;
git_buf make_path = GIT_BUF_INIT;
ssize_t root = 0, min_root_len, root_len;
ssize_t root = 0, min_root_len;
char lastch = '/', *tail;
struct stat st;
struct git_futils_mkdir_options empty_opts = {0};
int error;
if (!opts)
opts = &empty_opts;
/* build path and find "root" where we should start calling mkdir */
if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0)
return -1;
if (make_path.size == 0) {
giterr_set(GITERR_OS, "Attempt to create empty path");
if ((error = mkdir_canonicalize(&make_path, flags)) < 0 ||
make_path.size == 0)
goto done;
}
/* Trim trailing slashes (except the root) */
if ((root_len = git_path_root(make_path.ptr)) < 0)
root_len = 0;
else
root_len++;
while (make_path.size > (size_t)root_len &&
make_path.ptr[make_path.size - 1] == '/')
make_path.ptr[--make_path.size] = '\0';
/* if we are not supposed to made the last element, truncate it */
if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
git_path_dirname_r(&make_path, make_path.ptr);
flags |= GIT_MKDIR_SKIP_LAST;
}
if ((flags & GIT_MKDIR_SKIP_LAST) != 0) {
git_path_dirname_r(&make_path, make_path.ptr);
}
/* We were either given the root path (or trimmed it to
* the root), we don't have anything to do.
*/
if (make_path.size <= (size_t)root_len) {
error = 0;
goto done;
}
/* if we are not supposed to make the whole path, reset root */
if ((flags & GIT_MKDIR_PATH) == 0)
@ -437,32 +577,15 @@ retry_lstat:
goto done;
}
} else {
/* with exclusive create, existing dir is an error */
if ((flags & GIT_MKDIR_EXCL) != 0) {
giterr_set(GITERR_FILESYSTEM, "Failed to make directory '%s': directory exists", make_path.ptr);
error = GIT_EEXISTS;
if ((error = mkdir_validate_dir(
make_path.ptr, &st, mode, flags, opts)) < 0)
goto done;
}
if ((error = validate_existing(
make_path.ptr, &st, mode, flags, &opts->perfdata)) < 0)
goto done;
}
/* chmod if requested and necessary */
if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
st.st_mode != mode) {
opts->perfdata.chmod_calls++;
if ((error = p_chmod(make_path.ptr, mode)) < 0 &&
lastch == '\0') {
giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
make_path.ptr);
goto done;
}
}
if ((error = mkdir_validate_mode(
make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0)
goto done;
if (opts->dir_map && opts->pool) {
char *cache_path;
@ -501,21 +624,6 @@ done:
return error;
}
int git_futils_mkdir(
const char *path,
const char *base,
mode_t mode,
uint32_t flags)
{
struct git_futils_mkdir_options options = {0};
return git_futils_mkdir_ext(path, base, mode, flags, &options);
}
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
{
return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
}
typedef struct {
const char *base;
size_t baselen;
@ -777,7 +885,7 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
/* create root directory the first time we need to create a directory */
if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
error = git_futils_mkdir(
info->to_root, NULL, info->dirmode,
info->to_root, info->dirmode,
(info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
@ -785,9 +893,9 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
/* create directory with root as base to prevent excess chmods */
if (!error)
error = git_futils_mkdir(
error = git_futils_mkdir_relative(
from->ptr + info->from_prefix, info->to_root,
info->dirmode, info->mkdir_flags);
info->dirmode, info->mkdir_flags, NULL);
return error;
}
@ -934,12 +1042,18 @@ int git_futils_filestamp_check(
if (p_stat(path, &st) < 0)
return GIT_ENOTFOUND;
if (stamp->mtime == (git_time_t)st.st_mtime &&
if (stamp->mtime.tv_sec == st.st_mtime &&
#if defined(GIT_USE_NSEC)
stamp->mtime.tv_nsec == st.st_mtime_nsec &&
#endif
stamp->size == (git_off_t)st.st_size &&
stamp->ino == (unsigned int)st.st_ino)
return 0;
stamp->mtime = (git_time_t)st.st_mtime;
stamp->mtime.tv_sec = st.st_mtime;
#if defined(GIT_USE_NSEC)
stamp->mtime.tv_nsec = st.st_mtime_nsec;
#endif
stamp->size = (git_off_t)st.st_size;
stamp->ino = (unsigned int)st.st_ino;
@ -962,7 +1076,12 @@ void git_futils_filestamp_set_from_stat(
git_futils_filestamp *stamp, struct stat *st)
{
if (st) {
stamp->mtime = (git_time_t)st->st_mtime;
stamp->mtime.tv_sec = st->st_mtime;
#if defined(GIT_USE_NSEC)
stamp->mtime.tv_nsec = st->st_mtime_nsec;
#else
stamp->mtime.tv_nsec = 0;
#endif
stamp->size = (git_off_t)st->st_size;
stamp->ino = (unsigned int)st->st_ino;
} else {

View File

@ -13,6 +13,7 @@
#include "path.h"
#include "pool.h"
#include "strmap.h"
#include "oid.h"
/**
* Filebuffer methods
@ -21,7 +22,7 @@
*/
extern int git_futils_readbuffer(git_buf *obj, const char *path);
extern int git_futils_readbuffer_updated(
git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated);
git_buf *obj, const char *path, git_oid *checksum, int *updated);
extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
extern int git_futils_writebuffer(
@ -55,12 +56,9 @@ extern int git_futils_creat_locked(const char *path, const mode_t mode);
extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode);
/**
* Create a path recursively
*
* If a base parameter is being passed, it's expected to be valued with a
* path pointing to an already existing directory.
* Create a path recursively.
*/
extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);
extern int git_futils_mkdir_r(const char *path, const mode_t mode);
/**
* Flags to pass to `git_futils_mkdir`.
@ -111,20 +109,20 @@ struct git_futils_mkdir_options
* and optionally chmods the directory immediately after (or each part of the
* path if requested).
*
* @param path The path to create.
* @param path The path to create, relative to base.
* @param base Root for relative path. These directories will never be made.
* @param mode The mode to use for created directories.
* @param flags Combination of the mkdir flags above.
* @param opts Extended options, use `git_futils_mkdir` if you are not interested.
* @param opts Extended options, or null.
* @return 0 on success, else error code
*/
extern int git_futils_mkdir_ext(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts);
extern int git_futils_mkdir_relative(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts);
/**
* Create a directory or entire path. Similar to `git_futils_mkdir_withperf`
* Create a directory or entire path. Similar to `git_futils_mkdir_relative`
* without performance data.
*/
extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uint32_t flags);
extern int git_futils_mkdir(const char *path, mode_t mode, uint32_t flags);
/**
* Create all the folders required to contain
@ -312,7 +310,7 @@ extern int git_futils_fake_symlink(const char *new, const char *old);
* versions could be implemented in the future.
*/
typedef struct {
git_time_t mtime;
struct timespec mtime;
git_off_t size;
unsigned int ino;
} git_futils_filestamp;

View File

@ -56,80 +56,15 @@ static int filter_def_priority_cmp(const void *a, const void *b)
return (pa < pb) ? -1 : (pa > pb) ? 1 : 0;
}
struct filter_registry {
struct git_filter_registry {
git_rwlock lock;
git_vector filters;
};
static struct filter_registry *git__filter_registry = NULL;
static struct git_filter_registry filter_registry;
static void filter_registry_shutdown(void)
{
struct filter_registry *reg = NULL;
size_t pos;
git_filter_def *fdef;
static void git_filter_global_shutdown(void);
if ((reg = git__swap(git__filter_registry, NULL)) == NULL)
return;
git_vector_foreach(&reg->filters, pos, fdef) {
if (fdef->filter && fdef->filter->shutdown) {
fdef->filter->shutdown(fdef->filter);
fdef->initialized = false;
}
git__free(fdef->filter_name);
git__free(fdef->attrdata);
git__free(fdef);
}
git_vector_free(&reg->filters);
git__free(reg);
}
static int filter_registry_initialize(void)
{
int error = 0;
struct filter_registry *reg;
if (git__filter_registry)
return 0;
reg = git__calloc(1, sizeof(struct filter_registry));
GITERR_CHECK_ALLOC(reg);
if ((error = git_vector_init(
&reg->filters, 2, filter_def_priority_cmp)) < 0)
goto cleanup;
reg = git__compare_and_swap(&git__filter_registry, NULL, reg);
if (reg != NULL)
goto cleanup;
git__on_shutdown(filter_registry_shutdown);
/* try to register both default filters */
{
git_filter *crlf = git_crlf_filter_new();
git_filter *ident = git_ident_filter_new();
if (crlf && git_filter_register(
GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0)
crlf = NULL;
if (ident && git_filter_register(
GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
ident = NULL;
if (!crlf || !ident)
return -1;
}
return 0;
cleanup:
git_vector_free(&reg->filters);
git__free(reg);
return error;
}
static int filter_def_scan_attrs(
git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str)
@ -210,40 +145,14 @@ static int filter_def_filter_key_check(const void *key, const void *fdef)
return (key == filter) ? 0 : -1;
}
static int filter_registry_find(size_t *pos, const char *name)
{
return git_vector_search2(
pos, &git__filter_registry->filters, filter_def_name_key_check, name);
}
static git_filter_def *filter_registry_lookup(size_t *pos, const char *name)
{
git_filter_def *fdef = NULL;
if (!filter_registry_find(pos, name))
fdef = git_vector_get(&git__filter_registry->filters, *pos);
return fdef;
}
int git_filter_register(
/* Note: callers must lock the registry before calling this function */
static int filter_registry_insert(
const char *name, git_filter *filter, int priority)
{
git_filter_def *fdef;
size_t nattr = 0, nmatch = 0, alloc_len;
git_buf attrs = GIT_BUF_INIT;
assert(name && filter);
if (filter_registry_initialize() < 0)
return -1;
if (!filter_registry_find(NULL, name)) {
giterr_set(
GITERR_FILTER, "Attempt to reregister existing filter '%s'", name);
return GIT_EEXISTS;
}
if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
return -1;
@ -265,21 +174,123 @@ int git_filter_register(
filter_def_set_attrs(fdef);
if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) {
if (git_vector_insert(&filter_registry.filters, fdef) < 0) {
git__free(fdef->filter_name);
git__free(fdef->attrdata);
git__free(fdef);
return -1;
}
git_vector_sort(&git__filter_registry->filters);
git_vector_sort(&filter_registry.filters);
return 0;
}
int git_filter_global_init(void)
{
git_filter *crlf = NULL, *ident = NULL;
int error = 0;
if (git_rwlock_init(&filter_registry.lock) < 0)
return -1;
if ((error = git_vector_init(&filter_registry.filters, 2,
filter_def_priority_cmp)) < 0)
goto done;
if ((crlf = git_crlf_filter_new()) == NULL ||
filter_registry_insert(
GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 ||
(ident = git_ident_filter_new()) == NULL ||
filter_registry_insert(
GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
error = -1;
git__on_shutdown(git_filter_global_shutdown);
done:
if (error) {
git_filter_free(crlf);
git_filter_free(ident);
}
return error;
}
static void git_filter_global_shutdown(void)
{
size_t pos;
git_filter_def *fdef;
if (git_rwlock_wrlock(&filter_registry.lock) < 0)
return;
git_vector_foreach(&filter_registry.filters, pos, fdef) {
if (fdef->filter && fdef->filter->shutdown) {
fdef->filter->shutdown(fdef->filter);
fdef->initialized = false;
}
git__free(fdef->filter_name);
git__free(fdef->attrdata);
git__free(fdef);
}
git_vector_free(&filter_registry.filters);
git_rwlock_wrunlock(&filter_registry.lock);
git_rwlock_free(&filter_registry.lock);
}
/* Note: callers must lock the registry before calling this function */
static int filter_registry_find(size_t *pos, const char *name)
{
return git_vector_search2(
pos, &filter_registry.filters, filter_def_name_key_check, name);
}
/* Note: callers must lock the registry before calling this function */
static git_filter_def *filter_registry_lookup(size_t *pos, const char *name)
{
git_filter_def *fdef = NULL;
if (!filter_registry_find(pos, name))
fdef = git_vector_get(&filter_registry.filters, *pos);
return fdef;
}
int git_filter_register(
const char *name, git_filter *filter, int priority)
{
int error;
assert(name && filter);
if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
giterr_set(GITERR_OS, "failed to lock filter registry");
return -1;
}
if (!filter_registry_find(NULL, name)) {
giterr_set(
GITERR_FILTER, "attempt to reregister existing filter '%s'", name);
error = GIT_EEXISTS;
goto done;
}
error = filter_registry_insert(name, filter, priority);
done:
git_rwlock_wrunlock(&filter_registry.lock);
return error;
}
int git_filter_unregister(const char *name)
{
size_t pos;
git_filter_def *fdef;
int error = 0;
assert(name);
@ -289,12 +300,18 @@ int git_filter_unregister(const char *name)
return -1;
}
if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name);
return GIT_ENOTFOUND;
if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
giterr_set(GITERR_OS, "failed to lock filter registry");
return -1;
}
(void)git_vector_remove(&git__filter_registry->filters, pos);
if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name);
error = GIT_ENOTFOUND;
goto done;
}
git_vector_remove(&filter_registry.filters, pos);
if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
fdef->filter->shutdown(fdef->filter);
@ -305,21 +322,18 @@ int git_filter_unregister(const char *name)
git__free(fdef->attrdata);
git__free(fdef);
return 0;
done:
git_rwlock_wrunlock(&filter_registry.lock);
return error;
}
static int filter_initialize(git_filter_def *fdef)
{
int error = 0;
if (!fdef->initialized &&
fdef->filter &&
fdef->filter->initialize &&
(error = fdef->filter->initialize(fdef->filter)) < 0)
{
/* auto-unregister if initialize fails */
git_filter_unregister(fdef->filter_name);
return error;
if (!fdef->initialized && fdef->filter && fdef->filter->initialize) {
if ((error = fdef->filter->initialize(fdef->filter)) < 0)
return error;
}
fdef->initialized = true;
@ -330,17 +344,22 @@ git_filter *git_filter_lookup(const char *name)
{
size_t pos;
git_filter_def *fdef;
git_filter *filter = NULL;
if (filter_registry_initialize() < 0)
if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
giterr_set(GITERR_OS, "failed to lock filter registry");
return NULL;
}
if ((fdef = filter_registry_lookup(&pos, name)) == NULL)
return NULL;
if ((fdef = filter_registry_lookup(&pos, name)) == NULL ||
(!fdef->initialized && filter_initialize(fdef) < 0))
goto done;
if (!fdef->initialized && filter_initialize(fdef) < 0)
return NULL;
filter = fdef->filter;
return fdef->filter;
done:
git_rwlock_rdunlock(&filter_registry.lock);
return filter;
}
void git_filter_free(git_filter *filter)
@ -433,8 +452,11 @@ static int filter_list_check_attributes(
want_type = git_attr_value(want);
found_type = git_attr_value(strs[i]);
if (want_type != found_type ||
(want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i])))
if (want_type != found_type)
error = GIT_ENOTFOUND;
else if (want_type == GIT_ATTR_VALUE_T &&
strcmp(want, strs[i]) &&
strcmp(want, "*"))
error = GIT_ENOTFOUND;
}
@ -475,8 +497,10 @@ int git_filter_list__load_ext(
size_t idx;
git_filter_def *fdef;
if (filter_registry_initialize() < 0)
if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
giterr_set(GITERR_OS, "failed to lock filter registry");
return -1;
}
src.repo = repo;
src.path = path;
@ -486,7 +510,7 @@ int git_filter_list__load_ext(
if (blob)
git_oid_cpy(&src.oid, git_blob_id(blob));
git_vector_foreach(&git__filter_registry->filters, idx, fdef) {
git_vector_foreach(&filter_registry.filters, idx, fdef) {
const char **values = NULL;
void *payload = NULL;
@ -520,7 +544,7 @@ int git_filter_list__load_ext(
else {
if (!fl) {
if ((error = filter_list_new(&fl, &src)) < 0)
return error;
break;
fl->temp_buf = filter_opts->temp_buf;
}
@ -534,6 +558,8 @@ int git_filter_list__load_ext(
}
}
git_rwlock_rdunlock(&filter_registry.lock);
if (error && fl != NULL) {
git_array_clear(fl->filters);
git__free(fl);
@ -601,19 +627,27 @@ int git_filter_list_push(
{
int error = 0;
size_t pos;
git_filter_def *fdef;
git_filter_def *fdef = NULL;
git_filter_entry *fe;
assert(fl && filter);
if (git_vector_search2(
&pos, &git__filter_registry->filters,
filter_def_filter_key_check, filter) < 0) {
giterr_set(GITERR_FILTER, "Cannot use an unregistered filter");
if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
giterr_set(GITERR_OS, "failed to lock filter registry");
return -1;
}
fdef = git_vector_get(&git__filter_registry->filters, pos);
if (git_vector_search2(
&pos, &filter_registry.filters,
filter_def_filter_key_check, filter) == 0)
fdef = git_vector_get(&filter_registry.filters, pos);
git_rwlock_rdunlock(&filter_registry.lock);
if (fdef == NULL) {
giterr_set(GITERR_FILTER, "Cannot use an unregistered filter");
return -1;
}
if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
return error;

View File

@ -32,6 +32,8 @@ typedef struct {
#define GIT_FILTER_OPTIONS_INIT {0}
extern int git_filter_global_init(void);
extern void git_filter_free(git_filter *filter);
extern int git_filter_list__load_ext(

View File

@ -8,26 +8,25 @@
#include "global.h"
#include "hash.h"
#include "sysdir.h"
#include "git2/global.h"
#include "git2/sys/openssl.h"
#include "filter.h"
#include "openssl_stream.h"
#include "thread-utils.h"
#include "git2/global.h"
#include "transports/ssh.h"
#if defined(GIT_MSVC_CRTDBG)
#include "win32/w32_stack.h"
#include "win32/w32_crtdbg_stacktrace.h"
#endif
git_mutex git__mwindow_mutex;
#define MAX_SHUTDOWN_CB 8
#ifdef GIT_OPENSSL
# include <openssl/ssl.h>
SSL_CTX *git__ssl_ctx;
# ifdef GIT_THREADS
static git_mutex *openssl_locks;
# endif
#endif
static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
static git_atomic git__n_shutdown_callbacks;
static git_atomic git__n_inits;
char *git__user_agent;
void git__on_shutdown(git_global_shutdown_fn callback)
{
@ -45,118 +44,49 @@ static void git__global_state_cleanup(git_global_st *st)
st->error_t.message = NULL;
}
static void git__shutdown(void)
static int init_common(void)
{
int ret;
/* Initialize the CRT debug allocator first, before our first malloc */
#if defined(GIT_MSVC_CRTDBG)
git_win32__crtdbg_stacktrace_init();
git_win32__stack_init();
#endif
/* Initialize any other subsystems that have global state */
if ((ret = git_hash_global_init()) == 0 &&
(ret = git_sysdir_global_init()) == 0 &&
(ret = git_filter_global_init()) == 0 &&
(ret = git_transport_ssh_global_init()) == 0)
ret = git_openssl_stream_global_init();
GIT_MEMORY_BARRIER;
return ret;
}
static void shutdown_common(void)
{
int pos;
/* Shutdown subsystems that have registered */
for (pos = git_atomic_get(&git__n_shutdown_callbacks); pos > 0; pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
git_global_shutdown_fn cb = git__swap(git__shutdown_callbacks[pos - 1], NULL);
for (pos = git_atomic_get(&git__n_shutdown_callbacks);
pos > 0;
pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
git_global_shutdown_fn cb = git__swap(
git__shutdown_callbacks[pos - 1], NULL);
if (cb != NULL)
cb();
}
}
#if defined(GIT_THREADS) && defined(GIT_OPENSSL)
void openssl_locking_function(int mode, int n, const char *file, int line)
{
int lock;
git__free(git__user_agent);
GIT_UNUSED(file);
GIT_UNUSED(line);
lock = mode & CRYPTO_LOCK;
if (lock) {
git_mutex_lock(&openssl_locks[n]);
} else {
git_mutex_unlock(&openssl_locks[n]);
}
}
static void shutdown_ssl_locking(void)
{
int num_locks, i;
num_locks = CRYPTO_num_locks();
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < num_locks; ++i)
git_mutex_free(openssl_locks);
git__free(openssl_locks);
}
#endif
static void init_ssl(void)
{
#ifdef GIT_OPENSSL
long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
/* Older OpenSSL and MacOS OpenSSL doesn't have this */
#ifdef SSL_OP_NO_COMPRESSION
ssl_opts |= SSL_OP_NO_COMPRESSION;
#endif
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
/*
* Load SSLv{2,3} and TLSv1 so that we can talk with servers
* which use the SSL hellos, which are often used for
* compatibility. We then disable SSL so we only allow OpenSSL
* to speak TLSv1 to perform the encryption itself.
*/
git__ssl_ctx = SSL_CTX_new(SSLv23_method());
SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) {
SSL_CTX_free(git__ssl_ctx);
git__ssl_ctx = NULL;
}
#endif
}
/**
* This function aims to clean-up the SSL context which
* we allocated.
*/
static void uninit_ssl(void)
{
#ifdef GIT_OPENSSL
if (git__ssl_ctx) {
SSL_CTX_free(git__ssl_ctx);
git__ssl_ctx = NULL;
}
#endif
}
int git_openssl_set_locking(void)
{
#ifdef GIT_OPENSSL
# ifdef GIT_THREADS
int num_locks, i;
num_locks = CRYPTO_num_locks();
openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
GITERR_CHECK_ALLOC(openssl_locks);
for (i = 0; i < num_locks; i++) {
if (git_mutex_init(&openssl_locks[i]) != 0) {
giterr_set(GITERR_SSL, "failed to initialize openssl locks");
return -1;
}
}
CRYPTO_set_locking_callback(openssl_locking_function);
git__on_shutdown(shutdown_ssl_locking);
return 0;
# else
giterr_set(GITERR_THREAD, "libgit2 as not built with threads");
return -1;
# endif
#else
giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
return -1;
#if defined(GIT_MSVC_CRTDBG)
git_win32__crtdbg_stacktrace_cleanup();
git_win32__stack_cleanup();
#endif
}
@ -204,14 +134,13 @@ static int synchronized_threads_init(void)
int error;
_tls_index = TlsAlloc();
win32_pthread_initialize();
if (git_mutex_init(&git__mwindow_mutex))
return -1;
/* Initialize any other subsystems that have global state */
if ((error = git_hash_global_init()) >= 0)
error = git_sysdir_global_init();
win32_pthread_initialize();
error = init_common();
return error;
}
@ -235,17 +164,6 @@ int git_libgit2_init(void)
return ret;
}
static void synchronized_threads_shutdown(void)
{
/* Shut down any subsystems that have global state */
git__shutdown();
git__free_tls_data();
TlsFree(_tls_index);
git_mutex_free(&git__mwindow_mutex);
}
int git_libgit2_shutdown(void)
{
int ret;
@ -254,8 +172,14 @@ int git_libgit2_shutdown(void)
while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
/* Only do work on a 1 -> 0 transition of the refcount */
if ((ret = git_atomic_dec(&git__n_inits)) == 0)
synchronized_threads_shutdown();
if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
shutdown_common();
git__free_tls_data();
TlsFree(_tls_index);
git_mutex_free(&git__mwindow_mutex);
}
/* Exit the lock */
InterlockedExchange(&_mutex, 0);
@ -265,18 +189,19 @@ int git_libgit2_shutdown(void)
git_global_st *git__global_state(void)
{
void *ptr;
git_global_st *ptr;
assert(git_atomic_get(&git__n_inits) > 0);
if ((ptr = TlsGetValue(_tls_index)) != NULL)
return ptr;
ptr = git__malloc(sizeof(git_global_st));
ptr = git__calloc(1, sizeof(git_global_st));
if (!ptr)
return NULL;
memset(ptr, 0x0, sizeof(git_global_st));
git_buf_init(&ptr->error_buf, 0);
TlsSetValue(_tls_index, ptr);
return ptr;
}
@ -313,25 +238,18 @@ static void init_once(void)
{
if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
return;
pthread_key_create(&_tls_key, &cb__free_status);
/* Initialize any other subsystems that have global state */
if ((init_error = git_hash_global_init()) >= 0)
init_error = git_sysdir_global_init();
/* OpenSSL needs to be initialized from the main thread */
init_ssl();
GIT_MEMORY_BARRIER;
init_error = init_common();
}
int git_libgit2_init(void)
{
int ret;
pthread_once(&_once_init, init_once);
ret = git_atomic_inc(&git__n_inits);
pthread_once(&_once_init, init_once);
return init_error ? init_error : ret;
}
@ -346,8 +264,7 @@ int git_libgit2_shutdown(void)
return ret;
/* Shut down any subsystems that have global state */
git__shutdown();
uninit_ssl();
shutdown_common();
ptr = pthread_getspecific(_tls_key);
pthread_setspecific(_tls_key, NULL);
@ -364,18 +281,18 @@ int git_libgit2_shutdown(void)
git_global_st *git__global_state(void)
{
void *ptr;
git_global_st *ptr;
assert(git_atomic_get(&git__n_inits) > 0);
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
return ptr;
ptr = git__malloc(sizeof(git_global_st));
ptr = git__calloc(1, sizeof(git_global_st));
if (!ptr)
return NULL;
memset(ptr, 0x0, sizeof(git_global_st));
git_buf_init(&ptr->error_buf, 0);
pthread_setspecific(_tls_key, ptr);
return ptr;
}
@ -386,14 +303,16 @@ static git_global_st __state;
int git_libgit2_init(void)
{
static int ssl_inited = 0;
int ret;
if (!ssl_inited) {
init_ssl();
ssl_inited = 1;
}
/* Only init SSL the first time */
if ((ret = git_atomic_inc(&git__n_inits)) != 1)
return ret;
return git_atomic_inc(&git__n_inits);
if ((ret = init_common()) < 0)
return ret;
return 1;
}
int git_libgit2_shutdown(void)
@ -401,14 +320,12 @@ int git_libgit2_shutdown(void)
int ret;
/* Shut down any subsystems that have global state */
if ((ret = git_atomic_dec(&git__n_inits)) != 0)
return ret;
if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
shutdown_common();
git__global_state_cleanup(&__state);
}
git__shutdown();
git__global_state_cleanup(&__state);
uninit_ssl();
return 0;
return ret;
}
git_global_st *git__global_state(void)

View File

@ -14,6 +14,7 @@
typedef struct {
git_error *last_error;
git_error error_t;
git_buf error_buf;
char oid_fmt[GIT_OID_HEXSZ+1];
} git_global_st;
@ -34,4 +35,6 @@ extern void git__on_shutdown(git_global_shutdown_fn callback);
extern void git__free_tls_data(void);
extern const char *git_libgit2__user_agent(void);
#endif

93
src/idxmap.h Normal file
View File

@ -0,0 +1,93 @@
/*
* 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_idxmap_h__
#define INCLUDE_idxmap_h__
#include <ctype.h>
#include "common.h"
#include "git2/index.h"
#define kmalloc git__malloc
#define kcalloc git__calloc
#define krealloc git__realloc
#define kreallocarray git__reallocarray
#define kfree git__free
#include "khash.h"
__KHASH_TYPE(idx, const git_index_entry *, git_index_entry *)
__KHASH_TYPE(idxicase, const git_index_entry *, git_index_entry *)
typedef khash_t(idx) git_idxmap;
typedef khash_t(idxicase) git_idxmap_icase;
typedef khiter_t git_idxmap_iter;
/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */
static kh_inline khint_t idxentry_hash(const git_index_entry *e)
{
const char *s = e->path;
khint_t h = (khint_t)git__tolower(*s);
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s);
return h + GIT_IDXENTRY_STAGE(e);
}
#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0)
#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0)
#define GIT__USE_IDXMAP \
__KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal)
#define GIT__USE_IDXMAP_ICASE \
__KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal)
#define git_idxmap_alloc(hp) \
((*(hp) = kh_init(idx)) == NULL) ? giterr_set_oom(), -1 : 0
#define git_idxmap_icase_alloc(hp) \
((*(hp) = kh_init(idxicase)) == NULL) ? giterr_set_oom(), -1 : 0
#define git_idxmap_insert(h, key, val, rval) do { \
khiter_t __pos = kh_put(idx, h, key, &rval); \
if (rval >= 0) { \
if (rval == 0) kh_key(h, __pos) = key; \
kh_val(h, __pos) = val; \
} } while (0)
#define git_idxmap_icase_insert(h, key, val, rval) do { \
khiter_t __pos = kh_put(idxicase, h, key, &rval); \
if (rval >= 0) { \
if (rval == 0) kh_key(h, __pos) = key; \
kh_val(h, __pos) = val; \
} } while (0)
#define git_idxmap_lookup_index(h, k) kh_get(idx, h, k)
#define git_idxmap_icase_lookup_index(h, k) kh_get(idxicase, h, k)
#define git_idxmap_value_at(h, idx) kh_val(h, idx)
#define git_idxmap_valid_index(h, idx) (idx != kh_end(h))
#define git_idxmap_has_data(h, idx) kh_exist(h, idx)
#define git_idxmap_resize(h,s) kh_resize(idx, h, s)
#define git_idxmap_free(h) kh_destroy(idx, h), h = NULL
#define git_idxmap_clear(h) kh_clear(idx, h)
#define git_idxmap_delete_at(h, id) kh_del(idx, h, id)
#define git_idxmap_icase_delete_at(h, id) kh_del(idxicase, h, id)
#define git_idxmap_delete(h, key) do { \
khiter_t __pos = git_idxmap_lookup_index(h, key); \
if (git_idxmap_valid_index(h, __pos)) \
git_idxmap_delete_at(h, __pos); } while (0)
#define git_idxmap_icase_delete(h, key) do { \
khiter_t __pos = git_idxmap_icase_lookup_index(h, key); \
if (git_idxmap_valid_index(h, __pos)) \
git_idxmap_icase_delete_at(h, __pos); } while (0)
#define git_idxmap_begin kh_begin
#define git_idxmap_end kh_end
#endif

View File

@ -89,18 +89,20 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
}
/*
* If we're dealing with a directory (which we know via the
* strchr() check) we want to use 'dirname/<star>' as the
* pattern so p_fnmatch() honours FNM_PATHNAME
* When dealing with a directory, we add '/<star>' so
* p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR
* alone isn't enough as that's also set for nagations, so we
* need to check that NEGATIVE is off.
*/
git_buf_clear(&buf);
if (rule->containing_dir) {
git_buf_puts(&buf, rule->containing_dir);
}
if (!strchr(rule->pattern, '*'))
error = git_buf_printf(&buf, "%s/*", rule->pattern);
else
error = git_buf_puts(&buf, rule->pattern);
error = git_buf_puts(&buf, rule->pattern);
if ((rule->flags & (GIT_ATTR_FNMATCH_LEADINGDIR | GIT_ATTR_FNMATCH_NEGATIVE)) == GIT_ATTR_FNMATCH_LEADINGDIR)
error = git_buf_PUTS(&buf, "/*");
if (error < 0)
goto out;

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
#include "fileops.h"
#include "filebuf.h"
#include "vector.h"
#include "idxmap.h"
#include "tree-cache.h"
#include "git2/odb.h"
#include "git2/index.h"
@ -25,8 +26,8 @@ struct git_index {
git_oid checksum; /* checksum at the end of the file */
git_vector entries;
git_idxmap *entries_map;
git_mutex lock; /* lock held while entries is being changed */
git_vector deleted; /* deleted entries if readers > 0 */
git_atomic readers; /* number of active iterators */
@ -63,6 +64,45 @@ extern int git_index_entry_icmp(const void *a, const void *b);
extern int git_index_entry_srch(const void *a, const void *b);
extern int git_index_entry_isrch(const void *a, const void *b);
/* Index time handling functions */
GIT_INLINE(bool) git_index_time_eq(const git_index_time *one, const git_index_time *two)
{
if (one->seconds != two->seconds)
return false;
#ifdef GIT_USE_NSEC
if (one->nanoseconds != two->nanoseconds)
return false;
#endif
return true;
}
/*
* Test if the given index time is newer than the given existing index entry.
* If the timestamps are exactly equivalent, then the given index time is
* considered "racily newer" than the existing index entry.
*/
GIT_INLINE(bool) git_index_entry_newer_than_index(
const git_index_entry *entry, git_index *index)
{
/* If we never read the index, we can't have this race either */
if (!index || index->stamp.mtime.tv_sec == 0)
return false;
/* If the timestamp is the same or newer than the index, it's racy */
#if defined(GIT_USE_NSEC)
if ((int32_t)index->stamp.mtime.tv_sec < entry->mtime.seconds)
return true;
else if ((int32_t)index->stamp.mtime.tv_sec > entry->mtime.seconds)
return false;
else
return (uint32_t)index->stamp.mtime.tv_nsec <= entry->mtime.nanoseconds;
#else
return ((int32_t)index->stamp.mtime.tv_sec) <= entry->mtime.seconds;
#endif
}
/* Search index for `path`, returning GIT_ENOTFOUND if it does not exist
* (but not setting an error message).
*
@ -72,6 +112,8 @@ extern int git_index_entry_isrch(const void *a, const void *b);
extern int git_index__find_pos(
size_t *at_pos, git_index *index, const char *path, size_t path_len, int stage);
extern int git_index__fill(git_index *index, const git_vector *source_entries);
extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
extern unsigned int git_index__create_mode(unsigned int mode);

View File

@ -31,14 +31,22 @@
(P)->base.cb = &(P)->cb; \
ITERATOR_SET_CB(P,NAME_LC); \
(P)->base.repo = (REPO); \
(P)->base.start = start ? git__strdup(start) : NULL; \
(P)->base.end = end ? git__strdup(end) : NULL; \
if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
(P)->base.start = options && options->start ? \
git__strdup(options->start) : NULL; \
(P)->base.end = options && options->end ? \
git__strdup(options->end) : NULL; \
if ((options && options->start && !(P)->base.start) || \
(options && options->end && !(P)->base.end)) { \
git__free(P); return -1; } \
(P)->base.strcomp = git__strcmp; \
(P)->base.strncomp = git__strncmp; \
(P)->base.prefixcomp = git__prefixcmp; \
(P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \
(P)->base.flags = options ? options->flags & ~ITERATOR_CASE_FLAGS : 0; \
if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
(P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
if (options && options->pathlist.count && \
iterator_pathlist__init(&P->base, &options->pathlist) < 0) { \
git__free(P); return -1; } \
} while (0)
#define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
@ -56,6 +64,139 @@
(iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
typedef enum {
ITERATOR_PATHLIST_NONE = 0,
ITERATOR_PATHLIST_MATCH = 1,
ITERATOR_PATHLIST_MATCH_DIRECTORY = 2,
ITERATOR_PATHLIST_MATCH_CHILD = 3,
} iterator_pathlist__match_t;
static int iterator_pathlist__init(git_iterator *iter, git_strarray *pathspec)
{
size_t i;
if (git_vector_init(&iter->pathlist, pathspec->count,
(git_vector_cmp)iter->strcomp) < 0)
return -1;
for (i = 0; i < pathspec->count; i++) {
if (!pathspec->strings[i])
continue;
if (git_vector_insert(&iter->pathlist, pathspec->strings[i]) < 0)
return -1;
}
git_vector_sort(&iter->pathlist);
return 0;
}
static iterator_pathlist__match_t iterator_pathlist__match(
git_iterator *iter, const char *path, size_t path_len)
{
const char *p;
size_t idx;
int error;
error = git_vector_bsearch2(&idx, &iter->pathlist,
(git_vector_cmp)iter->strcomp, path);
if (error == 0)
return ITERATOR_PATHLIST_MATCH;
/* at this point, the path we're examining may be a directory (though we
* don't know that yet, since we're avoiding a stat unless it's necessary)
* so see if the pathlist contains a file beneath this directory.
*/
while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) {
if (iter->prefixcomp(p, path) != 0)
break;
/* an exact match would have been matched by the bsearch above */
assert(p[path_len]);
/* is this a literal directory entry (eg `foo/`) or a file beneath */
if (p[path_len] == '/') {
return (p[path_len+1] == '\0') ?
ITERATOR_PATHLIST_MATCH_DIRECTORY :
ITERATOR_PATHLIST_MATCH_CHILD;
}
if (p[path_len] > '/')
break;
idx++;
}
return ITERATOR_PATHLIST_NONE;
}
static void iterator_pathlist_walk__reset(git_iterator *iter)
{
iter->pathlist_walk_idx = 0;
}
/* walker for the index iterator that allows it to walk the sorted pathlist
* entries alongside the sorted index entries. the `iter->pathlist_walk_idx`
* stores the starting position for subsequent calls, the position is advanced
* along with the index iterator, with a special case for handling directories
* in the pathlist that are specified without trailing '/'. (eg, `foo`).
* we do not advance over these entries until we're certain that the index
* iterator will not ask us for a file beneath that directory (eg, `foo/bar`).
*/
static bool iterator_pathlist_walk__contains(git_iterator *iter, const char *path)
{
size_t i;
char *p;
size_t p_len;
int cmp;
for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) {
p = iter->pathlist.contents[i];
p_len = strlen(p);
/* see if the pathlist entry is a prefix of this path */
cmp = iter->strncomp(p, path, p_len);
/* this pathlist entry sorts before the given path, try the next */
if (!p_len || cmp < 0)
iter->pathlist_walk_idx++;
/* this pathlist sorts after the given path, no match. */
else if (cmp > 0)
return false;
/* match! an exact match (`foo` vs `foo`), the path is a child of an
* explicit directory in the pathlist (`foo/` vs `foo/bar`) or the path
* is a child of an entry in the pathlist (`foo` vs `foo/bar`)
*/
else if (path[p_len] == '\0' || p[p_len - 1] == '/' || path[p_len] == '/')
return true;
/* only advance the start index for future callers if we know that we
* will not see a child of this path. eg, a pathlist entry `foo` is
* a prefix for `foo.txt` and `foo/bar`. don't advance the start
* pathlist index when we see `foo.txt` or we would miss a subsequent
* inspection of `foo/bar`. only advance when there are no more
* potential children.
*/
else if (path[p_len] > '/')
iter->pathlist_walk_idx++;
}
return false;
}
static void iterator_pathlist__update_ignore_case(git_iterator *iter)
{
git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp);
git_vector_sort(&iter->pathlist);
iter->pathlist_walk_idx = 0;
}
static int iterator__reset_range(
git_iterator *iter, const char *start, const char *end)
{
@ -82,7 +223,8 @@ static int iterator__update_ignore_case(
git_iterator *iter,
git_iterator_flag_t flags)
{
int error = 0, ignore_case = -1;
bool ignore_case;
int error;
if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
ignore_case = true;
@ -91,19 +233,29 @@ static int iterator__update_ignore_case(
else {
git_index *index;
if (!(error = git_repository_index__weakptr(&index, iter->repo)))
ignore_case = (index->ignore_case != false);
if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0)
return error;
ignore_case = (index->ignore_case == 1);
}
if (ignore_case > 0)
if (ignore_case) {
iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE);
else if (ignore_case == 0)
iter->strcomp = git__strcasecmp;
iter->strncomp = git__strncasecmp;
iter->prefixcomp = git__prefixcmp_icase;
} else {
iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE);
iter->prefixcomp = iterator__ignore_case(iter) ?
git__prefixcmp_icase : git__prefixcmp;
iter->strcomp = git__strcmp;
iter->strncomp = git__strncmp;
iter->prefixcomp = git__prefixcmp;
}
return error;
iterator_pathlist__update_ignore_case(iter);
return 0;
}
GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry)
@ -149,9 +301,7 @@ typedef struct {
int git_iterator_for_nothing(
git_iterator **iter,
git_iterator_flag_t flags,
const char *start,
const char *end)
git_iterator_options *options)
{
empty_iterator *i = git__calloc(1, sizeof(empty_iterator));
GITERR_CHECK_ALLOC(i);
@ -162,7 +312,7 @@ int git_iterator_for_nothing(
ITERATOR_BASE_INIT(i, empty, EMPTY, NULL);
if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
if (options && (options->flags & GIT_ITERATOR_IGNORE_CASE) != 0)
i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
*iter = (git_iterator *)i;
@ -201,7 +351,6 @@ typedef struct {
int path_ambiguities;
bool path_has_filename;
bool entry_is_current;
int (*strncomp)(const char *a, const char *b, size_t sz);
} tree_iterator;
static char *tree_iterator__current_filename(
@ -271,7 +420,7 @@ static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
return git_path_cmp(
tf->start, tf->startlen, false,
te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE,
((tree_iterator *)p)->strncomp);
((git_iterator *)p)->strncomp);
}
static bool tree_iterator__move_to_next(
@ -303,7 +452,7 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
for (; tf->next < tf->n_entries; tf->next++, last = te) {
te = tf->entries[tf->next]->te;
if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
if (last && tree_iterator__te_cmp(last, te, ti->base.strncomp))
break;
/* try to load trees for items in [current,next) range */
@ -409,6 +558,8 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
{
tree_iterator_frame *tf = ti->head;
assert(tf);
if (!tf->up)
return false;
@ -418,7 +569,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
tree_iterator__move_to_next(ti, tf);
if (!final) { /* if final, don't bother to clean up */
git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries);
// TODO: maybe free the pool so far?
git_buf_rtruncate_at_char(&ti->path, '/');
}
@ -432,6 +583,8 @@ static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
if (!final) {
assert(ti->head);
ti->head->current = to_end ? ti->head->n_entries : 0;
ti->path_ambiguities = 0;
git_buf_clear(&ti->path);
@ -468,7 +621,7 @@ static int tree_iterator__update_entry(tree_iterator *ti)
return 0;
}
static int tree_iterator__current(
static int tree_iterator__current_internal(
const git_index_entry **entry, git_iterator *self)
{
int error;
@ -491,41 +644,32 @@ static int tree_iterator__current(
return 0;
}
static int tree_iterator__advance_into(
const git_index_entry **entry, git_iterator *self)
static int tree_iterator__advance_into_internal(git_iterator *self)
{
int error = 0;
tree_iterator *ti = (tree_iterator *)self;
iterator__clear_entry(entry);
if (tree_iterator__at_tree(ti))
error = tree_iterator__push_frame(ti);
if (!error && entry)
error = tree_iterator__current(entry, self);
return error;
}
static int tree_iterator__advance(
const git_index_entry **entry, git_iterator *self)
static int tree_iterator__advance_internal(git_iterator *self)
{
int error;
tree_iterator *ti = (tree_iterator *)self;
tree_iterator_frame *tf = ti->head;
iterator__clear_entry(entry);
if (tf->current >= tf->n_entries)
return GIT_ITEROVER;
if (!iterator__has_been_accessed(ti))
return tree_iterator__current(entry, self);
return 0;
if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
tree_iterator__at_tree(ti))
return tree_iterator__advance_into(entry, self);
return tree_iterator__advance_into_internal(self);
if (ti->path_has_filename) {
git_buf_rtruncate_at_char(&ti->path, '/');
@ -534,7 +678,7 @@ static int tree_iterator__advance(
/* scan forward and up, advancing in frame or popping frame when done */
while (!tree_iterator__move_to_next(ti, tf) &&
tree_iterator__pop_frame(ti, false))
tree_iterator__pop_frame(ti, false))
tf = ti->head;
/* find next and load trees */
@ -543,7 +687,63 @@ static int tree_iterator__advance(
/* deal with include_trees / auto_expand as needed */
if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
return tree_iterator__advance_into(entry, self);
return tree_iterator__advance_into_internal(self);
return 0;
}
static int tree_iterator__current(
const git_index_entry **out, git_iterator *self)
{
const git_index_entry *entry = NULL;
iterator_pathlist__match_t m;
int error;
do {
if ((error = tree_iterator__current_internal(&entry, self)) < 0)
return error;
if (self->pathlist.length) {
m = iterator_pathlist__match(
self, entry->path, strlen(entry->path));
if (m != ITERATOR_PATHLIST_MATCH) {
if ((error = tree_iterator__advance_internal(self)) < 0)
return error;
entry = NULL;
}
}
} while (!entry);
if (out)
*out = entry;
return error;
}
static int tree_iterator__advance(
const git_index_entry **entry, git_iterator *self)
{
int error = tree_iterator__advance_internal(self);
iterator__clear_entry(entry);
if (error < 0)
return error;
return tree_iterator__current(entry, self);
}
static int tree_iterator__advance_into(
const git_index_entry **entry, git_iterator *self)
{
int error = tree_iterator__advance_into_internal(self);
iterator__clear_entry(entry);
if (error < 0)
return error;
return tree_iterator__current(entry, self);
}
@ -577,10 +777,12 @@ static void tree_iterator__free(git_iterator *self)
{
tree_iterator *ti = (tree_iterator *)self;
tree_iterator__pop_all(ti, true, false);
if (ti->head) {
tree_iterator__pop_all(ti, true, false);
git_tree_free(ti->head->entries[0]->tree);
git__free(ti->head);
}
git_tree_free(ti->head->entries[0]->tree);
git__free(ti->head);
git_pool_clear(&ti->pool);
git_buf_free(&ti->path);
}
@ -607,15 +809,13 @@ static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree)
int git_iterator_for_tree(
git_iterator **iter,
git_tree *tree,
git_iterator_flag_t flags,
const char *start,
const char *end)
git_iterator_options *options)
{
int error;
tree_iterator *ti;
if (tree == NULL)
return git_iterator_for_nothing(iter, flags, start, end);
return git_iterator_for_nothing(iter, options);
if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
return error;
@ -625,12 +825,12 @@ int git_iterator_for_tree(
ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
if ((error = iterator__update_ignore_case((git_iterator *)ti, options ? options->flags : 0)) < 0)
goto fail;
ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp;
if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 ||
(error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
git_pool_init(&ti->pool, sizeof(tree_iterator_entry));
if ((error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
(error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
goto fail;
@ -650,6 +850,8 @@ typedef struct {
git_vector entries;
git_vector_cmp entry_srch;
size_t current;
/* when limiting with a pathlist, this is the current index into it */
size_t pathlist_idx;
/* when not in autoexpand mode, use these to represent "tree" state */
git_buf partial;
size_t partial_pos;
@ -669,15 +871,35 @@ static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
return ie;
}
static const git_index_entry *index_iterator__advance_over_conflicts(index_iterator *ii)
static const git_index_entry *index_iterator__advance_over_unwanted(
index_iterator *ii)
{
const git_index_entry *ie = index_iterator__index_entry(ii);
bool match;
if (!iterator__include_conflicts(ii)) {
while (ie && git_index_entry_is_conflict(ie)) {
while (ie) {
if (!iterator__include_conflicts(ii) &&
git_index_entry_is_conflict(ie)) {
ii->current++;
ie = index_iterator__index_entry(ii);
continue;
}
/* if we have a pathlist, this entry's path must be in it to be
* returned. walk the pathlist in unison with the index to
* compare paths.
*/
if (ii->base.pathlist.length) {
match = iterator_pathlist_walk__contains(&ii->base, ie->path);
if (!match) {
ii->current++;
ie = index_iterator__index_entry(ii);
continue;
}
}
break;
}
return ie;
@ -706,7 +928,7 @@ static void index_iterator__next_prefix_tree(index_iterator *ii)
static int index_iterator__first_prefix_tree(index_iterator *ii)
{
const git_index_entry *ie = index_iterator__advance_over_conflicts(ii);
const git_index_entry *ie = index_iterator__advance_over_unwanted(ii);
const char *scan, *prior, *slash;
if (!ie || !iterator__include_trees(ii))
@ -825,11 +1047,16 @@ static int index_iterator__reset(
ii->current = 0;
iterator_pathlist_walk__reset(self);
/* if we're given a start prefix, find it; if we're given a pathlist, find
* the first of those. start at the later of the two.
*/
if (ii->base.start)
git_index_snapshot_find(
&ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0);
if ((ie = index_iterator__advance_over_conflicts(ii)) == NULL)
if ((ie = index_iterator__advance_over_unwanted(ii)) == NULL)
return 0;
if (git_buf_sets(&ii->partial, ie->path) < 0)
@ -859,10 +1086,9 @@ static void index_iterator__free(git_iterator *self)
int git_iterator_for_index(
git_iterator **iter,
git_repository *repo,
git_index *index,
git_iterator_flag_t flags,
const char *start,
const char *end)
git_iterator_options *options)
{
int error = 0;
index_iterator *ii = git__calloc(1, sizeof(index_iterator));
@ -874,9 +1100,9 @@ int git_iterator_for_index(
}
ii->index = index;
ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
ITERATOR_BASE_INIT(ii, index, INDEX, repo);
if ((error = iterator__update_ignore_case((git_iterator *)ii, flags)) < 0) {
if ((error = iterator__update_ignore_case((git_iterator *)ii, options ? options->flags : 0)) < 0) {
git_iterator_free((git_iterator *)ii);
return error;
}
@ -916,6 +1142,7 @@ struct fs_iterator {
size_t root_len;
uint32_t dirload_flags;
int depth;
iterator_pathlist__match_t pathlist_match;
int (*enter_dir_cb)(fs_iterator *self);
int (*leave_dir_cb)(fs_iterator *self);
@ -926,6 +1153,7 @@ struct fs_iterator {
typedef struct {
struct stat st;
iterator_pathlist__match_t pathlist_match;
size_t path_len;
char path[GIT_FLEX_ARRAY];
} fs_iterator_path_with_stat;
@ -1007,28 +1235,20 @@ static void fs_iterator__seek_frame_start(
ff->index = 0;
}
static int dirload_with_stat(
const char *dirpath,
size_t prefix_len,
unsigned int flags,
const char *start_stat,
const char *end_stat,
git_vector *contents)
static int dirload_with_stat(git_vector *contents, fs_iterator *fi)
{
git_path_diriter diriter = GIT_PATH_DIRITER_INIT;
const char *path;
int (*strncomp)(const char *a, const char *b, size_t sz);
size_t start_len = start_stat ? strlen(start_stat) : 0;
size_t end_len = end_stat ? strlen(end_stat) : 0;
size_t start_len = fi->base.start ? strlen(fi->base.start) : 0;
size_t end_len = fi->base.end ? strlen(fi->base.end) : 0;
fs_iterator_path_with_stat *ps;
size_t path_len, cmp_len, ps_size;
iterator_pathlist__match_t pathlist_match = ITERATOR_PATHLIST_MATCH;
int error;
strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ?
git__strncasecmp : git__strncmp;
/* Any error here is equivalent to the dir not existing, skip over it */
if ((error = git_path_diriter_init(&diriter, dirpath, flags)) < 0) {
if ((error = git_path_diriter_init(
&diriter, fi->path.ptr, fi->dirload_flags)) < 0) {
error = GIT_ENOTFOUND;
goto done;
}
@ -1037,18 +1257,31 @@ static int dirload_with_stat(
if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0)
goto done;
assert(path_len > prefix_len);
assert(path_len > fi->root_len);
/* remove the prefix if requested */
path += prefix_len;
path_len -= prefix_len;
path += fi->root_len;
path_len -= fi->root_len;
/* skip if before start_stat or after end_stat */
cmp_len = min(start_len, path_len);
if (cmp_len && strncomp(path, start_stat, cmp_len) < 0)
if (cmp_len && fi->base.strncomp(path, fi->base.start, cmp_len) < 0)
continue;
/* skip if after end_stat */
cmp_len = min(end_len, path_len);
if (cmp_len && strncomp(path, end_stat, cmp_len) > 0)
if (cmp_len && fi->base.strncomp(path, fi->base.end, cmp_len) > 0)
continue;
/* if we have a pathlist that we're limiting to, examine this path.
* if the frame has already deemed us inside the path (eg, we're in
* `foo/bar` and the pathlist previously was detected to say `foo/`)
* then simply continue. otherwise, examine the pathlist looking for
* this path or children of this path.
*/
if (fi->base.pathlist.length &&
fi->pathlist_match != ITERATOR_PATHLIST_MATCH &&
fi->pathlist_match != ITERATOR_PATHLIST_MATCH_DIRECTORY &&
!(pathlist_match = iterator_pathlist__match(&fi->base, path, path_len)))
continue;
/* Make sure to append two bytes, one for the path's null
@ -1062,6 +1295,8 @@ static int dirload_with_stat(
memcpy(ps->path, path, path_len);
/* TODO: don't stat if assume unchanged for this path */
if ((error = git_path_diriter_stat(&ps->st, &diriter)) < 0) {
if (error == GIT_ENOTFOUND) {
/* file was removed between readdir and lstat */
@ -1069,6 +1304,12 @@ static int dirload_with_stat(
continue;
}
if (pathlist_match == ITERATOR_PATHLIST_MATCH_DIRECTORY) {
/* were looking for a directory, but this is a file */
git__free(ps);
continue;
}
/* Treat the file as unreadable if we get any other error */
memset(&ps->st, 0, sizeof(ps->st));
ps->st.st_mode = GIT_FILEMODE_UNREADABLE;
@ -1085,6 +1326,11 @@ static int dirload_with_stat(
continue;
}
/* record whether this path was explicitly found in the path list
* or whether we're only examining it because something beneath it
* is in the path list.
*/
ps->pathlist_match = pathlist_match;
git_vector_insert(contents, ps);
}
@ -1114,13 +1360,11 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
ff = fs_iterator__alloc_frame(fi);
GITERR_CHECK_ALLOC(ff);
error = dirload_with_stat(
fi->path.ptr, fi->root_len, fi->dirload_flags,
fi->base.start, fi->base.end, &ff->entries);
error = dirload_with_stat(&ff->entries, fi);
if (error < 0) {
git_error_state last_error = { 0 };
giterr_capture(&last_error, error);
giterr_state_capture(&last_error, error);
/* these callbacks may clear the error message */
fs_iterator__free_frame(ff);
@ -1128,7 +1372,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
/* next time return value we skipped to */
fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
return giterr_restore(&last_error);
return giterr_state_restore(&last_error);
}
if (ff->entries.length == 0) {
@ -1196,19 +1440,14 @@ static int fs_iterator__advance_into(
return error;
}
static int fs_iterator__advance_over(
const git_index_entry **entry, git_iterator *self)
static void fs_iterator__advance_over_internal(git_iterator *self)
{
int error = 0;
fs_iterator *fi = (fs_iterator *)self;
fs_iterator_frame *ff;
fs_iterator_path_with_stat *next;
if (entry != NULL)
*entry = NULL;
while (fi->entry.path != NULL) {
ff = fi->stack;
ff = fi->stack;
next = git_vector_get(&ff->entries, ++ff->index);
if (next != NULL)
@ -1216,8 +1455,19 @@ static int fs_iterator__advance_over(
fs_iterator__pop_frame(fi, ff, false);
}
}
error = fs_iterator__update_entry(fi);
static int fs_iterator__advance_over(
const git_index_entry **entry, git_iterator *self)
{
int error;
if (entry != NULL)
*entry = NULL;
fs_iterator__advance_over_internal(self);
error = fs_iterator__update_entry((fs_iterator *)self);
if (!error && entry != NULL)
error = fs_iterator__current(entry, self);
@ -1294,40 +1544,50 @@ static int fs_iterator__update_entry(fs_iterator *fi)
{
fs_iterator_path_with_stat *ps;
memset(&fi->entry, 0, sizeof(fi->entry));
while (true) {
memset(&fi->entry, 0, sizeof(fi->entry));
if (!fi->stack)
return GIT_ITEROVER;
if (!fi->stack)
return GIT_ITEROVER;
ps = git_vector_get(&fi->stack->entries, fi->stack->index);
if (!ps)
return GIT_ITEROVER;
ps = git_vector_get(&fi->stack->entries, fi->stack->index);
if (!ps)
return GIT_ITEROVER;
git_buf_truncate(&fi->path, fi->root_len);
if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
return -1;
git_buf_truncate(&fi->path, fi->root_len);
if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
return -1;
if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
return GIT_ITEROVER;
if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
return GIT_ITEROVER;
fi->entry.path = ps->path;
git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
fi->entry.path = ps->path;
fi->pathlist_match = ps->pathlist_match;
git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
/* need different mode here to keep directories during iteration */
fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
/* need different mode here to keep directories during iteration */
fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
/* allow wrapper to check/update the entry (can force skip) */
if (fi->update_entry_cb &&
fi->update_entry_cb(fi) == GIT_ENOTFOUND)
return fs_iterator__advance_over(NULL, (git_iterator *)fi);
/* allow wrapper to check/update the entry (can force skip) */
if (fi->update_entry_cb &&
fi->update_entry_cb(fi) == GIT_ENOTFOUND) {
fs_iterator__advance_over_internal(&fi->base);
continue;
}
/* if this is a tree and trees aren't included, then skip */
if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
if (error != GIT_ENOTFOUND)
return error;
giterr_clear();
return fs_iterator__advance_over(NULL, (git_iterator *)fi);
/* if this is a tree and trees aren't included, then skip */
if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
int error = fs_iterator__advance_into(NULL, &fi->base);
if (error != GIT_ENOTFOUND)
return error;
giterr_clear();
fs_iterator__advance_over_internal(&fi->base);
continue;
}
break;
}
return 0;
@ -1343,6 +1603,7 @@ static int fs_iterator__initialize(
return -1;
}
fi->root_len = fi->path.size;
fi->pathlist_match = ITERATOR_PATHLIST_MATCH_CHILD;
fi->dirload_flags =
(iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
@ -1366,16 +1627,14 @@ static int fs_iterator__initialize(
int git_iterator_for_filesystem(
git_iterator **out,
const char *root,
git_iterator_flag_t flags,
const char *start,
const char *end)
git_iterator_options *options)
{
fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
GITERR_CHECK_ALLOC(fi);
ITERATOR_BASE_INIT(fi, fs, FS, NULL);
if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
if (options && (options->flags & GIT_ITERATOR_IGNORE_CASE) != 0)
fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
return fs_iterator__initialize(out, fi, root);
@ -1559,9 +1818,7 @@ int git_iterator_for_workdir_ext(
const char *repo_workdir,
git_index *index,
git_tree *tree,
git_iterator_flag_t flags,
const char *start,
const char *end)
git_iterator_options *options)
{
int error, precompose = 0;
workdir_iterator *wi;
@ -1583,7 +1840,7 @@ int git_iterator_for_workdir_ext(
wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
wi->fi.update_entry_cb = workdir_iterator__update_entry;
if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
if ((error = iterator__update_ignore_case((git_iterator *)wi, options ? options->flags : 0)) < 0 ||
(error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
{
git_iterator_free((git_iterator *)wi);
@ -1618,6 +1875,7 @@ void git_iterator_free(git_iterator *iter)
iter->cb->free(iter);
git_vector_free(&iter->pathlist);
git__free(iter->start);
git__free(iter->end);
@ -1687,7 +1945,7 @@ int git_iterator_current_parent_tree(
if (!(tf = tf->down) ||
tf->current >= tf->n_entries ||
!(te = tf->entries[tf->current]->te) ||
ti->strncomp(scan, te->filename, te->filename_len) != 0)
ti->base.strncomp(scan, te->filename, te->filename_len) != 0)
return 0;
scan += te->filename_len;
@ -1820,9 +2078,18 @@ int git_iterator_advance_over_with_status(
if (!error)
continue;
else if (error == GIT_ENOTFOUND) {
/* we entered this directory only hoping to find child matches to
* our pathlist (eg, this is `foo` and we had a pathlist entry for
* `foo/bar`). it should not be ignored, it should be excluded.
*/
if (wi->fi.pathlist_match == ITERATOR_PATHLIST_MATCH_CHILD)
*status = GIT_ITERATOR_STATUS_FILTERED;
else
wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */
error = 0;
wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */
} else
break; /* real error, stop here */
} else {

View File

@ -38,6 +38,21 @@ typedef enum {
GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 5),
} git_iterator_flag_t;
typedef struct {
const char *start;
const char *end;
/* paths to include in the iterator (literal). if set, any paths not
* listed here will be excluded from iteration.
*/
git_strarray pathlist;
/* flags, from above */
unsigned int flags;
} git_iterator_options;
#define GIT_ITERATOR_OPTIONS_INIT {0}
typedef struct {
int (*current)(const git_index_entry **, git_iterator *);
int (*advance)(const git_index_entry **, git_iterator *);
@ -54,6 +69,10 @@ struct git_iterator {
git_repository *repo;
char *start;
char *end;
git_vector pathlist;
size_t pathlist_walk_idx;
int (*strcomp)(const char *a, const char *b);
int (*strncomp)(const char *a, const char *b, size_t n);
int (*prefixcomp)(const char *str, const char *prefix);
size_t stat_calls;
unsigned int flags;
@ -61,9 +80,7 @@ struct git_iterator {
extern int git_iterator_for_nothing(
git_iterator **out,
git_iterator_flag_t flags,
const char *start,
const char *end);
git_iterator_options *options);
/* tree iterators will match the ignore_case value from the index of the
* repository, unless you override with a non-zero flag value
@ -71,19 +88,16 @@ extern int git_iterator_for_nothing(
extern int git_iterator_for_tree(
git_iterator **out,
git_tree *tree,
git_iterator_flag_t flags,
const char *start,
const char *end);
git_iterator_options *options);
/* index iterators will take the ignore_case value from the index; the
* ignore_case flags are not used
*/
extern int git_iterator_for_index(
git_iterator **out,
git_repository *repo,
git_index *index,
git_iterator_flag_t flags,
const char *start,
const char *end);
git_iterator_options *options);
extern int git_iterator_for_workdir_ext(
git_iterator **out,
@ -91,9 +105,7 @@ extern int git_iterator_for_workdir_ext(
const char *repo_workdir,
git_index *index,
git_tree *tree,
git_iterator_flag_t flags,
const char *start,
const char *end);
git_iterator_options *options);
/* workdir iterators will match the ignore_case value from the index of the
* repository, unless you override with a non-zero flag value
@ -103,11 +115,9 @@ GIT_INLINE(int) git_iterator_for_workdir(
git_repository *repo,
git_index *index,
git_tree *tree,
git_iterator_flag_t flags,
const char *start,
const char *end)
git_iterator_options *options)
{
return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, flags, start, end);
return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, options);
}
/* for filesystem iterators, you have to explicitly pass in the ignore_case
@ -116,9 +126,7 @@ GIT_INLINE(int) git_iterator_for_workdir(
extern int git_iterator_for_filesystem(
git_iterator **out,
const char *root,
git_iterator_flag_t flags,
const char *start,
const char *end);
git_iterator_options *options);
extern void git_iterator_free(git_iterator *iter);
@ -271,7 +279,8 @@ extern git_index *git_iterator_get_index(git_iterator *iter);
typedef enum {
GIT_ITERATOR_STATUS_NORMAL = 0,
GIT_ITERATOR_STATUS_IGNORED = 1,
GIT_ITERATOR_STATUS_EMPTY = 2
GIT_ITERATOR_STATUS_EMPTY = 2,
GIT_ITERATOR_STATUS_FILTERED = 3
} git_iterator_status_t;
/* Advance over a directory and check if it contains no files or just

File diff suppressed because it is too large Load Diff

View File

@ -19,8 +19,8 @@
#define GIT_MERGE_MODE_FILE "MERGE_MODE"
#define GIT_MERGE_FILE_MODE 0666
#define GIT_MERGE_TREE_RENAME_THRESHOLD 50
#define GIT_MERGE_TREE_TARGET_LIMIT 1000
#define GIT_MERGE_DEFAULT_RENAME_THRESHOLD 50
#define GIT_MERGE_DEFAULT_TARGET_LIMIT 1000
/** Types of changes when files are merged from branch to branch. */
typedef enum {

View File

@ -7,17 +7,23 @@
#include "common.h"
#include "repository.h"
#include "merge_file.h"
#include "posix.h"
#include "fileops.h"
#include "index.h"
#include "diff_xdiff.h"
#include "git2/repository.h"
#include "git2/object.h"
#include "git2/index.h"
#include "git2/merge.h"
#include "xdiff/xdiff.h"
/* only examine the first 8000 bytes for binaryness.
* https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197
*/
#define GIT_MERGE_FILE_BINARY_SIZE 8000
#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
GIT_INLINE(const char *) merge_file_best_path(
@ -100,7 +106,7 @@ static void merge_file_normalize_opts(
}
}
static int git_merge_file__from_inputs(
static int merge_file__xdiff(
git_merge_file_result *out,
const git_merge_file_input *ancestor,
const git_merge_file_input *ours,
@ -189,6 +195,63 @@ done:
return error;
}
static bool merge_file__is_binary(const git_merge_file_input *file)
{
size_t len = file ? file->size : 0;
if (len > GIT_XDIFF_MAX_SIZE)
return true;
if (len > GIT_MERGE_FILE_BINARY_SIZE)
len = GIT_MERGE_FILE_BINARY_SIZE;
return len ? (memchr(file->ptr, 0, len) != NULL) : false;
}
static int merge_file__binary(
git_merge_file_result *out,
const git_merge_file_input *ours,
const git_merge_file_input *theirs,
const git_merge_file_options *given_opts)
{
const git_merge_file_input *favored = NULL;
memset(out, 0x0, sizeof(git_merge_file_result));
if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS)
favored = ours;
else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS)
favored = theirs;
else
goto done;
if ((out->path = git__strdup(favored->path)) == NULL ||
(out->ptr = git__malloc(favored->size)) == NULL)
goto done;
memcpy((char *)out->ptr, favored->ptr, favored->size);
out->len = favored->size;
out->mode = favored->mode;
out->automergeable = 1;
done:
return 0;
}
static int merge_file__from_inputs(
git_merge_file_result *out,
const git_merge_file_input *ancestor,
const git_merge_file_input *ours,
const git_merge_file_input *theirs,
const git_merge_file_options *given_opts)
{
if (merge_file__is_binary(ancestor) ||
merge_file__is_binary(ours) ||
merge_file__is_binary(theirs))
return merge_file__binary(out, ours, theirs, given_opts);
return merge_file__xdiff(out, ancestor, ours, theirs, given_opts);
}
static git_merge_file_input *git_merge_file__normalize_inputs(
git_merge_file_input *out,
const git_merge_file_input *given)
@ -223,7 +286,7 @@ int git_merge_file(
ours = git_merge_file__normalize_inputs(&inputs[1], ours);
theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
return git_merge_file__from_inputs(out, ancestor, ours, theirs, options);
return merge_file__from_inputs(out, ancestor, ours, theirs, options);
}
int git_merge_file_from_index(
@ -234,8 +297,8 @@ int git_merge_file_from_index(
const git_index_entry *theirs,
const git_merge_file_options *options)
{
git_merge_file_input inputs[3] = { {0} },
*ancestor_input = NULL, *our_input = NULL, *their_input = NULL;
git_merge_file_input *ancestor_ptr = NULL,
ancestor_input = {0}, our_input = {0}, their_input = {0};
git_odb *odb = NULL;
git_odb_object *odb_object[3] = { 0 };
int error = 0;
@ -249,27 +312,20 @@ int git_merge_file_from_index(
if (ancestor) {
if ((error = git_merge_file__input_from_index(
&inputs[0], &odb_object[0], odb, ancestor)) < 0)
&ancestor_input, &odb_object[0], odb, ancestor)) < 0)
goto done;
ancestor_input = &inputs[0];
ancestor_ptr = &ancestor_input;
}
if ((error = git_merge_file__input_from_index(
&inputs[1], &odb_object[1], odb, ours)) < 0)
&our_input, &odb_object[1], odb, ours)) < 0 ||
(error = git_merge_file__input_from_index(
&their_input, &odb_object[2], odb, theirs)) < 0)
goto done;
our_input = &inputs[1];
if ((error = git_merge_file__input_from_index(
&inputs[2], &odb_object[2], odb, theirs)) < 0)
goto done;
their_input = &inputs[2];
if ((error = git_merge_file__from_inputs(out,
ancestor_input, our_input, their_input, options)) < 0)
goto done;
error = merge_file__from_inputs(out,
ancestor_ptr, &our_input, &their_input, options);
done:
git_odb_object_free(odb_object[0]);
@ -286,7 +342,5 @@ void git_merge_file_result_free(git_merge_file_result *result)
return;
git__free((char *)result->path);
/* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
free((char *)result->ptr);
git__free((char *)result->ptr);
}

View File

@ -261,6 +261,10 @@ int gitno_extract_url_parts(
*path = git__substrdup(_path, u.field_data[UF_PATH].len);
GITERR_CHECK_ALLOC(*path);
} else {
git__free(*port);
*port = NULL;
git__free(*host);
*host = NULL;
giterr_set(GITERR_NET, "invalid url, missing path");
return GIT_EINVALIDSPEC;
}

View File

@ -663,7 +663,7 @@ int git_note_iterator_new(
if (error < 0)
goto cleanup;
if ((error = git_iterator_for_tree(it, tree, 0, NULL, NULL)) < 0)
if ((error = git_iterator_for_tree(it, tree, NULL)) < 0)
git_iterator_free(*it);
cleanup:

View File

@ -14,7 +14,7 @@
#include "blob.h"
#include "tag.h"
static const int OBJECT_BASE_SIZE = 4096;
bool git_object__strict_input_validation = true;
typedef struct {
const char *str; /* type name string */
@ -467,3 +467,27 @@ int git_object_short_id(git_buf *out, const git_object *obj)
return error;
}
bool git_object__is_valid(
git_repository *repo, const git_oid *id, git_otype expected_type)
{
git_odb *odb;
git_otype actual_type;
size_t len;
int error;
if (!git_object__strict_input_validation)
return true;
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
(error = git_odb_read_header(&len, &actual_type, odb, id)) < 0)
return false;
if (expected_type != GIT_OBJ_ANY && expected_type != actual_type) {
giterr_set(GITERR_INVALID,
"the requested type does not match the type in the ODB");
return false;
}
return true;
}

View File

@ -7,6 +7,10 @@
#ifndef INCLUDE_object_h__
#define INCLUDE_object_h__
#include "repository.h"
extern bool git_object__strict_input_validation;
/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
@ -28,4 +32,23 @@ int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
bool git_object__is_valid(
git_repository *repo, const git_oid *id, git_otype expected_type);
GIT_INLINE(git_otype) git_object__type_from_filemode(git_filemode_t mode)
{
switch (mode) {
case GIT_FILEMODE_TREE:
return GIT_OBJ_TREE;
case GIT_FILEMODE_COMMIT:
return GIT_OBJ_COMMIT;
case GIT_FILEMODE_BLOB:
case GIT_FILEMODE_BLOB_EXECUTABLE:
case GIT_FILEMODE_LINK:
return GIT_OBJ_BLOB;
default:
return GIT_OBJ_BAD;
}
}
#endif

252
src/odb.c
View File

@ -374,10 +374,14 @@ static int backend_sort_cmp(const void *a, const void *b)
const backend_internal *backend_a = (const backend_internal *)(a);
const backend_internal *backend_b = (const backend_internal *)(b);
if (backend_a->is_alternate == backend_b->is_alternate)
return (backend_b->priority - backend_a->priority);
return backend_a->is_alternate ? 1 : -1;
if (backend_b->priority == backend_a->priority) {
if (backend_a->is_alternate)
return -1;
if (backend_b->is_alternate)
return 1;
return 0;
}
return (backend_b->priority - backend_a->priority);
}
int git_odb_new(git_odb **out)
@ -600,8 +604,7 @@ static void odb_free(git_odb *db)
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *backend = internal->backend;
if (backend->free) backend->free(backend);
else git__free(backend);
backend->free(backend);
git__free(internal);
}
@ -621,23 +624,18 @@ void git_odb_free(git_odb *db)
GIT_REFCOUNT_DEC(db, odb_free);
}
int git_odb_exists(git_odb *db, const git_oid *id)
static int odb_exists_1(git_odb *db, const git_oid *id, bool only_refreshed)
{
git_odb_object *object;
size_t i;
bool found = false;
assert(db && id);
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
git_odb_object_free(object);
return (int)true;
}
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (only_refreshed && !b->refresh)
continue;
if (b->exists != NULL)
found = (bool)b->exists(b, id);
}
@ -645,12 +643,74 @@ int git_odb_exists(git_odb *db, const git_oid *id)
return (int)found;
}
int git_odb_exists(git_odb *db, const git_oid *id)
{
git_odb_object *object;
assert(db && id);
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
git_odb_object_free(object);
return (int)true;
}
if (odb_exists_1(db, id, false))
return 1;
if (!git_odb_refresh(db))
return odb_exists_1(db, id, true);
/* Failed to refresh, hence not found */
return 0;
}
static int odb_exists_prefix_1(git_oid *out, git_odb *db,
const git_oid *key, size_t len, bool only_refreshed)
{
size_t i;
int error = GIT_ENOTFOUND, num_found = 0;
git_oid last_found = {{0}}, found;
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (only_refreshed && !b->refresh)
continue;
if (!b->exists_prefix)
continue;
error = b->exists_prefix(&found, b, key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
if (error)
return error;
/* make sure found item doesn't introduce ambiguity */
if (num_found) {
if (git_oid__cmp(&last_found, &found))
return git_odb__error_ambiguous("multiple matches for prefix");
} else {
git_oid_cpy(&last_found, &found);
num_found++;
}
}
if (!num_found)
return GIT_ENOTFOUND;
if (out)
git_oid_cpy(out, &last_found);
return 0;
}
int git_odb_exists_prefix(
git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
{
int error = GIT_ENOTFOUND, num_found = 0;
size_t i;
git_oid key = {{0}}, last_found = {{0}}, found;
int error;
git_oid key = {{0}};
assert(db && short_id);
@ -674,35 +734,15 @@ int git_odb_exists_prefix(
if (len & 1)
key.id[len / 2] &= 0xF0;
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
error = odb_exists_prefix_1(out, db, &key, len, false);
if (!b->exists_prefix)
continue;
if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
error = odb_exists_prefix_1(out, db, &key, len, true);
error = b->exists_prefix(&found, b, &key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
if (error)
return error;
/* make sure found item doesn't introduce ambiguity */
if (num_found) {
if (git_oid__cmp(&last_found, &found))
return git_odb__error_ambiguous("multiple matches for prefix");
} else {
git_oid_cpy(&last_found, &found);
num_found++;
}
}
if (!num_found)
if (error == GIT_ENOTFOUND)
return git_odb__error_notfound("no match for id prefix", &key);
if (out)
git_oid_cpy(out, &last_found);
return 0;
return error;
}
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
@ -784,36 +824,38 @@ static int hardcoded_objects(git_rawobj *raw, const git_oid *id)
}
}
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
bool only_refreshed)
{
size_t i, reads = 0;
int error;
size_t i;
git_rawobj raw;
git_odb_object *object;
bool found = false;
assert(out && db && id);
if (!hardcoded_objects(&raw, id))
found = true;
*out = git_cache_get_raw(odb_cache(db), id);
if (*out != NULL)
return 0;
error = hardcoded_objects(&raw, id);
for (i = 0; i < db->backends.length && error < 0; ++i) {
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (only_refreshed && !b->refresh)
continue;
if (b->read != NULL) {
++reads;
error = b->read(&raw.data, &raw.len, &raw.type, b, id);
int error = b->read(&raw.data, &raw.len, &raw.type, b, id);
if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
continue;
if (error < 0)
return error;
found = true;
}
}
if (error && error != GIT_PASSTHROUGH) {
if (!reads)
return git_odb__error_notfound("no match for id", id);
return error;
}
if (!found)
return GIT_ENOTFOUND;
giterr_clear();
if ((object = odb_object__alloc(id, &raw)) == NULL)
@ -823,42 +865,48 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
return 0;
}
int git_odb_read_prefix(
git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
{
int error;
assert(out && db && id);
*out = git_cache_get_raw(odb_cache(db), id);
if (*out != NULL)
return 0;
error = odb_read_1(out, db, id, false);
if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
error = odb_read_1(out, db, id, true);
if (error == GIT_ENOTFOUND)
return git_odb__error_notfound("no match for id", id);
return error;
}
static int read_prefix_1(git_odb_object **out, git_odb *db,
const git_oid *key, size_t len, bool only_refreshed)
{
size_t i;
int error = GIT_ENOTFOUND;
git_oid key = {{0}}, found_full_oid = {{0}};
git_oid found_full_oid = {{0}};
git_rawobj raw;
void *data = NULL;
bool found = false;
git_odb_object *object;
assert(out && db);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
if (len == GIT_OID_HEXSZ) {
*out = git_cache_get_raw(odb_cache(db), short_id);
if (*out != NULL)
return 0;
}
/* just copy valid part of short_id */
memcpy(&key.id, short_id->id, (len + 1) / 2);
if (len & 1)
key.id[len / 2] &= 0xF0;
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (only_refreshed && !b->refresh)
continue;
if (b->read_prefix != NULL) {
git_oid full_oid;
error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len);
error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
@ -879,7 +927,7 @@ int git_odb_read_prefix(
}
if (!found)
return git_odb__error_notfound("no match for prefix", &key);
return GIT_ENOTFOUND;
if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
return -1;
@ -888,6 +936,42 @@ int git_odb_read_prefix(
return 0;
}
int git_odb_read_prefix(
git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
{
git_oid key = {{0}};
int error;
assert(out && db);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
if (len == GIT_OID_HEXSZ) {
*out = git_cache_get_raw(odb_cache(db), short_id);
if (*out != NULL)
return 0;
}
/* just copy valid part of short_id */
memcpy(&key.id, short_id->id, (len + 1) / 2);
if (len & 1)
key.id[len / 2] &= 0xF0;
error = read_prefix_1(out, db, &key, len, false);
if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
error = read_prefix_1(out, db, &key, len, true);
if (error == GIT_ENOTFOUND)
return git_odb__error_notfound("no match for prefix", &key);
return error;
}
int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload)
{
unsigned int i;

View File

@ -84,9 +84,9 @@ static int object_file_name(
static int object_mkdir(const git_buf *name, const loose_backend *be)
{
return git_futils_mkdir(
return git_futils_mkdir_relative(
name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
}
static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)

View File

@ -154,12 +154,16 @@ void git_mempack_reset(git_odb_backend *_backend)
});
git_array_clear(db->commits);
git_oidmap_clear(db->objects);
}
static void impl__free(git_odb_backend *_backend)
{
git_mempack_reset(_backend);
git__free(_backend);
struct memory_packer_db *db = (struct memory_packer_db *)_backend;
git_oidmap_free(db->objects);
git__free(db);
}
int git_mempack_new(git_odb_backend **out)

View File

@ -346,7 +346,7 @@ static int pack_backend__refresh(git_odb_backend *backend_)
return error;
}
static int pack_backend__read_header_internal(
static int pack_backend__read_header(
size_t *len_p, git_otype *type_p,
struct git_odb_backend *backend, const git_oid *oid)
{
@ -361,24 +361,7 @@ static int pack_backend__read_header_internal(
return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
}
static int pack_backend__read_header(
size_t *len_p, git_otype *type_p,
struct git_odb_backend *backend, const git_oid *oid)
{
int error;
error = pack_backend__read_header_internal(len_p, type_p, backend, oid);
if (error != GIT_ENOTFOUND)
return error;
if ((error = pack_backend__refresh(backend)) < 0)
return error;
return pack_backend__read_header_internal(len_p, type_p, backend, oid);
}
static int pack_backend__read_internal(
static int pack_backend__read(
void **buffer_p, size_t *len_p, git_otype *type_p,
git_odb_backend *backend, const git_oid *oid)
{
@ -397,24 +380,7 @@ static int pack_backend__read_internal(
return 0;
}
static int pack_backend__read(
void **buffer_p, size_t *len_p, git_otype *type_p,
git_odb_backend *backend, const git_oid *oid)
{
int error;
error = pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
if (error != GIT_ENOTFOUND)
return error;
if ((error = pack_backend__refresh(backend)) < 0)
return error;
return pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
}
static int pack_backend__read_prefix_internal(
static int pack_backend__read_prefix(
git_oid *out_oid,
void **buffer_p,
size_t *len_p,
@ -451,45 +417,9 @@ static int pack_backend__read_prefix_internal(
return error;
}
static int pack_backend__read_prefix(
git_oid *out_oid,
void **buffer_p,
size_t *len_p,
git_otype *type_p,
git_odb_backend *backend,
const git_oid *short_oid,
size_t len)
{
int error;
error = pack_backend__read_prefix_internal(
out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
if (error != GIT_ENOTFOUND)
return error;
if ((error = pack_backend__refresh(backend)) < 0)
return error;
return pack_backend__read_prefix_internal(
out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
}
static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
struct git_pack_entry e;
int error;
error = pack_entry_find(&e, (struct pack_backend *)backend, oid);
if (error != GIT_ENOTFOUND)
return error == 0;
if ((error = pack_backend__refresh(backend)) < 0) {
giterr_clear();
return (int)false;
}
return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
}
@ -501,12 +431,7 @@ static int pack_backend__exists_prefix(
struct git_pack_entry e = {0};
error = pack_entry_find_prefix(&e, pb, short_id, len);
if (error == GIT_ENOTFOUND && !(error = pack_backend__refresh(backend)))
error = pack_entry_find_prefix(&e, pb, short_id, len);
git_oid_cpy(out, &e.sha1);
return error;
}
@ -674,7 +599,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
git_path_isdir(git_buf_cstr(&path)))
{
backend->pack_folder = git_buf_detach(&path);
error = pack_backend__refresh((git_odb_backend *)backend);
}

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