mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-12 20:56:48 +00:00
Merge tag 'upstream/0.24.0'
Upstream version 0.24.0
This commit is contained in:
commit
f92de12b3d
3
.mailmap
3
.mailmap
@ -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>
|
||||
|
@ -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
|
||||
|
116
CHANGELOG.md
116
CHANGELOG.md
@ -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.
|
||||
|
138
CMakeLists.txt
138
CMakeLists.txt
@ -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
75
CODE_OF_CONDUCT.md
Normal 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/
|
@ -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
46
COPYING
@ -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
|
||||
|
@ -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
|
||||
|
22
README.md
22
README.md
@ -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
|
||||
|
16
THREADING.md
16
THREADING.md
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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`
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 *);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 ***
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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@
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -2,4 +2,5 @@
|
||||
|
||||
set -x
|
||||
|
||||
brew install libssh2 cmake
|
||||
brew update
|
||||
brew install libssh2
|
||||
|
34
script/user_nodefs.h
Normal file
34
script/user_nodefs.h
Normal 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)-- )
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
26
src/blame.c
26
src/blame.c
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
13
src/branch.c
13
src/branch.c
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
232
src/commit.c
232
src/commit.c
@ -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;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ struct git_commit {
|
||||
char *raw_header;
|
||||
|
||||
char *summary;
|
||||
char *body;
|
||||
};
|
||||
|
||||
void git_commit__free(void *commit);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
38
src/common.h
38
src/common.h
@ -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; }
|
||||
|
46
src/config.c
46
src/config.c
@ -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
|
||||
***********/
|
||||
|
16
src/config.h
16
src/config.h
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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); */
|
||||
|
||||
|
167
src/diff.c
167
src/diff.c
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
121
src/errors.c
121
src/errors.c
@ -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)
|
||||
|
@ -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;
|
||||
|
349
src/fileops.c
349
src/fileops.c
@ -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 {
|
||||
|
@ -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;
|
||||
|
292
src/filter.c
292
src/filter.c
@ -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(®->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(®->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(
|
||||
®->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(®->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;
|
||||
|
@ -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(
|
||||
|
241
src/global.c
241
src/global.c
@ -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)
|
||||
|
@ -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
93
src/idxmap.h
Normal 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
|
16
src/ignore.c
16
src/ignore.c
@ -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;
|
||||
|
802
src/index.c
802
src/index.c
File diff suppressed because it is too large
Load Diff
44
src/index.h
44
src/index.h
@ -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);
|
||||
|
517
src/iterator.c
517
src/iterator.c
@ -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 {
|
||||
|
@ -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
|
||||
|
798
src/merge.c
798
src/merge.c
File diff suppressed because it is too large
Load Diff
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
26
src/object.c
26
src/object.c
@ -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;
|
||||
}
|
||||
|
||||
|
23
src/object.h
23
src/object.h
@ -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
252
src/odb.c
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user