mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-11-04 04:38:58 +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