mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-10-31 17:50:41 +00:00 
			
		
		
		
	Imported Upstream version 0.24.0
This commit is contained in:
		
						commit
						b3ee20cb0a
					
				
							
								
								
									
										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
	 Andreas Henriksson
						Andreas Henriksson