- secure: "YnhS+8n6B+uoyaYfaJ3Lei7cSJqHDPiKJCKFIF2c87YDfmCvAJke8QtE7IzjYDs7UFkTCM4ox+ph2bERUrxZbSCyEkHdjIZpKuMJfYWja/jgMqTMxdyOH9y8JLFbZsSXDIXDwqBlC6vVyl1fP90M35wuWcNTs6tctfVWVofEFbs="
- GITTEST_INVASIVE_FS_SIZE=1
matrix:
- - OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release"
- - OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON"
-
-addons:
- apt:
- packages:
- - cmake
- - libssh2-1-dev
- - openssh-client
- - openssh-server
- - valgrind
+ - OPTIONS="-DTHREADSAFE=ON -DENABLE_TRACE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_WERROR=ON"
+ - OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON -DENABLE_WERROR=ON"
+dist: trusty
sudo: false
+addons:
+ apt:
+ sources:
+ - sourceline: 'deb https://dl.bintray.com/libgit2/ci-dependencies trusty libgit2deps'
+ key_url: 'https://bintray.com/user/downloadSubjectPublicKey?username=bintray'
+ packages:
+ cmake
+ curl
+ libcurl3
+ libcurl3-gnutls
+ libcurl4-gnutls-dev
+ libssh2-1-dev
+ openssh-client
+ openssh-server
+ valgrind
+
matrix:
fast_finish: true
exclude:
- compiler: gcc
env: COVERITY=1
os: linux
+ dist: trusty
- compiler: gcc
env:
- VALGRIND=1
OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
os: linux
+ dist: trusty
allow_failures:
- env: COVERITY=1
install:
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
+ - if [ -f ./script/install-deps-${TRAVIS_OS_NAME}.sh ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
# Run the Build script and tests
script:
Dmitry Kovega
Emeric Fermas
Emmanuel Rodriguez
+Eric Myhre
Florian Forster
Holger Weiss
Ingmar Vanhassel
-v0.26 + 1
+v0.27 + 1
---------
### Changes or improvements
### Breaking API changes
+v0.27
+---------
+
+### Changes or improvements
+
+* Improved `p_unlink` in `posix_w32.c` to try and make a file writable
+ before sleeping in the retry loop to prevent unnecessary calls to sleep.
+
+* The CMake build infrastructure has been improved to speed up building time.
+
+* A new CMake option "-DUSE_HTTPS=<backend>" makes it possible to explicitly
+ choose an HTTP backend.
+
+* A new CMake option "-DSHA1_BACKEND=<backend>" makes it possible to explicitly
+ choose an SHA1 backend. The collision-detecting backend is now the default.
+
+* A new CMake option "-DUSE_BUNDLED_ZLIB" makes it possible to explicitly use
+ the bundled zlib library.
+
+* A new CMake option "-DENABLE_REPRODUCIBLE_BUILDS" makes it possible to
+ generate a reproducible static archive. This requires support from your
+ toolchain.
+
+* The minimum required CMake version has been bumped to 2.8.11.
+
+* Writing to a configuration file now preserves the case of the key given by the
+ caller for the case-insensitive portions of the key (existing sections are
+ used even if they don't match).
+
+* We now support conditional includes in configuration files.
+
+* Fix for handling re-reading of configuration files with includes.
+
+* Fix for reading patches which contain exact renames only.
+
+* Fix for reading patches with whitespace in the compared files' paths.
+
+* We will now fill `FETCH_HEAD` from all passed refspecs instead of overwriting
+ with the last one.
+
+* There is a new diff option, `GIT_DIFF_INDENT_HEURISTIC` which activates a
+ heuristic which takes into account whitespace and indentation in order to
+ produce better diffs when dealing with ambiguous diff hunks.
+
+* Fix for pattern-based ignore rules where files ignored by a rule cannot be
+ un-ignored by another rule.
+
+* Sockets opened by libgit2 are now being closed on exec(3) if the platform
+ supports it.
+
+* Fix for peeling annotated tags from packed-refs files.
+
+* Fix reading huge loose objects from the object database.
+
+* Fix files not being treated as modified when only the file mode has changed.
+
+* We now explicitly reject adding submodules to the index via
+ `git_index_add_frombuffer`.
+
+* Fix handling of `GIT_DIFF_FIND_RENAMES_FROM_REWRITES` raising `SIGABRT` when
+ one file has been deleted and another file has been rewritten.
+
+* Fix for WinHTTP not properly handling NTLM and Negotiate challenges.
+
+* When using SSH-based transports, we now repeatedly ask for the passphrase to
+ decrypt the private key in case a wrong passphrase is being provided.
+
+* When generating conflict markers, they will now use the same line endings as
+ the rest of the file.
+
+### API additions
+
+* The `git_merge_file_options` structure now contains a new setting,
+ `marker_size`. This allows users to set the size of markers that
+ delineate the sides of merged files in the output conflict file.
+ By default this is 7 (`GIT_MERGE_CONFLICT_MARKER_SIZE`), which
+ produces output markers like `<<<<<<<` and `>>>>>>>`.
+
+* `git_remote_create_detached()` creates a remote that is not associated
+ to any repository (and does not apply configuration like 'insteadof' rules).
+ This is mostly useful for e.g. emulating `git ls-remote` behavior.
+
+* `git_diff_patchid()` lets you generate patch IDs for diffs.
+
+* `git_status_options` now has an additional field `baseline` to allow creating
+ status lists against different trees.
+
+* New family of functions to allow creating notes for a specific notes commit
+ instead of for a notes reference.
+
+* New family of functions to allow parsing message trailers. This API is still
+ experimental and may change in future releases.
+
+### API removals
+
+### Breaking API changes
+
+* Signatures now distinguish between +0000 and -0000 UTC offsets.
+
+* The certificate check callback in the WinHTTP transport will now receive the
+ `message_cb_payload` instead of the `cred_acquire_payload`.
+
+* We are now reading symlinked directories under .git/refs.
+
+* We now refuse creating branches named "HEAD".
+
+* We now refuse reading and writing all-zero object IDs into the
+ object database.
+
+* We now read the effective user's configuration file instead of the real user's
+ configuration in case libgit2 runs as part of a setuid binary.
+
+* The `git_odb_open_rstream` function and its `readstream` callback in the
+ `git_odb_backend` interface have changed their signatures to allow providing
+ the object's size and type to the caller.
+
v0.26
-----
# > cmake --build . --target install
PROJECT(libgit2 C)
-CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
CMAKE_POLICY(SET CMP0015 NEW)
+IF (POLICY CMP0051)
+ CMAKE_POLICY(SET CMP0051 NEW)
+ENDIF()
+IF (POLICY CMP0042)
+ CMAKE_POLICY(SET CMP0042 NEW)
+ENDIF()
# Add find modules to the path
-SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${libgit2_SOURCE_DIR}/cmake/Modules/")
INCLUDE(CheckLibraryExists)
INCLUDE(CheckFunctionExists)
INCLUDE(CheckStructHasMember)
INCLUDE(AddCFlagIfSupported)
INCLUDE(FindPkgConfig)
+INCLUDE(FindThreads)
+INCLUDE(FindStatNsec)
+INCLUDE(IdeSplitSources)
+INCLUDE(FeatureSummary)
+INCLUDE(EnableWarnings)
# Build options
#
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
-OPTION( USE_SHA1DC "Use SHA-1 with collision detection" OFF )
-OPTION( USE_ICONV "Link with and use iconv library" OFF )
+SET(SHA1_BACKEND "CollisionDetection" CACHE STRING "Backend to use for SHA1. One of Generic, OpenSSL, Win32, CommonCrypto, CollisionDetection. ")
OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
+OPTION( USE_HTTPS "Enable HTTPS support. Can be set to a specific backend" ON )
OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF )
OPTION( VALGRIND "Configure build for valgrind" OFF )
OPTION( CURL "Use curl for HTTP if available" ON)
+OPTION( USE_EXT_HTTP_PARSER "Use system HTTP_Parser if available" ON)
OPTION( DEBUG_POOL "Enable debug pool allocator" OFF )
+OPTION( ENABLE_WERROR "Enable compilation with -Werror" OFF )
+OPTION( USE_BUNDLED_ZLIB "Use the bundled version of zlib" OFF )
-IF(DEBUG_POOL)
- ADD_DEFINITIONS(-DGIT_DEBUG_POOL)
+IF (UNIX AND NOT APPLE)
+ OPTION( ENABLE_REPRODUCIBLE_BUILDS "Enable reproducible builds" OFF )
ENDIF()
-IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
- SET( USE_ICONV ON )
- FIND_PACKAGE(Security)
- FIND_PACKAGE(CoreFoundation REQUIRED)
+IF (APPLE)
+ OPTION( USE_ICONV "Link with and use iconv library" ON )
ENDIF()
IF(MSVC)
# 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)
ENDIF()
OPTION(MSVC_CRTDBG "Enable CRTDBG memory leak reporting" OFF)
ENDIF()
-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" ON )
-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.
-SET(LIBGIT2_PC_REQUIRES "")
-# This will be set later if we use the system's http-parser library or
-# use iconv (OSX) and will be written to the Libs.private field in the
-# pc file.
-SET(LIBGIT2_PC_LIBS "")
-
-# Installation paths
-#
-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)
- ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
- TARGET_LINK_LIBRARIES(${target} socket nsl)
- LIST(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
- SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
- ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Haiku")
- TARGET_LINK_LIBRARIES(${target} network)
- LIST(APPEND LIBGIT2_PC_LIBS "-lnetwork")
- SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
- ENDIF()
- CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT)
- IF(NEED_LIBRT)
- TARGET_LINK_LIBRARIES(${target} rt)
- LIST(APPEND LIBGIT2_PC_LIBS "-lrt")
- SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
- ENDIF()
-
- IF(THREADSAFE)
- TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
- LIST(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
- SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
- ENDIF()
-ENDFUNCTION()
-
-# 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 ".*/")
- STRING(REPLACE ${CMAKE_CURRENT_SOURCE_DIR}/ "" rel ${source})
- IF(rel)
- STRING(REGEX REPLACE "/([^/]*)$" "" rel ${rel})
- IF(rel)
- STRING(REPLACE "/" "\\\\" rel ${rel})
- SOURCE_GROUP(${rel} FILES ${source})
- ENDIF()
- ENDIF()
- ENDIF()
- ENDFOREACH()
- ENDIF()
-ENDFUNCTION()
-
-FILE(STRINGS "include/git2/version.h" GIT2_HEADER REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$")
+FILE(STRINGS "${libgit2_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$")
STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"([0-9]+).*$" "\\1" LIBGIT2_VERSION_MAJOR "${GIT2_HEADER}")
STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_MINOR "${GIT2_HEADER}")
STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_REV "${GIT2_HEADER}")
SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}")
-FILE(STRINGS "include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
+FILE(STRINGS "${libgit2_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}")
-# Find required dependencies
-INCLUDE_DIRECTORIES(src include)
-
-IF (SECURITY_FOUND)
- # 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)
- MESSAGE("-- Found CoreFoundation ${COREFOUNDATION_DIRS}")
- LIST(APPEND LIBGIT2_PC_LIBS "-framework CoreFoundation")
-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)
- ADD_DEFINITIONS(-DGIT_HTTPS)
-
- # Since MinGW does not come with headers or an import library for winhttp,
- # we have to include a private header and generate our own import library
- IF (MINGW)
- FIND_PROGRAM(DLLTOOL dlltool CMAKE_FIND_ROOT_PATH_BOTH)
- IF (NOT DLLTOOL)
- MESSAGE(FATAL_ERROR "Could not find dlltool command")
- ENDIF ()
-
- SET(LIBWINHTTP_PATH "${CMAKE_CURRENT_BINARY_DIR}/deps/winhttp")
- FILE(MAKE_DIRECTORY ${LIBWINHTTP_PATH})
-
- 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")
- ENDIF()
-
- ADD_CUSTOM_COMMAND(
- OUTPUT ${LIBWINHTTP_PATH}/libwinhttp.a
- COMMAND ${DLLTOOL} -d ${WINHTTP_DEF} -k -D winhttp.dll -l libwinhttp.a
- DEPENDS ${WINHTTP_DEF}
- WORKING_DIRECTORY ${LIBWINHTTP_PATH}
- )
-
- SET_SOURCE_FILES_PROPERTIES(
- ${CMAKE_CURRENT_SOURCE_DIR}/src/transports/winhttp.c
- PROPERTIES OBJECT_DEPENDS ${LIBWINHTTP_PATH}/libwinhttp.a
- )
-
- INCLUDE_DIRECTORIES(deps/winhttp)
- LINK_DIRECTORIES(${LIBWINHTTP_PATH})
- ENDIF ()
-
- LINK_LIBRARIES(winhttp rpcrt4 crypt32 ole32)
- LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp" "-lrpcrt4" "-lcrypt32" "-lole32")
-ELSE ()
- IF (CURL)
- PKG_CHECK_MODULES(CURL libcurl)
- ENDIF ()
-
- IF (NOT AMIGA AND USE_OPENSSL)
- FIND_PACKAGE(OpenSSL)
- ENDIF ()
-
- IF (CURL_FOUND)
- ADD_DEFINITIONS(-DGIT_CURL)
- INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIRS})
- LINK_DIRECTORIES(${CURL_LIBRARY_DIRS})
- LINK_LIBRARIES(${CURL_LIBRARIES})
- LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS})
- ENDIF()
-ENDIF()
-
-# Specify sha1 implementation
-IF (USE_SHA1DC)
- ADD_DEFINITIONS(-DGIT_SHA1_COLLISIONDETECT)
- ADD_DEFINITIONS(-DSHA1DC_NO_STANDARD_INCLUDES=1)
- ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\")
- ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\")
- FILE(GLOB SRC_SHA1 src/hash/hash_collisiondetect.c src/hash/sha1dc/*)
-ELSEIF (WIN32 AND NOT MINGW)
- ADD_DEFINITIONS(-DGIT_SHA1_WIN32)
- FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
-ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
- ADD_DEFINITIONS(-DGIT_SHA1_COMMON_CRYPTO)
-ELSEIF (OPENSSL_FOUND)
- ADD_DEFINITIONS(-DGIT_SHA1_OPENSSL)
- IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
- LIST(APPEND LIBGIT2_PC_LIBS "-lssl")
- ELSE()
- SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
- ENDIF ()
-ELSE()
- FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
-ENDIF()
-
-# Enable tracing
-IF (ENABLE_TRACE STREQUAL "ON")
- ADD_DEFINITIONS(-DGIT_TRACE)
-ENDIF()
-
-# Include POSIX regex when it is required
-IF(WIN32 OR AMIGA OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
- INCLUDE_DIRECTORIES(deps/regex)
- SET(SRC_REGEX deps/regex/regex.c)
-ENDIF()
-
-# Optional external dependency: http-parser
-FIND_PACKAGE(HTTP_Parser)
-IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
- INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
- LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
- LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
-ELSE()
- MESSAGE(STATUS "http-parser version 2 was not found; using bundled 3rd-party sources.")
- INCLUDE_DIRECTORIES(deps/http-parser)
- FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
-ENDIF()
-
-# Optional external dependency: zlib
-FIND_PACKAGE(ZLIB)
-IF (ZLIB_FOUND)
- INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
- LINK_LIBRARIES(${ZLIB_LIBRARIES})
- IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
- LIST(APPEND LIBGIT2_PC_LIBS "-lz")
- ELSE()
- SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib")
- ENDIF()
-ELSE()
- MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." )
- INCLUDE_DIRECTORIES(deps/zlib)
- ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP)
- FILE(GLOB SRC_ZLIB deps/zlib/*.c deps/zlib/*.h)
-ENDIF()
-
-# Optional external dependency: libssh2
-IF (USE_SSH)
- PKG_CHECK_MODULES(LIBSSH2 libssh2)
-ENDIF()
-IF (LIBSSH2_FOUND)
- ADD_DEFINITIONS(-DGIT_SSH)
- INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIRS})
- LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
- LIST(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
- #SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}")
- SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
-
- 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()
-ELSE()
- MESSAGE(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
-ENDIF()
-
-# Optional external dependency: libgssapi
-IF (USE_GSSAPI)
- FIND_PACKAGE(GSSAPI)
-ENDIF()
-IF (GSSAPI_FOUND)
- ADD_DEFINITIONS(-DGIT_GSSAPI)
-ENDIF()
-
-# Optional external dependency: iconv
-IF (USE_ICONV)
- FIND_PACKAGE(Iconv)
-ENDIF()
-IF (ICONV_FOUND)
- ADD_DEFINITIONS(-DGIT_USE_ICONV)
- INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR})
- LIST(APPEND LIBGIT2_PC_LIBS ${ICONV_LIBRARIES})
-ENDIF()
-
# Platform specific compilation flags
IF (MSVC)
+ ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
+ ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
+ ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
ENDIF()
IF (MSVC_CRTDBG)
- SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG} /DGIT_MSVC_CRTDBG")
- SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}" "Dbghelp.lib")
+ SET(GIT_MSVC_CRTDBG 1)
+ SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG}")
+ SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} Dbghelp.lib")
ENDIF()
# /Zi - Create debugging information
SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
SET(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
SET(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}")
-
- SET(WIN_RC "src/win32/git2.rc")
ELSE ()
+ IF (ENABLE_REPRODUCIBLE_BUILDS)
+ SET(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Dqc <TARGET> <LINK_FLAGS> <OBJECTS>")
+ SET(CMAKE_C_ARCHIVE_APPEND "<CMAKE_AR> Dq <TARGET> <LINK_FLAGS> <OBJECTS>")
+ SET(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -D <TARGET>")
+ ENDIF()
+
SET(CMAKE_C_FLAGS "-D_GNU_SOURCE ${CMAKE_C_FLAGS}")
- ADD_C_FLAG_IF_SUPPORTED(-Wall)
- ADD_C_FLAG_IF_SUPPORTED(-Wextra)
+ ENABLE_WARNINGS(all)
+ ENABLE_WARNINGS(extra)
IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
SET(CMAKE_C_FLAGS "-std=c99 -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}")
ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1)
ENDIF ()
- ADD_C_FLAG_IF_SUPPORTED(-Wdocumentation)
- ADD_C_FLAG_IF_SUPPORTED(-Wno-missing-field-initializers)
- ADD_C_FLAG_IF_SUPPORTED(-Wstrict-aliasing=2)
- ADD_C_FLAG_IF_SUPPORTED(-Wstrict-prototypes)
- ADD_C_FLAG_IF_SUPPORTED(-Wdeclaration-after-statement)
- ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-const-variable)
- ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-function)
+ ENABLE_WARNINGS(documentation)
+ DISABLE_WARNINGS(missing-field-initializers)
+ ENABLE_WARNINGS(strict-aliasing)
+ ENABLE_WARNINGS(strict-prototypes)
+ ENABLE_WARNINGS(declaration-after-statement)
+ ENABLE_WARNINGS(shift-count-overflow)
+ DISABLE_WARNINGS(unused-const-variable)
+ DISABLE_WARNINGS(unused-function)
IF (APPLE) # Apple deprecated OpenSSL
- ADD_C_FLAG_IF_SUPPORTED(-Wno-deprecated-declarations)
+ DISABLE_WARNINGS(deprecated-declarations)
ENDIF()
IF (PROFILE)
ENDIF ()
ENDIF()
-CHECK_SYMBOL_EXISTS(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L)
-IF (HAVE_REGCOMP_L)
- ADD_DEFINITIONS(-DHAVE_REGCOMP_L)
-ENDIF ()
-
-CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
-IF (HAVE_FUTIMENS)
- ADD_DEFINITIONS(-DHAVE_FUTIMENS)
-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)
# that uses CMAKE_CONFIGURATION_TYPES and not CMAKE_BUILD_TYPE
ENDIF()
-IF (SECURITY_FOUND)
- ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
- ADD_DEFINITIONS(-DGIT_HTTPS)
- INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
-ENDIF ()
-
-IF (OPENSSL_FOUND)
- ADD_DEFINITIONS(-DGIT_OPENSSL)
- ADD_DEFINITIONS(-DGIT_HTTPS)
- INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
- SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
-ENDIF()
-
-
-
-IF (THREADSAFE)
- IF (NOT WIN32)
- FIND_PACKAGE(Threads REQUIRED)
- ENDIF()
-
- 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)
+ADD_SUBDIRECTORY(src)
-# Collect sourcefiles
-FILE(GLOB SRC_H include/git2.h include/git2/*.h include/git2/sys/*.h)
-
-# On Windows use specific platform sources
-IF (WIN32 AND NOT CYGWIN)
- ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
- FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h)
-ELSEIF (AMIGA)
- ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
-ELSE()
- IF (VALGRIND)
- ADD_DEFINITIONS(-DNO_MMAP)
- ENDIF()
- FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h)
-ENDIF()
-FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h)
-
-# Determine architecture of the machine
-IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
- ADD_DEFINITIONS(-DGIT_ARCH_64)
-ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4)
- ADD_DEFINITIONS(-DGIT_ARCH_32)
-ELSEIF (CMAKE_SIZEOF_VOID_P)
- MESSAGE(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
-ELSE()
- MESSAGE(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
-ENDIF()
-
-# Compile and link libgit2
-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})
-TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
-TARGET_LINK_LIBRARIES(git2 ${GSSAPI_LIBRARIES})
-TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES})
-TARGET_OS_LIBRARIES(git2)
-
-# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
-# Win64+MSVC+static libs = linker error
-IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
- SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
-ENDIF()
-
-IDE_SPLIT_SOURCES(git2)
-
-IF (SONAME)
- SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
- SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_SOVERSION})
- IF (LIBGIT2_FILENAME)
- ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
- SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
- ELSEIF (DEFINED LIBGIT2_PREFIX)
- SET_TARGET_PROPERTIES(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
- ENDIF()
-ENDIF()
-STRING(REPLACE ";" " " LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS}")
-CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY)
-
-IF (MSVC_IDE)
- # Precompiled headers
- SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
- SET_SOURCE_FILES_PROPERTIES(src/win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
+# Tests
+IF (NOT MSVC)
+ IF (NOT BUILD_SHARED_LIBS)
+ SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+ ENDIF()
ENDIF ()
-# Install
-INSTALL(TARGETS git2
- RUNTIME DESTINATION ${BIN_INSTALL_DIR}
- LIBRARY DESTINATION ${LIB_INSTALL_DIR}
- ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
-)
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
-INSTALL(DIRECTORY include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} )
-INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
-
-# Tests
IF (BUILD_CLAR)
- 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")
- SET(CLAR_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources" CACHE PATH "Path to test resources.")
- ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
- ADD_DEFINITIONS(-DCLAR_RESOURCES=\"${TEST_RESOURCES}\")
- ADD_DEFINITIONS(-DCLAR_TMPDIR=\"libgit2_tests\")
-
- INCLUDE_DIRECTORIES(${CLAR_PATH})
- FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
- SET(SRC_CLAR "${CLAR_PATH}/main.c" "${CLAR_PATH}/clar_libgit2.c" "${CLAR_PATH}/clar_libgit2_trace.c" "${CLAR_PATH}/clar_libgit2_timer.c" "${CLAR_PATH}/clar.c")
-
- ADD_CUSTOM_COMMAND(
- OUTPUT ${CLAR_PATH}/clar.suite
- COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline -xstress .
- DEPENDS ${SRC_TEST}
- WORKING_DIRECTORY ${CLAR_PATH}
- )
-
- SET_SOURCE_FILES_PROPERTIES(
- ${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_SSH} ${SRC_SHA1})
-
- TARGET_LINK_LIBRARIES(libgit2_clar ${COREFOUNDATION_DIRS})
- TARGET_LINK_LIBRARIES(libgit2_clar ${SECURITY_DIRS})
- TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
- TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
- TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
- TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
- TARGET_OS_LIBRARIES(libgit2_clar)
- IDE_SPLIT_SOURCES(libgit2_clar)
-
- IF (MSVC_IDE)
- # Precompiled headers
- SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
- ENDIF ()
-
ENABLE_TESTING()
- IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND)
- ADD_TEST(libgit2_clar libgit2_clar -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
- ELSE ()
- ADD_TEST(libgit2_clar libgit2_clar -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
- 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)
- ADD_TEST(libgit2_clar-proxy_credentials_in_url libgit2_clar -v -sonline::clone::proxy_credentials_in_url)
- ADD_TEST(libgit2_clar-proxy_credentials_request libgit2_clar -v -sonline::clone::proxy_credentials_request)
+ ADD_SUBDIRECTORY(tests)
ENDIF ()
IF (TAGS)
IF (BUILD_EXAMPLES)
ADD_SUBDIRECTORY(examples)
ENDIF ()
+
+IF(CMAKE_VERSION VERSION_GREATER 3)
+ FEATURE_SUMMARY(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
+ FEATURE_SUMMARY(WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:")
+ELSE()
+ PRINT_ENABLED_FEATURES()
+ PRINT_DISABLED_FEATURES()
+ENDIF()
in the first place, you're less likely to get feedback and have your change
merged in.
+In addition to outlining your thought process in the PR's description, please
+also try to document it in your commits. We welcome it if every commit has a
+description of why you have been doing your changes alongside with your
+reasoning why this is a good idea. The messages shall be written in
+present-tense and in an imperative style (e.g. "Add feature foobar", not "Added
+feature foobar" or "Adding feature foobar"). Lines should be wrapped at 80
+characters so people with small screens are able to read the commit messages in
+their terminal without any problem.
+
+To make it easier to attribute commits to certain parts of our code base, we
+also prefer to have the commit subject be prefixed with a "scope". E.g. if you
+are changing code in our merging subsystem, make sure to prefix the subject with
+"merge:". The first word following the colon shall start with an lowercase
+letter. The maximum line length for the subject is 70 characters, preferably
+shorter.
+
If you are starting to work on a particular area, feel free to submit a PR
that highlights your work in progress (and note in the PR title that it's
not ready to merge). These early PRs are welcome and will help in getting
trick to this one will be doing it in a manner that is clean and
simple, but still handles the various cases correctly (e.g. `-B/70%`
is apparently a legal setting).
- * Implement the `--log-size` option for `examples/log.c`. I think all
- the data is available, you would just need to add the code into the
- `print_commit()` routine (along with a way of passing the option
- into that function).
* As an extension to the matching idea for `examples/log.c`, add the
`-i` option to use `strcasestr()` for matches.
* For `examples/log.c`, implement the `--first-parent` option now that
[![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639)
`libgit2` is a portable, pure C implementation of the Git core methods
-provided as a re-entrant linkable library with a solid API, allowing you to
-write native speed custom Git applications in any language with bindings.
+provided as a linkable library with a solid API, allowing to build Git
+functionality into your application. Language bindings like
+[Rugged](https://github.com/libgit2/rugged) (Ruby),
+[LibGit2Sharp](https://github.com/libgit2/libgit2sharp) (.NET),
+[pygit2](http://www.pygit2.org/) (Python) and
+[NodeGit](http://nodegit.org) (Node) allow you to build Git tooling
+in your favorite language.
+
+`libgit2` is used to power Git GUI clients like
+[GitKraken](https://gitkraken.com/) and [gmaster](https://gmaster.io/)
+and on Git hosting providers like [GitHub](https://github.com/),
+[GitLab](https://gitlab.com/) and
+[Visual Studio Team Services](https://visualstudio.com/team-services/).
+We perform the merge every time you click "merge pull request".
`libgit2` is licensed under a **very permissive license** (GPLv2 with a special
Linking Exception). This basically means that you can link it (unmodified)
Additionally, the example code has been released to the public domain (see the
[separate license](examples/COPYING) for more information).
+Quick Start
+===========
+
+**Prerequisites** for building libgit2:
+
+1. [CMake](https://cmake.org/), and is recommended to be installed into
+ your `PATH`.
+2. [Python](https://www.python.org) is used by our test framework, and
+ should be installed into your `PATH`.
+3. C compiler: libgit2 is C90 and should compile on most compilers.
+ * Windows: Visual Studio is recommended
+ * Mac: Xcode is recommended
+ * Unix: gcc or clang is recommended.
+
+**Build**
+
+1. Create a build directory beneath the libgit2 source directory, and change
+ into it: `mkdir build && cd build`
+2. Create the cmake build environment: `cmake ..`
+3. Build libgit2: `cmake --build .`
+
+Trouble with these steps? Read `TROUBLESHOOTING.md`. More detailed build
+guidance is available below.
+
Getting Help
============
We ask that you not open a GitHub Issue for help, only for bug reports.
+**Reporting Security Issues**
+
+In case you think to have found a security issue with libgit2, please do not
+open a public issue. Instead, you can report the issue to the private mailing
+list [security@libgit2.com](mailto:security@libgit2.com).
+
What It Can Do
==============
Building libgit2 - Using CMake
==============================
+Building
+--------
+
`libgit2` builds cleanly on most platforms without any external dependencies.
Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthreads` to be available;
they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
Alternatively you can point the CMake GUI tool to the CMakeLists.txt file and generate platform specific build project or IDE workspace.
+Running Tests
+-------------
+
Once built, you can run the tests from the `build` directory with the command
- $ make test
+ $ ctest -V
Alternatively you can run the test suite directly using,
$ ./libgit2_clar
+Invoking the test suite directly is useful because it allows you to execute
+individual tests, or groups of tests using the `-s` flag. For example, to
+run the index tests:
+
+ $ ./libgit2_clar -sindex
+
+To run a single test named `index::racy::diff`, which corresponds to the test
+function (`test_index_racy__diff`)[https://github.com/libgit2/libgit2/blob/master/tests/index/racy.c#L23]:
+
+ $ ./libgit2_clar -sindex::racy::diff
+
+The test suite will print a `.` for every passing test, and an `F` for any
+failing test. An `S` indicates that a test was skipped because it is not
+applicable to your platform or is particularly expensive.
+
+**Note:** There should be _no_ failing tests when you build an unmodified
+source tree from a [release](https://github.com/libgit2/libgit2/releases),
+or from the [master branch](https://github.com/libgit2/libgit2/tree/master).
+Please contact us or [open an issue](https://github.com/libgit2/libgit2/issues)
+if you see test failures.
+
+Installation
+------------
+
To install the library you can specify the install prefix by setting:
$ cmake .. -DCMAKE_INSTALL_PREFIX=/install/prefix
$ cmake --build . --target install
+Advanced Usage
+--------------
+
For more advanced use or questions about CMake please read <https://cmake.org/Wiki/CMake_FAQ>.
The following CMake variables are declared:
all up for you if you use `-DCMAKE_OSX_ARCHITECTURES="i386;x86_64"`
when configuring.
-Windows
--------
-
-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](http://libgit2.github.com/docs/guides/build-and-link/)
-for more detailed instructions.
-
Android
-------
* git2go <https://github.com/libgit2/git2go>
* GObject
* libgit2-glib <https://wiki.gnome.org/Projects/Libgit2-glib>
+* Guile
+ * Guile-Git <https://gitlab.com/guile-git/guile-git>
* Haskell
* hgit2 <https://github.com/jwiegley/gitlib>
* Java
--- /dev/null
+Troubleshooting libgit2 Problems
+================================
+
+CMake Failures
+--------------
+
+* **`Asked for OpenSSL TLS backend, but it wasn't found`**
+ CMake cannot find your SSL/TLS libraries. By default, libgit2 always
+ builds with HTTPS support, and you are encouraged to install the
+ OpenSSL libraries for your system (eg, `apt-get install libssl-dev`).
+
+ For development, if you simply want to disable HTTPS support entirely,
+ pass the `-DUSE_HTTPS=OFF` argument to `cmake` when configuring it.
GITTEST_INVASIVE_FS_SIZE: 1
matrix:
- - GENERATOR: "Visual Studio 11"
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
+ GENERATOR: "Visual Studio 10 2010"
ARCH: 32
- - GENERATOR: "Visual Studio 11 Win64"
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
+ GENERATOR: "Visual Studio 10 2010 Win64"
ARCH: 64
- - GENERATOR: "MSYS Makefiles"
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ GENERATOR: "Visual Studio 14 2015"
+ ARCH: 32
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ GENERATOR: "Visual Studio 14 2015 Win64"
+ ARCH: 64
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ GENERATOR: "MSYS Makefiles"
ARCH: i686 # this is for 32-bit MinGW-w64
- - GENERATOR: "MSYS Makefiles"
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ GENERATOR: "MSYS Makefiles"
ARCH: 64
cache:
- i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
mkdir build
cd build
if ($env:GENERATOR -ne "MSYS Makefiles") {
- cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D MSVC_CRTDBG=ON .. -G"$env:GENERATOR"
+ cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D BUILD_EXAMPLES=ON -D MSVC_CRTDBG=ON .. -G"$env:GENERATOR"
cmake --build . --config Debug
}
- cmd: |
# Run this early so we know it's ready by the time we need it
$proxyJob = Start-Job { java -jar $Env:APPVEYOR_BUILD_FOLDER\build\poxyproxy.jar -d --port 8080 --credentials foo:bar }
ctest -V -R libgit2_clar
- $env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
- $env:GITTEST_REMOTE_USER="libgit2test"
- ctest -V -R libgit2_clar-cred_callback
Receive-Job -Job $proxyJob
- $env:GITTEST_REMOTE_PROXY_URL = "http://foo:bar@localhost:8080"
- ctest -V -R libgit2_clar-proxy_credentials_in_url
- $env:GITTEST_REMOTE_PROXY_URL = "http://localhost:8080"
+ $env:GITTEST_REMOTE_PROXY_URL = "localhost:8080"
$env:GITTEST_REMOTE_PROXY_USER = "foo"
$env:GITTEST_REMOTE_PROXY_PASS = "bar"
- ctest -V -R libgit2_clar-proxy_credentials_request
+ ctest -V -R libgit2_clar-proxy_credentials
--- /dev/null
+MACRO(ENABLE_WARNINGS flag)
+ IF(ENABLE_WERROR)
+ ADD_C_FLAG_IF_SUPPORTED(-Werror=${flag})
+ ELSE()
+ ADD_C_FLAG_IF_SUPPORTED(-W${flag})
+ ENDIF()
+ENDMACRO()
+
+MACRO(DISABLE_WARNINGS flag)
+ ADD_C_FLAG_IF_SUPPORTED(-Wno-${flag})
+ IF(ENABLE_WERROR)
+ ADD_C_FLAG_IF_SUPPORTED(-Wno-error=${flag})
+ ENDIF()
+ENDMACRO()
-IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_DIRS)
- SET(COREFOUNDATION_FOUND TRUE)
-ELSE ()
- FIND_PATH(COREFOUNDATION_INCLUDE_DIR NAMES CoreFoundation.h)
- FIND_LIBRARY(COREFOUNDATION_DIRS NAMES CoreFoundation)
- IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_DIRS)
- SET(COREFOUNDATION_FOUND TRUE)
- ENDIF ()
+# Find CoreFoundation.framework
+# This will define :
+#
+# COREFOUNDATION_FOUND
+# COREFOUNDATION_LIBRARIES
+# COREFOUNDATION_LDFLAGS
+#
+
+FIND_PATH(COREFOUNDATION_INCLUDE_DIR NAMES CoreFoundation.h)
+FIND_LIBRARY(COREFOUNDATION_LIBRARIES NAMES CoreFoundation)
+IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_LIBRARIES)
+ IF (NOT CoreFoundation_FIND_QUIETLY)
+ MESSAGE("-- Found CoreFoundation ${COREFOUNDATION_LIBRARIES}")
+ ENDIF()
+ SET(COREFOUNDATION_FOUND TRUE)
+ SET(COREFOUNDATION_LDFLAGS "-framework CoreFoundation")
ENDIF ()
+
+IF (CoreFoundation_FIND_REQUIRED AND NOT COREFOUNDATION_FOUND)
+ MESSAGE(FATAL "-- CoreFoundation not found")
+ENDIF()
+
+MARK_AS_ADVANCED(
+ COREFOUNDATION_INCLUDE_DIR
+ COREFOUNDATION_LIBRARIES
+)
-IF (SECURITY_INCLUDE_DIR AND SECURITY_DIRS)
- SET(SECURITY_FOUND TRUE)
-ELSE ()
- FIND_PATH(SECURITY_INCLUDE_DIR NAMES Security/Security.h)
- FIND_LIBRARY(SECURITY_DIRS NAMES Security)
- IF (SECURITY_INCLUDE_DIR AND SECURITY_DIRS)
- SET(SECURITY_FOUND TRUE)
- ENDIF ()
+# Find Security.framework
+# This will define :
+#
+# SECURITY_FOUND
+# SECURITY_LIBRARIES
+# SECURITY_LDFLAGS
+# SECURITY_HAS_SSLCREATECONTEXT
+#
+
+FIND_PATH(SECURITY_INCLUDE_DIR NAMES Security/Security.h)
+FIND_LIBRARY(SECURITY_LIBRARIES NAMES Security)
+IF (SECURITY_INCLUDE_DIR AND SECURITY_LIBRARIES)
+ IF (NOT Security_FIND_QUIETLY)
+ MESSAGE("-- Found Security ${SECURITY_LIBRARIES}")
+ ENDIF()
+ SET(SECURITY_FOUND TRUE)
+ SET(SECURITY_LDFLAGS "-framework Security")
+ CHECK_LIBRARY_EXISTS("${SECURITY_LIBRARIES}" SSLCreateContext "Security/SecureTransport.h" SECURITY_HAS_SSLCREATECONTEXT)
ENDIF ()
+
+IF (Security_FIND_REQUIRED AND NOT SECURITY_FOUND)
+ MESSAGE(FATAL "-- Security not found")
+ENDIF()
+
+MARK_AS_ADVANCED(
+ SECURITY_INCLUDE_DIR
+ SECURITY_LIBRARIES
+)
--- /dev/null
+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" ON )
+ENDIF()
--- /dev/null
+# 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 ".*/")
+ STRING(REPLACE ${libgit2_SOURCE_DIR}/ "" rel ${source})
+ IF(rel)
+ STRING(REGEX REPLACE "/([^/]*)$" "" rel ${rel})
+ IF(rel)
+ STRING(REPLACE "/" "\\\\" rel ${rel})
+ SOURCE_GROUP(${rel} FILES ${source})
+ ENDIF()
+ ENDIF()
+ ENDIF()
+ ENDFOREACH()
+ ENDIF()
+ENDFUNCTION()
--- /dev/null
+# Releasing the library
+
+We have three kinds of releases: "full" releases, maintenance releases and security releases. Full ones release the state of the `master` branch whereas maintenance releases provide bugfixes building on top of the currently released series. Security releases are also for the current series but only contain security fixes on top of the previous release.
+
+## Full release
+
+We aim to release once every six months. We start the process by opening an issue. This is accompanied with a feature freeze. From now until the release, only bug fixes are to be merged. Use the following as a base for the issue
+
+ Release v0.X
+
+ Let's release v0.X, codenamed: <something witty>
+
+ - [ ] Bump the versions in the headers
+ - [ ] Make a release candidate
+ - [ ] Plug any final leaks
+ - [ ] Fix any last-minute issues
+ - [ ] Make sure CHANGELOG reflects everything worth discussing
+ - [ ] Update the version in CHANGELOG and the header
+ - [ ] Produce a release candidate
+ - [ ] Tag
+ - [ ] Create maint/v0.X
+ - [ ] Update any bindings the core team works with
+
+We tag at least one release candidate. This RC must carry the new version in the headers, including the SOVERSION. If there are no significant issues found, we can go straight to the release after a single RC. This is up to the discretion of the release manager. There is no set time to have the candidate out, but we should we should give downstream projects at least a week to give feedback.
+
+Preparing the first release candidate includes updating the version number of libgit2 to the new version number. To do so, a pull request shall be submitted that adjusts the version number in the following places:
+
+- CHANGELOG.md
+- include/git2/version.h
+
+As soon as the pull request is merged, the merge commit shall be tagged with a lightweight tag.
+
+The tagging happens via GitHub's "releases" tab which lets us attach release notes to a particular tag. In the description we include the changes in `CHANGELOG.md` between the last full release. Use the following as a base for the release notes
+
+ This is the first release of the v0.X series, <codename>. The changelog follows.
+
+followed by the three sections in the changelog. For release candidates we can avoid copying the full changelog and only include any new entries.
+
+During the freeze, and certainly after the first release candidate, any bindings the core team work with should be updated in order to discover any issues that might come up with the multitude of approaches to memory management, embedding or linking.
+
+Create a branch `maint/v0.X` at the current state of `master` after you've created the tag. This will be used for maintenance releases and lets our dependents track the latest state of the series.
+
+## Maintenance release
+
+Every once in a while, when we feel we've accumulated a significant amount of backportable fixes in the mainline branch, we produce a maintenance release in order to provide fixes or improvements for those who track the releases. This also lets our users and integrators receive updates without having to upgrade to the next full release.
+
+As a rule of thumb, it's a good idea to produce a maintenance release for the current series when we're getting ready for a full release. This gives the (still) current series a last round of fixes without having to upgrade (which with us potentially means adjusting to API changes).
+
+Start by opening an issue. Use the following as a base.
+
+ Release v0.X.Y
+
+ Enough fixes have accumulated, let's release v0.X.Y
+
+ - [ ] Select the changes we want to backport
+ - [ ] Update maint/v0.X
+ - [ ] Tag
+
+The list of changes to backport does not need to be comprehensive and we might not backport something if the code in mainline has diverged significantly. These fixes do not include those which require API or ABI changes as we release under the same SOVERSION.
+
+Do not merge into the `maint/v0.X` until we are getting ready to produce a new release. There is always the possibility that we will need to produce a security release and those must only include the relevant security fixes and not arbitrary fixes we were planning on releasing at some point.
+
+Here we do not use release candidates as the changes are supposed to be small and proven.
+
+## Security releases
+
+This is the same as a maintenance release, except that the fix itself will most likely be developed in a private repository and will only be visible to a select group of people until the release.
+
+Everything else remains the same. Occasionally we might opt to backport a security fix to the previous series, based on how recently we started the new series and how serious the issue is.
+
+## Updating documentation
+
+We use docurium to generate our documentation. It is a tool written in ruby which leverages libclang's documentation parser. Install docurium
+
+ gem install docurium
+
+and run it against our description file with the tip of master checked out.
+
+ cm doc api.docurium
+
+It will start up a few proceses and write out the results as a new commit onto the `gh-pages` branch. That can be pushed to GitHub to update what will show up on our documentation reference site.
+++ /dev/null
-general
-showindex
-diff
-rev-list
-blame
-cat-file
-init
-log
-rev-parse
-remote
-status
-tag
-for-each-ref
-describe
-*.dSYM
-FILE(GLOB_RECURSE SRC_EXAMPLE_GIT2 network/*.c network/*.h)
+LINK_DIRECTORIES(${LIBGIT2_LIBDIRS})
+INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES})
+
+FILE(GLOB_RECURSE SRC_EXAMPLE_GIT2 network/*.c network/*.h common.?)
ADD_EXECUTABLE(cgit2 ${SRC_EXAMPLE_GIT2})
IF(WIN32 OR ANDROID)
TARGET_LINK_LIBRARIES(cgit2 git2)
+++ /dev/null
-.PHONY: all
-
-CC = gcc
-CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
-LFLAGS = -L../build -lgit2 -lz
-APPS = general showindex diff rev-list cat-file status log rev-parse init blame tag remote
-APPS += for-each-ref
-APPS += describe
-
-all: $(APPS)
-
-% : %.c
- $(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
-
-clean:
- $(RM) $(APPS)
- $(RM) -r *.dSYM
git_object_free(obj);
}
+void *xrealloc(void *oldp, size_t newsz)
+{
+ void *p = realloc(oldp, newsz);
+ if (p == NULL) {
+ fprintf(stderr, "Cannot allocate memory, exiting.\n");
+ exit(1);
+ }
+ return p;
+}
+
*/
extern void treeish_to_tree(
git_tree **out, git_repository *repo, const char *treeish);
+
+/**
+ * A realloc that exits on failure
+ */
+extern void *xrealloc(void *oldp, size_t newsz);
typedef struct args_info args_info;
-static void *xrealloc(void *oldp, size_t newsz)
-{
- void *p = realloc(oldp, newsz);
- if (p == NULL) {
- fprintf(stderr, "Cannot allocate memory, exiting.\n");
- exit(1);
- }
- return p;
-}
-
static void opts_add_commit(describe_options *opts, const char *commit)
{
size_t sz;
int32_t autocorrect;
git_config *cfg;
git_config *snap_cfg;
+ int error_code;
printf("\n*Config Listing*\n");
git_config_get_string(&email, snap_cfg, "user.email");
printf("Email: %s\n", email);
- /**
- * Remember to free the configurations after usage.
- */
+ error_code = git_config_get_int32(&autocorrect, cfg, "help.autocorrect");
+ switch (error_code)
+ {
+ case 0:
+ printf("Autocorrect: %d\n", autocorrect);
+ break;
+ case GIT_ENOTFOUND:
+ printf("Autocorrect: Undefined\n");
+ break;
+ default:
+ check_error(error_code, "get_int32 failed");
+ }
git_config_free(cfg);
+
+ check_error(git_repository_config_snapshot(&snap_cfg, repo), "config snapshot");
+ error_code = git_config_get_string(&email, snap_cfg, "user.email");
+ switch (error_code)
+ {
+ case 0:
+ printf("Email: %s\n", email);
+ break;
+ case GIT_ENOTFOUND:
+ printf("Email: Undefined\n");
+ break;
+ default:
+ check_error(error_code, "get_string failed");
+ }
+
git_config_free(snap_cfg);
}
/** log_options holds other command line options that affect log output */
struct log_options {
int show_diff;
+ int show_log_size;
int skip, limit;
int min_parents, max_parents;
git_time_t before;
static int parse_options(
struct log_state *s, struct log_options *opt, int argc, char **argv);
static void print_time(const git_time *intime, const char *prefix);
-static void print_commit(git_commit *commit);
+static void print_commit(git_commit *commit, struct log_options *opts);
static int match_with_parent(git_commit *commit, int i, git_diff_options *);
/** utility functions for filtering */
break;
}
- print_commit(commit);
+ print_commit(commit, &opt);
if (opt.show_diff) {
git_tree *a = NULL, *b = NULL;
}
/** Helper to print a commit object. */
-static void print_commit(git_commit *commit)
+static void print_commit(git_commit *commit, struct log_options *opts)
{
char buf[GIT_OID_HEXSZ + 1];
int i, count;
git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
printf("commit %s\n", buf);
+ if (opts->show_log_size) {
+ printf("log size %d\n", (int)strlen(git_commit_message(commit)));
+ }
+
if ((count = (int)git_commit_parentcount(commit)) > 1) {
printf("Merge:");
for (i = 0; i < count; ++i) {
/** Found valid --min_parents. */;
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
opt->show_diff = 1;
+ else if (!strcmp(a, "--log-size"))
+ opt->show_log_size = 1;
else
usage("Unsupported argument", a);
}
--- /dev/null
+/*
+ * libgit2 "merge" example - shows how to perform merges
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+#include <assert.h>
+
+#ifdef _MSC_VER
+#define snprintf sprintf_s
+#endif
+
+/** The following example demonstrates how to do merges with libgit2.
+ *
+ * It will merge whatever commit-ish you pass in into the current branch.
+ *
+ * Recognized options are :
+ * --no-commit: don't actually commit the merge.
+ *
+ */
+
+typedef struct {
+ const char **heads;
+ size_t heads_count;
+
+ git_annotated_commit **annotated;
+ size_t annotated_count;
+
+ int no_commit : 1;
+} merge_options;
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: merge [--no-commit] <commit...>\n");
+ exit(1);
+}
+
+static void merge_options_init(merge_options *opts)
+{
+ memset(opts, 0, sizeof(*opts));
+
+ opts->heads = NULL;
+ opts->heads_count = 0;
+ opts->annotated = NULL;
+ opts->annotated_count = 0;
+}
+
+static void opts_add_refish(merge_options *opts, const char *refish)
+{
+ size_t sz;
+
+ assert(opts != NULL);
+
+ sz = ++opts->heads_count * sizeof(opts->heads[0]);
+ opts->heads = xrealloc(opts->heads, sz);
+ opts->heads[opts->heads_count - 1] = refish;
+}
+
+static void parse_options(const char **repo_path, merge_options *opts, int argc, char **argv)
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ if (argc <= 1)
+ print_usage();
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ const char *curr = argv[args.pos];
+
+ if (curr[0] != '-') {
+ opts_add_refish(opts, curr);
+ } else if (!strcmp(curr, "--no-commit")) {
+ opts->no_commit = 1;
+ } else if (match_str_arg(repo_path, &args, "--git-dir")) {
+ continue;
+ } else {
+ print_usage();
+ }
+ }
+}
+
+static int resolve_refish(git_annotated_commit **commit, git_repository *repo, const char *refish)
+{
+ git_reference *ref;
+ int err = 0;
+ git_oid oid;
+
+ assert(commit != NULL);
+
+ err = git_reference_dwim(&ref, repo, refish);
+ if (err == GIT_OK) {
+ git_annotated_commit_from_ref(commit, repo, ref);
+ git_reference_free(ref);
+ return 0;
+ }
+
+ err = git_oid_fromstr(&oid, refish);
+ if (err == GIT_OK) {
+ err = git_annotated_commit_lookup(commit, repo, &oid);
+ }
+
+ return err;
+}
+
+static int resolve_heads(git_repository *repo, merge_options *opts)
+{
+ git_annotated_commit **annotated = calloc(opts->heads_count, sizeof(git_annotated_commit *));
+ size_t annotated_count = 0, i;
+ int err = 0;
+
+ for (i = 0; i < opts->heads_count; i++) {
+ err = resolve_refish(&annotated[annotated_count++], repo, opts->heads[i]);
+ if (err != 0) {
+ fprintf(stderr, "failed to resolve refish %s: %s\n", opts->heads[i], giterr_last()->message);
+ annotated_count--;
+ continue;
+ }
+ }
+
+ if (annotated_count != opts->heads_count) {
+ fprintf(stderr, "unable to parse some refish\n");
+ free(annotated);
+ return -1;
+ }
+
+ opts->annotated = annotated;
+ opts->annotated_count = annotated_count;
+ return 0;
+}
+
+static int perform_fastforward(git_repository *repo, const git_oid *target_oid, int is_unborn)
+{
+ git_checkout_options ff_checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
+ git_reference *target_ref;
+ git_reference *new_target_ref;
+ git_object *target = NULL;
+ int err = 0;
+
+ if (is_unborn) {
+ const char *symbolic_ref;
+ git_reference *head_ref;
+
+ /* HEAD reference is unborn, lookup manually so we don't try to resolve it */
+ err = git_reference_lookup(&head_ref, repo, "HEAD");
+ if (err != 0) {
+ fprintf(stderr, "failed to lookup HEAD ref\n");
+ return -1;
+ }
+
+ /* Grab the reference HEAD should be pointing to */
+ symbolic_ref = git_reference_symbolic_target(head_ref);
+
+ /* Create our master reference on the target OID */
+ err = git_reference_create(&target_ref, repo, symbolic_ref, target_oid, 0, NULL);
+ if (err != 0) {
+ fprintf(stderr, "failed to create master reference\n");
+ return -1;
+ }
+
+ git_reference_free(head_ref);
+ } else {
+ /* HEAD exists, just lookup and resolve */
+ err = git_repository_head(&target_ref, repo);
+ if (err != 0) {
+ fprintf(stderr, "failed to get HEAD reference\n");
+ return -1;
+ }
+ }
+
+ /* Lookup the target object */
+ err = git_object_lookup(&target, repo, target_oid, GIT_OBJ_COMMIT);
+ if (err != 0) {
+ fprintf(stderr, "failed to lookup OID %s\n", git_oid_tostr_s(target_oid));
+ return -1;
+ }
+
+ /* Checkout the result so the workdir is in the expected state */
+ ff_checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
+ err = git_checkout_tree(repo, target, &ff_checkout_options);
+ if (err != 0) {
+ fprintf(stderr, "failed to checkout HEAD reference\n");
+ return -1;
+ }
+
+ /* Move the target reference to the target OID */
+ err = git_reference_set_target(&new_target_ref, target_ref, target_oid, NULL);
+ if (err != 0) {
+ fprintf(stderr, "failed to move HEAD reference\n");
+ return -1;
+ }
+
+ git_reference_free(target_ref);
+ git_reference_free(new_target_ref);
+ git_object_free(target);
+
+ return 0;
+}
+
+static void output_conflicts(git_index *index)
+{
+ git_index_conflict_iterator *conflicts;
+ const git_index_entry *ancestor;
+ const git_index_entry *our;
+ const git_index_entry *their;
+ int err = 0;
+
+ check_lg2(git_index_conflict_iterator_new(&conflicts, index), "failed to create conflict iterator", NULL);
+
+ while ((err = git_index_conflict_next(&ancestor, &our, &their, conflicts)) == 0) {
+ fprintf(stderr, "conflict: a:%s o:%s t:%s\n",
+ ancestor ? ancestor->path : "NULL",
+ our->path ? our->path : "NULL",
+ their->path ? their->path : "NULL");
+ }
+
+ if (err != GIT_ITEROVER) {
+ fprintf(stderr, "error iterating conflicts\n");
+ }
+
+ git_index_conflict_iterator_free(conflicts);
+}
+
+static int create_merge_commit(git_repository *repo, git_index *index, merge_options *opts)
+{
+ git_oid tree_oid, commit_oid;
+ git_tree *tree;
+ git_signature *sign;
+ git_reference *merge_ref = NULL;
+ git_annotated_commit *merge_commit;
+ git_reference *head_ref;
+ git_commit **parents = calloc(opts->annotated_count + 1, sizeof(git_commit *));
+ const char *msg_target = NULL;
+ size_t msglen = 0;
+ char *msg;
+ size_t i;
+ int err;
+
+ /* Grab our needed references */
+ check_lg2(git_repository_head(&head_ref, repo), "failed to get repo HEAD", NULL);
+ if (resolve_refish(&merge_commit, repo, opts->heads[0])) {
+ fprintf(stderr, "failed to resolve refish %s", opts->heads[0]);
+ return -1;
+ }
+
+ /* Maybe that's a ref, so DWIM it */
+ err = git_reference_dwim(&merge_ref, repo, opts->heads[0]);
+ check_lg2(err, "failed to DWIM reference", giterr_last()->message);
+
+ /* Grab a signature */
+ check_lg2(git_signature_now(&sign, "Me", "me@example.com"), "failed to create signature", NULL);
+
+#define MERGE_COMMIT_MSG "Merge %s '%s'"
+ /* Prepare a standard merge commit message */
+ if (merge_ref != NULL) {
+ check_lg2(git_branch_name(&msg_target, merge_ref), "failed to get branch name of merged ref", NULL);
+ } else {
+ msg_target = git_oid_tostr_s(git_annotated_commit_id(merge_commit));
+ }
+
+ msglen = snprintf(NULL, 0, MERGE_COMMIT_MSG, (merge_ref ? "branch" : "commit"), msg_target);
+ if (msglen > 0) msglen++;
+ msg = malloc(msglen);
+ err = snprintf(msg, msglen, MERGE_COMMIT_MSG, (merge_ref ? "branch" : "commit"), msg_target);
+
+ /* This is only to silence the compiler */
+ if (err < 0) goto cleanup;
+
+ /* Setup our parent commits */
+ err = git_reference_peel((git_object **)&parents[0], head_ref, GIT_OBJ_COMMIT);
+ check_lg2(err, "failed to peel head reference", NULL);
+ for (i = 0; i < opts->annotated_count; i++) {
+ git_commit_lookup(&parents[i + 1], repo, git_annotated_commit_id(opts->annotated[i]));
+ }
+
+ /* Prepare our commit tree */
+ check_lg2(git_index_write_tree(&tree_oid, index), "failed to write merged tree", NULL);
+ check_lg2(git_tree_lookup(&tree, repo, &tree_oid), "failed to lookup tree", NULL);
+
+ /* Commit time ! */
+ err = git_commit_create(&commit_oid,
+ repo, git_reference_name(head_ref),
+ sign, sign,
+ NULL, msg,
+ tree,
+ opts->annotated_count + 1, (const git_commit **)parents);
+ check_lg2(err, "failed to create commit", NULL);
+
+ /* We're done merging, cleanup the repository state */
+ git_repository_state_cleanup(repo);
+
+cleanup:
+ free(parents);
+ return err;
+}
+
+int main(int argc, char **argv)
+{
+ git_repository *repo = NULL;
+ merge_options opts;
+ git_index *index;
+ git_repository_state_t state;
+ git_merge_analysis_t analysis;
+ git_merge_preference_t preference;
+ const char *path = ".";
+ int err = 0;
+
+ merge_options_init(&opts);
+ parse_options(&path, &opts, argc, argv);
+
+ git_libgit2_init();
+
+ check_lg2(git_repository_open_ext(&repo, path, 0, NULL),
+ "Could not open repository", NULL);
+
+ state = git_repository_state(repo);
+ if (state != GIT_REPOSITORY_STATE_NONE) {
+ fprintf(stderr, "repository is in unexpected state %d\n", state);
+ goto cleanup;
+ }
+
+ err = resolve_heads(repo, &opts);
+ if (err != 0)
+ goto cleanup;
+
+ err = git_merge_analysis(&analysis, &preference,
+ repo,
+ (const git_annotated_commit **)opts.annotated,
+ opts.annotated_count);
+ check_lg2(err, "merge analysis failed", NULL);
+
+ if (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) {
+ printf("Already up-to-date\n");
+ return 0;
+ } else if (analysis & GIT_MERGE_ANALYSIS_UNBORN ||
+ (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD &&
+ !(preference & GIT_MERGE_PREFERENCE_NO_FASTFORWARD))) {
+ const git_oid *target_oid;
+ if (analysis & GIT_MERGE_ANALYSIS_UNBORN) {
+ printf("Unborn\n");
+ } else {
+ printf("Fast-forward\n");
+ }
+
+ /* Since this is a fast-forward, there can be only one merge head */
+ target_oid = git_annotated_commit_id(opts.annotated[0]);
+ assert(opts.annotated_count == 1);
+
+ return perform_fastforward(repo, target_oid, (analysis & GIT_MERGE_ANALYSIS_UNBORN));
+ } else if (analysis & GIT_MERGE_ANALYSIS_NORMAL) {
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ merge_opts.flags = 0;
+ merge_opts.file_flags = GIT_MERGE_FILE_STYLE_DIFF3;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE|GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+ if (preference & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY) {
+ printf("Fast-forward is preferred, but only a merge is possible\n");
+ return -1;
+ }
+
+ err = git_merge(repo,
+ (const git_annotated_commit **)opts.annotated, opts.annotated_count,
+ &merge_opts, &checkout_opts);
+ check_lg2(err, "merge failed", NULL);
+ }
+
+ /* If we get here, we actually performed the merge above */
+
+ check_lg2(git_repository_index(&index, repo), "failed to get repository index", NULL);
+
+ if (git_index_has_conflicts(index)) {
+ /* Handle conflicts */
+ output_conflicts(index);
+ } else if (!opts.no_commit) {
+ create_merge_commit(repo, index, &opts);
+ printf("Merge made\n");
+ }
+
+cleanup:
+ free(opts.heads);
+ free(opts.annotated);
+ git_repository_free(repo);
+ git_libgit2_shutdown();
+
+ return 0;
+}
# define UNUSED(x) x
#endif
+static int readline(char **out)
+{
+ int c, error = 0, length = 0, allocated = 0;
+ char *line = NULL;
+
+ errno = 0;
+
+ while ((c = getchar()) != EOF) {
+ if (length == allocated) {
+ allocated += 16;
+
+ if ((line = realloc(line, allocated)) == NULL) {
+ error = -1;
+ goto error;
+ }
+ }
+
+ if (c == '\n')
+ break;
+
+ line[length++] = c;
+ }
+
+ if (errno != 0) {
+ error = -1;
+ goto error;
+ }
+
+ line[length] = '\0';
+ *out = line;
+ line = NULL;
+ error = length;
+error:
+ free(line);
+ return error;
+}
+
int cred_acquire_cb(git_cred **out,
const char * UNUSED(url),
const char * UNUSED(username_from_url),
int error;
printf("Username: ");
- if (getline(&username, NULL, stdin) < 0) {
+ if (readline(&username) < 0) {
fprintf(stderr, "Unable to read username: %s", strerror(errno));
return -1;
}
/* Yup. Right there on your terminal. Careful where you copy/paste output. */
printf("Password: ");
- if (getline(&password, NULL, stdin) < 0) {
+ if (readline(&password) < 0) {
fprintf(stderr, "Unable to read password: %s", strerror(errno));
free(username);
return -1;
# include <unistd.h>
#endif
-struct dl_data {
- git_remote *remote;
- git_fetch_options *fetch_opts;
- int ret;
- int finished;
-};
-
static int progress_cb(const char *str, int len, void *data)
{
(void)data;
#include <stdio.h>
#include <string.h>
+#include "../common.h"
#include "common.h"
-// This part is not strictly libgit2-dependent, but you can use this
-// as a starting point for a git-like tool
+/* This part is not strictly libgit2-dependent, but you can use this
+ * as a starting point for a git-like tool */
struct {
char *name;
{ NULL, NULL}
};
-static int run_command(git_cb fn, int argc, char **argv)
+static int run_command(git_cb fn, git_repository *repo, struct args_info args)
{
int error;
- git_repository *repo;
-
- // Before running the actual command, create an instance of the local
- // repository and pass it to the function.
-
- error = git_repository_open(&repo, ".git");
- if (error < 0)
- repo = NULL;
- // Run the command. If something goes wrong, print the error message to stderr
- error = fn(repo, argc, argv);
+ /* Run the command. If something goes wrong, print the error message to stderr */
+ error = fn(repo, args.argc - args.pos, &args.argv[args.pos]);
if (error < 0) {
if (giterr_last() == NULL)
fprintf(stderr, "Error without message");
fprintf(stderr, "Bad news:\n %s\n", giterr_last()->message);
}
- if(repo)
- git_repository_free(repo);
-
return !!error;
}
{
int i;
int return_code = 1;
+ int error;
+ git_repository *repo;
+ struct args_info args = ARGS_INFO_INIT;
+ const char *git_dir = NULL;
if (argc < 2) {
fprintf(stderr, "usage: %s <cmd> [repo]\n", argv[0]);
git_libgit2_init();
+ for (args.pos = 1; args.pos < args.argc; ++args.pos) {
+ char *a = args.argv[args.pos];
+
+ if (a[0] != '-') {
+ /* non-arg */
+ break;
+ } else if (optional_str_arg(&git_dir, &args, "--git-dir", ".git")) {
+ continue;
+ } else if (!strcmp(a, "--")) {
+ /* arg separator */
+ break;
+ }
+ }
+
+ /* Before running the actual command, create an instance of the local
+ * repository and pass it to the function. */
+
+ error = git_repository_open(&repo, git_dir);
+ if (error < 0)
+ repo = NULL;
+
for (i = 0; commands[i].name != NULL; ++i) {
- if (!strcmp(argv[1], commands[i].name)) {
- return_code = run_command(commands[i].fn, --argc, ++argv);
+ if (!strcmp(args.argv[args.pos], commands[i].name)) {
+ return_code = run_command(commands[i].fn, repo, args);
goto shutdown;
}
}
fprintf(stderr, "Command not found: %s\n", argv[1]);
shutdown:
+ git_repository_free(repo);
+
git_libgit2_shutdown();
return return_code;
ok Johannes Schindelin <Johannes.Schindelin@gmx.de>
ok Johannes Sixt <j6t@kdbg.org>
ask Jonathan Nieder <jrnieder@gmail.com>
+ok Jonathan Tan <jonathantanmy@google.com>
ok Junio C Hamano <gitster@pobox.com>
ok Kristian Høgsberg <krh@redhat.com>
ok Linus Torvalds <torvalds@linux-foundation.org>
#include "git2/tree.h"
#include "git2/types.h"
#include "git2/version.h"
+#include "git2/worktree.h"
#endif
* @param path path to the configuration file to add
* @param level the priority level of the backend
* @param force replace config file at the given priority level
+ * @param repo optional repository to allow parsing of
+ * conditional includes
* @return 0 on success, GIT_EEXISTS when adding more than one file
* for a given priority level (and force_replace set to 0),
* GIT_ENOTFOUND when the file doesn't exist or error code
git_config *cfg,
const char *path,
git_config_level_t level,
+ const git_repository *repo,
int force);
/**
*
* The callback will be called on each variable found
*
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
* @param cfg where to look for the variable
* @param name the variable's name
* @param regexp regular expression to filter which variables we're
/**
* Get each value of a multivar
*
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
* @param out pointer to store the iterator
* @param cfg where to look for the variable
* @param name the variable's name
/**
* Set a multivar in the local config file.
*
+ * The regular expression is applied case-sensitively on the value.
+ *
* @param cfg where to look for the variable
* @param name the variable's name
* @param regexp a regular expression to indicate which values to replace
/**
* Deletes one or several entries from a multivar in the local config file.
*
+ * The regular expression is applied case-sensitively on the value.
+ *
* @param cfg where to look for the variables
* @param name the variable's name
* @param regexp a regular expression to indicate which values to delete
* Use `git_config_next` to advance the iteration and
* `git_config_iterator_free` when done.
*
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
* @param out pointer to store the iterator
* @param cfg where to ge the variables from
* @param regexp regular expression to match the names
* regular expression that filters which config keys are passed to the
* callback.
*
- * The pointers passed to the callback are only valid as long as the
- * iteration is ongoing.
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the case-insensitive parts are lower-case.
*
* @param cfg where to get the variables from
* @param regexp regular expression to match against config names
* This behaviors like `git_config_foreach_match` except instead of all config
* entries it just enumerates through the given backend entry.
*
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
* @param backend where to get the variables from
* @param regexp regular expression to match against config names (can be NULL)
* @param callback the function to call on each variable
/**
* Reference lookup strategy
*
- * These behave like the --tags and --all optios to git-describe,
+ * These behave like the --tags and --all options to git-describe,
* namely they say to look for any reference in either refs/tags/ or
* refs/ respectively.
*/
/** Use the "patience diff" algorithm */
GIT_DIFF_PATIENCE = (1u << 28),
/** Take extra time to find minimal diff */
- GIT_DIFF_MINIMAL = (1 << 29),
+ GIT_DIFF_MINIMAL = (1u << 29),
/** Include the necessary deflate / delta information so that `git-apply`
* can apply given diff information to binary files.
*/
- GIT_DIFF_SHOW_BINARY = (1 << 30),
+ GIT_DIFF_SHOW_BINARY = (1u << 30),
+
+ /** Use a heuristic that takes indentation and whitespace into account
+ * which generally can produce better diffs when dealing with ambiguous
+ * diff hunks.
+ */
+ GIT_DIFF_INDENT_HEURISTIC = (1u << 31),
} git_diff_option_t;
/**
* Structure describing a hunk of a diff.
*/
typedef struct {
- int old_start; /** Starting line number in old_file */
- int old_lines; /** Number of lines in old_file */
- int new_start; /** Starting line number in new_file */
- int new_lines; /** Number of lines in new_file */
- size_t header_len; /** Number of bytes in header text */
- char header[GIT_DIFF_HUNK_HEADER_SIZE]; /** Header text, NUL-byte terminated */
+ int old_start; /**< Starting line number in old_file */
+ int old_lines; /**< Number of lines in old_file */
+ int new_start; /**< Starting line number in new_file */
+ int new_lines; /**< Number of lines in new_file */
+ size_t header_len; /**< Number of bytes in header text */
+ char header[GIT_DIFF_HUNK_HEADER_SIZE]; /**< Header text, NUL-byte terminated */
} git_diff_hunk;
/**
git_diff_format_email_options *opts,
unsigned int version);
+/**
+ * Patch ID options structure
+ *
+ * Initialize with `GIT_DIFF_PATCHID_OPTIONS_INIT` macro to
+ * correctly set the default values and version.
+ */
+typedef struct git_diff_patchid_options {
+ unsigned int version;
+} git_diff_patchid_options;
+
+#define GIT_DIFF_PATCHID_OPTIONS_VERSION 1
+#define GIT_DIFF_PATCHID_OPTIONS_INIT { GIT_DIFF_PATCHID_OPTIONS_VERSION }
+
+/**
+ * Initialize `git_diff_patchid_options` structure.
+ *
+ * Initializes the structure with default values. Equivalent to
+ * creating an instance with `GIT_DIFF_PATCHID_OPTIONS_INIT`.
+ */
+GIT_EXTERN(int) git_diff_patchid_init_options(
+ git_diff_patchid_options *opts,
+ unsigned int version);
+
+/**
+ * Calculate the patch ID for the given patch.
+ *
+ * Calculate a stable patch ID for the given patch by summing the
+ * hash of the file diffs, ignoring whitespace and line numbers.
+ * This can be used to derive whether two diffs are the same with
+ * a high probability.
+ *
+ * Currently, this function only calculates stable patch IDs, as
+ * defined in git-patch-id(1), and should in fact generate the
+ * same IDs as the upstream git project does.
+ *
+ * @param out Pointer where the calculated patch ID shoul be
+ * stored
+ * @param diff The diff to calculate the ID for
+ * @param opts Options for how to calculate the patch ID. This is
+ * intended for future changes, as currently no options are
+ * available.
+ * @return 0 on success, an error code otherwise.
+ */
+GIT_EXTERN(int) git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts);
+
GIT_END_DECL
/** @} */
/**
* Determine if a commit is the descendant of another commit.
*
+ * Note that a commit is not considered a descendant of itself, in contrast
+ * to `git merge-base --is-ancestor`.
+ *
* @param commit a previously loaded commit.
* @param ancestor a potential ancestor commit.
* @return 1 if the given commit is a descendant of the potential ancestor,
GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7),
} git_merge_file_flag_t;
+#define GIT_MERGE_CONFLICT_MARKER_SIZE 7
+
/**
* Options for merging a file
*/
/** see `git_merge_file_flag_t` above */
git_merge_file_flag_t flags;
+
+ /** The size of conflict markers (eg, "<<<<<<<"). Default is
+ * GIT_MERGE_CONFLICT_MARKER_SIZE. */
+ unsigned short marker_size;
} git_merge_file_options;
#define GIT_MERGE_FILE_OPTIONS_VERSION 1
GIT_BEGIN_DECL
/**
- * Clean up message from excess whitespace and make sure that the last line
- * ends with a '\n'.
+ * Clean up excess whitespace and make sure there is a trailing newline in the message.
*
- * Optionally, can remove lines starting with a "#".
+ * Optionally, it can remove lines which start with the comment character.
*
* @param out The user-allocated git_buf which will be filled with the
* cleaned up message.
*/
GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char);
+/**
+ * Represents a single git message trailer.
+ */
+typedef struct {
+ const char *key;
+ const char *value;
+} git_message_trailer;
+
+/**
+ * Represents an array of git message trailers.
+ *
+ * Struct members under the private comment are private, subject to change
+ * and should not be used by callers.
+ */
+typedef struct {
+ git_message_trailer *trailers;
+ size_t count;
+
+ /* private */
+ char *_trailer_block;
+} git_message_trailer_array;
+
+/**
+ * Parse trailers out of a message, filling the array pointed to by +arr+.
+ *
+ * Trailers are key/value pairs in the last paragraph of a message, not
+ * including any patches or conflicts that may be present.
+ *
+ * @param arr A pre-allocated git_message_trailer_array struct to be filled in
+ * with any trailers found during parsing.
+ * @param message The message to be parsed
+ * @return 0 on success, or non-zero on error.
+ */
+GIT_EXTERN(int) git_message_trailers(git_message_trailer_array *arr, const char *message);
+
+/**
+ * Clean's up any allocated memory in the git_message_trailer_array filled by
+ * a call to git_message_trailers.
+ */
+GIT_EXTERN(void) git_message_trailer_array_free(git_message_trailer_array *arr);
+
/** @} */
GIT_END_DECL
git_repository *repo,
const char *notes_ref);
+/**
+ * Creates a new iterator for notes from a commit
+ *
+ * The iterator must be freed manually by the user.
+ *
+ * @param out pointer to the iterator
+ * @param notes_commit a pointer to the notes commit object
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_iterator_new(
+ git_note_iterator **out,
+ git_commit *notes_commit);
+
/**
* Frees an git_note_iterator
*
const char *notes_ref,
const git_oid *oid);
+
+/**
+ * Read the note for an object from a note commit
+ *
+ * The note must be freed manually by the user.
+ *
+ * @param out pointer to the read note; NULL in case of error
+ * @param repo repository where to look up the note
+ * @param notes_commit a pointer to the notes commit object
+ * @param oid OID of the git object to read the note from
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_read(
+ git_note **out,
+ git_repository *repo,
+ git_commit *notes_commit,
+ const git_oid *oid);
+
/**
* Get the note author
*
const char *note,
int force);
+/**
+ * Add a note for an object from a commit
+ *
+ * This function will create a notes commit for a given object,
+ * the commit is a dangling commit, no reference is created.
+ *
+ * @param notes_commit_out pointer to store the commit (optional);
+ * NULL in case of error
+ * @param notes_blob_out a point to the id of a note blob (optional)
+ * @param repo repository where the note will live
+ * @param parent Pointer to parent note
+ * or NULL if this shall start a new notes tree
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param oid OID of the git object to decorate
+ * @param note Content of the note to add for object oid
+ * @param allow_note_overwrite Overwrite existing note
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_create(
+ git_oid *notes_commit_out,
+ git_oid *notes_blob_out,
+ git_repository *repo,
+ git_commit *parent,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid,
+ const char *note,
+ int allow_note_overwrite);
/**
* Remove the note for an object
const git_signature *committer,
const git_oid *oid);
+/**
+ * Remove the note for an object
+ *
+ * @param notes_commit_out pointer to store the new notes commit (optional);
+ * NULL in case of error.
+ * When removing a note a new tree containing all notes
+ * sans the note to be removed is created and a new commit
+ * pointing to that tree is also created.
+ * In the case where the resulting tree is an empty tree
+ * a new commit pointing to this empty tree will be returned.
+ * @param repo repository where the note lives
+ * @param notes_commit a pointer to the notes commit object
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param oid OID of the git object to remove the note from
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_remove(
+ git_oid *notes_commit_out,
+ git_repository *repo,
+ git_commit *notes_commit,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid);
+
/**
* Free a git_note object
*
* @see git_odb_stream
*
* @param out pointer where to store the stream
+ * @param len pointer where to store the length of the object
+ * @param type pointer where to store the type of the object
* @param db object database where the stream will read from
* @param oid oid of the object the stream will read from
* @return 0 if the stream was created; error code otherwise
*/
-GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **out, git_odb *db, const git_oid *oid);
+GIT_EXTERN(int) git_odb_open_rstream(
+ git_odb_stream **out,
+ size_t *len,
+ git_otype *type,
+ git_odb *db,
+ const git_oid *oid);
/**
* Open a stream for writing a pack file to the ODB.
git_patch **out,
const git_blob *old_blob,
const char *old_as_path,
- const char *buffer,
+ const void *buffer,
size_t buffer_len,
const char *buffer_as_path,
const git_diff_options *opts);
const void *old_buffer,
size_t old_len,
const char *old_as_path,
- const char *new_buffer,
+ const void *new_buffer,
size_t new_len,
const char *new_as_path,
const git_diff_options *opts);
/**
* Options controlling how pathspec match should be executed
- *
- * - GIT_PATHSPEC_IGNORE_CASE forces match to ignore case; otherwise
- * match will use native case sensitivity of platform filesystem
- * - GIT_PATHSPEC_USE_CASE forces case sensitive match; otherwise
- * match will use native case sensitivity of platform filesystem
- * - GIT_PATHSPEC_NO_GLOB disables glob patterns and just uses simple
- * string comparison for matching
- * - GIT_PATHSPEC_NO_MATCH_ERROR means the match functions return error
- * code GIT_ENOTFOUND if no matches are found; otherwise no matches is
- * still success (return 0) but `git_pathspec_match_list_entrycount`
- * will indicate 0 matches.
- * - GIT_PATHSPEC_FIND_FAILURES means that the `git_pathspec_match_list`
- * should track which patterns matched which files so that at the end of
- * the match we can identify patterns that did not match any files.
- * - GIT_PATHSPEC_FAILURES_ONLY means that the `git_pathspec_match_list`
- * does not need to keep the actual matching filenames. Use this to
- * just test if there were any matches at all or in combination with
- * GIT_PATHSPEC_FIND_FAILURES to validate a pathspec.
*/
typedef enum {
GIT_PATHSPEC_DEFAULT = 0,
+
+ /**
+ * GIT_PATHSPEC_IGNORE_CASE forces match to ignore case; otherwise
+ * match will use native case sensitivity of platform filesystem
+ */
GIT_PATHSPEC_IGNORE_CASE = (1u << 0),
+
+ /**
+ * GIT_PATHSPEC_USE_CASE forces case sensitive match; otherwise
+ * match will use native case sensitivity of platform filesystem
+ */
GIT_PATHSPEC_USE_CASE = (1u << 1),
+
+ /**
+ * GIT_PATHSPEC_NO_GLOB disables glob patterns and just uses simple
+ * string comparison for matching
+ */
GIT_PATHSPEC_NO_GLOB = (1u << 2),
+
+ /**
+ * GIT_PATHSPEC_NO_MATCH_ERROR means the match functions return error
+ * code GIT_ENOTFOUND if no matches are found; otherwise no matches is
+ * still success (return 0) but `git_pathspec_match_list_entrycount`
+ * will indicate 0 matches.
+ */
GIT_PATHSPEC_NO_MATCH_ERROR = (1u << 3),
+
+ /**
+ * GIT_PATHSPEC_FIND_FAILURES means that the `git_pathspec_match_list`
+ * should track which patterns matched which files so that at the end of
+ * the match we can identify patterns that did not match any files.
+ */
GIT_PATHSPEC_FIND_FAILURES = (1u << 4),
+
+ /**
+ * GIT_PATHSPEC_FAILURES_ONLY means that the `git_pathspec_match_list`
+ * does not need to keep the actual matching filenames. Use this to
+ * just test if there were any matches at all or in combination with
+ * GIT_PATHSPEC_FIND_FAILURES to validate a pathspec.
+ */
GIT_PATHSPEC_FAILURES_ONLY = (1u << 5),
} git_pathspec_flag_t;
* passed to this method. Returning a non-zero value from the callback
* will terminate the iteration.
*
+ * Note that the callback function is responsible to call `git_reference_free`
+ * on each reference passed to it.
+ *
* @param repo Repository where to find the refs
* @param callback Function which will be called for every listed ref
* @param payload Additional data to pass to the callback
git_repository *repo,
const char *url);
+/**
+ * Create a remote without a connected local repo
+ *
+ * Create a remote with the given url in-memory. You can use this when
+ * you have a URL instead of a remote's name.
+ *
+ * Contrasted with git_remote_create_anonymous, a detached remote
+ * will not consider any repo configuration values (such as insteadof url
+ * substitutions).
+ *
+ * @param out pointer to the new remote objects
+ * @param url the remote repository's URL
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_create_detached(
+ git_remote **out,
+ const char *url);
+
/**
* Get the information for a particular remote
*
*/
typedef int (*git_push_negotiation)(const git_push_update **updates, size_t len, void *payload);
+/**
+ * Callback used to inform of the update status from the remote.
+ *
+ * Called for each updated reference on push. If `status` is
+ * not `NULL`, the update was rejected by the remote server
+ * and `status` contains the reason given.
+ *
+ * @param refname refname specifying to the remote ref
+ * @param status status message sent from the remote
+ * @param data data provided by the caller
+ * @return 0 on success, otherwise an error
+ */
+typedef int (*git_push_update_reference_cb)(const char *refname, const char *status, void *data);
+
/**
* The callback settings structure
*
git_push_transfer_progress push_transfer_progress;
/**
- * Called for each updated reference on push. If `status` is
- * not `NULL`, the update was rejected by the remote server
- * and `status` contains the reason given.
+ * See documentation of git_push_update_reference_cb
*/
- int (*push_update_reference)(const char *refname, const char *status, void *data);
+ git_push_update_reference_cb push_update_reference;
/**
* Called once between the negotiation step and the upload. It
* @param item The repository item for which to retrieve the path
* @return 0, GIT_ENOTFOUND if the path cannot exist or an error code
*/
-GIT_EXTERN(int) git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item);
+GIT_EXTERN(int) git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item);
/**
* Get the path of this repository
* @param repo A repository object
* @return the path to the repository
*/
-GIT_EXTERN(const char *) git_repository_path(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_path(const git_repository *repo);
/**
* Get the path of the working directory for this repository
* @param repo A repository object
* @return the path to the working dir, if it exists
*/
-GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_workdir(const git_repository *repo);
/**
* Get the path of the shared common directory for this repository
* @param repo A repository object
* @return the path to the common dir
*/
-GIT_EXTERN(const char *) git_repository_commondir(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_commondir(const git_repository *repo);
/**
* Set the path to the working directory for this repository
* @param repo Repo to test
* @return 1 if the repository is bare, 0 otherwise.
*/
-GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
+GIT_EXTERN(int) git_repository_is_bare(const git_repository *repo);
/**
* Check if a repository is a linked work tree
* @param repo Repo to test
* @return 1 if the repository is a linked work tree, 0 otherwise.
*/
-GIT_EXTERN(int) git_repository_is_worktree(git_repository *repo);
+GIT_EXTERN(int) git_repository_is_worktree(const git_repository *repo);
/**
* Get the configuration file for this repository.
*/
GIT_EXTERN(int) git_reset(
git_repository *repo,
- git_object *target,
+ const git_object *target,
git_reset_t reset_type,
const git_checkout_options *checkout_opts);
*/
GIT_EXTERN(int) git_reset_from_annotated(
git_repository *repo,
- git_annotated_commit *commit,
+ const git_annotated_commit *commit,
git_reset_t reset_type,
const git_checkout_options *checkout_opts);
*/
GIT_EXTERN(int) git_reset_default(
git_repository *repo,
- git_object *target,
- git_strarray* pathspecs);
+ const git_object *target,
+ const git_strarray* pathspecs);
/** @} */
GIT_END_DECL
* The `pathspec` is an array of path patterns to match (using
* fnmatch-style matching), or just an array of paths to match exactly if
* `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified in the flags.
+ *
+ * The `baseline` is the tree to be used for comparison to the working directory
+ * and index; defaults to HEAD.
*/
typedef struct {
unsigned int version;
git_status_show_t show;
unsigned int flags;
git_strarray pathspec;
+ git_tree *baseline;
} git_status_options;
#define GIT_STATUS_OPTIONS_VERSION 1
struct git_config *cfg;
/* Open means open the file/database and parse if necessary */
- int (*open)(struct git_config_backend *, git_config_level_t level);
+ int (*open)(struct git_config_backend *, git_config_level_t level, const git_repository *repo);
int (*get)(struct git_config_backend *, const char *key, git_config_entry **entry);
int (*set)(struct git_config_backend *, const char *key, const char *value);
int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
* @param cfg the configuration to add the file to
* @param file the configuration file (backend) to add
* @param level the priority level of the backend
+ * @param repo optional repository to allow parsing of
+ * conditional includes
* @param force if a config file already exists for the given
* priority level, replace it
* @return 0 on success, GIT_EEXISTS when adding more than one file
git_config *cfg,
git_config_backend *file,
git_config_level_t level,
+ const git_repository *repo,
int force);
/** @} */
#include "git2/types.h"
#include "git2/oid.h"
#include "git2/odb.h"
+#include "git2/buffer.h"
/**
* @file git2/sys/mempack.h
* Subsequent reads will also be served from the in-memory store
* to ensure consistency, until the memory store is dumped.
*
- * @param out Poiter where to store the ODB backend
+ * @param out Pointer where to store the ODB backend
* @return 0 on success; error code otherwise
*/
-int git_mempack_new(git_odb_backend **out);
+GIT_EXTERN(int) git_mempack_new(git_odb_backend **out);
/**
* Dump all the queued in-memory writes to a packfile.
* @param backend The mempack backend
* @return 0 on success; error code otherwise
*/
-int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backend);
+GIT_EXTERN(int) git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backend);
/**
* Reset the memory packer by clearing all the queued objects.
*
* @param backend The mempack backend
*/
-void git_mempack_reset(git_odb_backend *backend);
+GIT_EXTERN(void) git_mempack_reset(git_odb_backend *backend);
GIT_END_DECL
git_odb_stream **, git_odb_backend *, git_off_t, git_otype);
int (* readstream)(
- git_odb_stream **, git_odb_backend *, const git_oid *);
+ git_odb_stream **, size_t *, git_otype *,
+ git_odb_backend *, const git_oid *);
int (* exists)(
git_odb_backend *, const git_oid *);
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_sys_git_transport_h
-#define INCLUDE_sys_git_transport_h
-
-#include "git2/net.h"
-#include "git2/types.h"
-
-GIT_BEGIN_DECL
-
-GIT_END_DECL
* pointer may not be valid past the next operation in this
* builder. Duplicate the entry if you want to keep it.
*
- * No attempt is being made to ensure that the provided oid points
- * to an existing git object in the object database, nor that the
- * attributes make sense regarding the type of the pointed at object.
+ * By default the entry that you are inserting will be checked for
+ * validity; that it exists in the object database and is of the
+ * correct type. If you do not want this behavior, set the
+ * `GIT_OPT_ENABLE_STRICT_OBJECT_CREATION` library option to false.
*
* @param out Pointer to store the entry (optional)
* @param bld Tree builder
typedef struct git_time {
git_time_t time; /**< time in seconds from epoch */
int offset; /**< timezone offset, in minutes */
+ char sign; /**< indicator for questionable '-0000' offsets in signature */
} git_time;
/** An action signature (e.g. for committers, taggers, etc) */
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
-#define LIBGIT2_VERSION "0.26.0"
+#define LIBGIT2_VERSION "0.27.0"
#define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 26
+#define LIBGIT2_VER_MINOR 27
#define LIBGIT2_VER_REVISION 0
#define LIBGIT2_VER_PATCH 0
-#define LIBGIT2_SOVERSION 26
+#define LIBGIT2_SOVERSION 27
#endif
* @param reason Reason why the working tree is being locked
* @return 0 on success, non-zero otherwise
*/
-GIT_EXTERN(int) git_worktree_lock(git_worktree *wt, char *reason);
+GIT_EXTERN(int) git_worktree_lock(git_worktree *wt, const char *reason);
/**
* Unlock a locked worktree
cd build
gcc --version
cmake --version
-cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON .. -G"$GENERATOR"
+cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D BUILD_EXAMPLES=ON .. -G"$GENERATOR"
cmake --build . --config RelWithDebInfo
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
export PKG_CONFIG_PATH=$(ls -d /usr/local/Cellar/{curl,zlib}/*/lib/pkgconfig | paste -s -d':' -)
+
+ # Set up a ramdisk for us to put our test data on to speed up tests on macOS
+ export CLAR_TMP="$HOME"/_clar_tmp
+ mkdir -p $CLAR_TMP
+
+ # 5*2M sectors aka ~5GB of space
+ device=$(hdiutil attach -nomount ram://$((5 * 2 * 1024 * 1024)))
+ newfs_hfs $device
+ mount -t hfs $device $CLAR_TMP
fi
# Should we ask Travis to cache this file?
killall git-daemon
-if [ "$TRAVIS_OS_NAME" = "osx" ]; then
- echo 'PasswordAuthentication yes' | sudo tee -a /etc/sshd_config
-fi
-
+# Set up sshd
+mkdir ~/sshd/
+cat >~/sshd/sshd_config<<-EOF
+ Port 2222
+ ListenAddress 0.0.0.0
+ Protocol 2
+ HostKey ${HOME}/sshd/id_rsa
+ PidFile ${HOME}/sshd/pid
+ RSAAuthentication yes
+ PasswordAuthentication yes
+ PubkeyAuthentication yes
+ ChallengeResponseAuthentication no
+ # Required here as sshd will simply close connection otherwise
+ UsePAM no
+EOF
+ssh-keygen -t rsa -f ~/sshd/id_rsa -N "" -q
+/usr/sbin/sshd -f ~/sshd/sshd_config
+
+# Set up keys
ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
-ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
+while read algorithm key comment; do
+ echo "[localhost]:2222 $algorithm $key" >>~/.ssh/known_hosts
+done <~/sshd/id_rsa.pub
# Get the fingerprint for localhost and remove the colons so we can parse it as
# a hex number. The Mac version is newer so it has a different output format.
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
- export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -E md5 -F localhost -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
+ export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -E md5 -F '[localhost]:2222' -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
else
- export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -F localhost -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
+ export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -F '[localhost]:2222' -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
fi
-export GITTEST_REMOTE_URL="ssh://localhost/$HOME/_temp/test.git"
+# Use the SSH server
+export GITTEST_REMOTE_URL="ssh://localhost:2222/$HOME/_temp/test.git"
export GITTEST_REMOTE_USER=$USER
export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa"
export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE=""
+ctest -V -R libgit2_clar-ssh || exit $?
+# Use the proxy we started at the beginning
+export GITTEST_REMOTE_PROXY_URL="localhost:8080"
+export GITTEST_REMOTE_PROXY_USER="foo"
+export GITTEST_REMOTE_PROXY_PASS="bar"
+ctest -V -R libgit2_clar-proxy_credentials || exit $?
-if [ -e ./libgit2_clar ]; then
- ./libgit2_clar -sonline::push -sonline::clone::ssh_cert &&
- ./libgit2_clar -sonline::clone::ssh_with_paths || exit $?
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
- ./libgit2_clar -sonline::clone::cred_callback || exit $?
- fi
-
- # Use the proxy we started at the beginning
- export GITTEST_REMOTE_PROXY_URL="http://foo:bar@localhost:8080/"
- ./libgit2_clar -sonline::clone::proxy_credentials_in_url || exit $?
- export GITTEST_REMOTE_PROXY_URL="http://localhost:8080/"
- export GITTEST_REMOTE_PROXY_USER="foo"
- export GITTEST_REMOTE_PROXY_PASS="bar"
- ./libgit2_clar -sonline::clone::proxy_credentials_request || exit $?
-
-fi
-
-export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
-export GITTEST_REMOTE_USER="libgit2test"
-ctest -V -R libgit2_clar-cred_callback
+kill $(cat "$HOME/sshd/pid")
--- /dev/null
+IF(DEBUG_POOL)
+ SET(GIT_DEBUG_POOL 1)
+ENDIF()
+ADD_FEATURE_INFO(debugpool GIT_DEBUG_POOL "debug pool allocator")
+
+# 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.
+SET(LIBGIT2_PC_REQUIRES "")
+# This will be set later if we use the system's http-parser library or
+# use iconv (OSX) and will be written to the Libs.private field in the
+# pc file.
+SET(LIBGIT2_PC_LIBS "")
+
+SET(LIBGIT2_INCLUDES
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ "${libgit2_SOURCE_DIR}/src"
+ "${libgit2_SOURCE_DIR}/include")
+SET(LIBGIT2_LIBS "")
+SET(LIBGIT2_LIBDIRS "")
+
+# Installation paths
+#
+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})
+
+# Enable tracing
+IF (ENABLE_TRACE STREQUAL "ON")
+ SET(GIT_TRACE 1)
+ENDIF()
+ADD_FEATURE_INFO(tracing GIT_TRACE "tracing support")
+
+CHECK_SYMBOL_EXISTS(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L)
+IF (HAVE_REGCOMP_L)
+ SET(GIT_USE_REGCOMP_L 1)
+ENDIF ()
+
+CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
+IF (HAVE_FUTIMENS)
+ SET(GIT_USE_FUTIMENS 1)
+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 ()
+
+# Find required dependencies
+
+IF(WIN32)
+ LIST(APPEND LIBGIT2_LIBS ws2_32)
+ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+ LIST(APPEND LIBGIT2_LIBS socket nsl)
+ LIST(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
+ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Haiku")
+ LIST(APPEND LIBGIT2_LIBS network)
+ LIST(APPEND LIBGIT2_PC_LIBS "-lnetwork")
+ENDIF()
+
+CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT)
+IF(NEED_LIBRT)
+ LIST(APPEND LIBGIT2_LIBS rt)
+ LIST(APPEND LIBGIT2_PC_LIBS "-lrt")
+ENDIF()
+
+IF(THREADSAFE)
+ LIST(APPEND LIBGIT2_LIBS ${CMAKE_THREAD_LIBS_INIT})
+ LIST(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
+ENDIF()
+ADD_FEATURE_INFO(threadsafe THREADSAFE "threadsafe support")
+
+
+IF (WIN32 AND EMBED_SSH_PATH)
+ FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
+ LIST(APPEND LIBGIT2_INCLUDES "${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\"")
+ SET(GIT_SSH 1)
+ENDIF()
+
+IF (WIN32 AND WINHTTP)
+ SET(GIT_WINHTTP 1)
+
+ # Since MinGW does not come with headers or an import library for winhttp,
+ # we have to include a private header and generate our own import library
+ IF (MINGW)
+ ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/winhttp" "${libgit2_BINARY_DIR}/deps/winhttp")
+ LIST(APPEND LIBGIT2_LIBS winhttp)
+ LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/winhttp")
+ LIST(APPEND LIBGIT2_LIBDIRS ${LIBWINHTTP_PATH})
+ ELSE()
+ LIST(APPEND LIBGIT2_LIBS "winhttp")
+ LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp")
+ ENDIF ()
+
+ LIST(APPEND LIBGIT2_LIBS "rpcrt4" "crypt32" "ole32")
+ LIST(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32")
+ELSE ()
+ IF (CURL)
+ PKG_CHECK_MODULES(CURL libcurl)
+ ENDIF ()
+
+ IF (CURL_FOUND)
+ SET(GIT_CURL 1)
+ LIST(APPEND LIBGIT2_INCLUDES ${CURL_INCLUDE_DIRS})
+ LIST(APPEND LIBGIT2_LIBDIRS ${CURL_LIBRARY_DIRS})
+ LIST(APPEND LIBGIT2_LIBS ${CURL_LIBRARIES})
+ LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS})
+ ENDIF()
+ ADD_FEATURE_INFO(cURL GIT_CURL "cURL for HTTP proxy support")
+ENDIF()
+
+IF (USE_HTTPS)
+ IF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ FIND_PACKAGE(Security)
+ FIND_PACKAGE(CoreFoundation)
+ ENDIF()
+
+ # Auto-select TLS backend
+ IF (USE_HTTPS STREQUAL ON)
+ IF (SECURITY_FOUND)
+ IF (SECURITY_HAS_SSLCREATECONTEXT)
+ SET(HTTPS_BACKEND "SecureTransport")
+ ELSE()
+ MESSAGE("-- Security framework is too old, falling back to OpenSSL")
+ SET(HTTPS_BACKEND "OpenSSL")
+ ENDIF()
+ ELSEIF (WINHTTP)
+ SET(HTTPS_BACKEND "WinHTTP")
+ ELSE()
+ SET(HTTPS_BACKEND "OpenSSL")
+ ENDIF()
+ ELSE()
+ # Backend was explicitly set
+ SET(HTTPS_BACKEND ${USE_HTTPS})
+ ENDIF()
+
+ # Check that we can find what's required for the selected backend
+ IF (HTTPS_BACKEND STREQUAL "SecureTransport")
+ IF (NOT COREFOUNDATION_FOUND)
+ MESSAGE(FATAL_ERROR "Cannot use SecureTransport backend, CoreFoundation.framework not found")
+ ENDIF()
+ IF (NOT SECURITY_FOUND)
+ MESSAGE(FATAL_ERROR "Cannot use SecureTransport backend, Security.framework not found")
+ ENDIF()
+ IF (NOT SECURITY_HAS_SSLCREATECONTEXT)
+ MESSAGE(FATAL_ERROR "Cannot use SecureTransport backend, SSLCreateContext not supported")
+ ENDIF()
+
+ SET(GIT_SECURE_TRANSPORT 1)
+ LIST(APPEND LIBGIT2_INCLUDES ${SECURITY_INCLUDE_DIR})
+ LIST(APPEND LIBGIT2_LIBS ${COREFOUNDATION_LIBRARIES} ${SECURITY_LIBRARIES})
+ LIST(APPEND LIBGIT2_PC_LIBS ${COREFOUNDATION_LDFLAGS} ${SECURITY_LDFLAGS})
+ ELSEIF (HTTPS_BACKEND STREQUAL "OpenSSL")
+ FIND_PACKAGE(OpenSSL)
+
+ IF (NOT OPENSSL_FOUND)
+ MESSAGE(FATAL_ERROR "Asked for OpenSSL TLS backend, but it wasn't found")
+ ENDIF()
+
+ SET(GIT_OPENSSL 1)
+ LIST(APPEND LIBGIT2_INCLUDES ${OPENSSL_INCLUDE_DIR})
+ LIST(APPEND LIBGIT2_LIBS ${OPENSSL_LIBRARIES})
+ LIST(APPEND LIBGIT2_PC_LIBS ${OPENSSL_LDFLAGS})
+ LIST(APPEND LIBGIT2_PC_REQUIRES "openssl")
+ ELSEIF (HTTPS_BACKEND STREQUAL "WinHTTP")
+ # WinHTTP setup was handled in the WinHTTP-specific block above
+ ELSE()
+ MESSAGE(FATAL_ERROR "Asked for backend ${HTTPS_BACKEND} but it wasn't found")
+ ENDIF()
+
+ ADD_FEATURE_INFO(HTTPS ON "using ${HTTPS_BACKEND}")
+ SET(GIT_HTTPS 1)
+ELSE()
+ ADD_FEATURE_INFO(HTTPS OFF "no support")
+ENDIF()
+
+# Specify sha1 implementation
+IF(SHA1_BACKEND STREQUAL "OpenSSL")
+ IF(NOT OPENSSL_FOUND)
+ FIND_PACKAGE(OpenSSL)
+ IF(NOT OPENSSL_FOUND)
+ MESSAGE(FATAL_ERROR "Requested OpenSSL SHA1 backend, but OpenSSL could not be found")
+ ENDIF()
+ ENDIF()
+
+ ADD_FEATURE_INFO(SHA ON "using OpenSSL")
+ SET(GIT_SHA1_OPENSSL 1)
+ IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ LIST(APPEND LIBGIT2_PC_LIBS "-lssl")
+ ELSE()
+ LIST(APPEND LIBGIT2_PC_REQUIRES "openssl")
+ ENDIF()
+ELSEIF(SHA1_BACKEND STREQUAL "CollisionDetection")
+ ADD_FEATURE_INFO(SHA ON "using CollisionDetection")
+ SET(GIT_SHA1_COLLISIONDETECT 1)
+ ADD_DEFINITIONS(-DSHA1DC_NO_STANDARD_INCLUDES=1)
+ ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\")
+ ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\")
+ FILE(GLOB SRC_SHA1 hash/hash_collisiondetect.c hash/sha1dc/*)
+ELSEIF(SHA1_BACKEND STREQUAL "Generic")
+ ADD_FEATURE_INFO(SHA ON "using Generic")
+ FILE(GLOB SRC_SHA1 hash/hash_generic.c)
+ELSEIF(SHA1_BACKEND STREQUAL "Win32")
+ ADD_FEATURE_INFO(SHA ON "using Win32")
+ SET(GIT_SHA1_WIN32 1)
+ FILE(GLOB SRC_SHA1 hash/hash_win32.c)
+ELSEIF(SHA1_BACKEND STREQUAL "CommonCrypto")
+ ADD_FEATURE_INFO(SHA ON "using CommonCrypto")
+ SET(GIT_SHA1_COMMON_CRYPTO 1)
+ELSE()
+ MESSAGE(FATAL_ERROR "Asked for unknown SHA1 backend ${SHA1_BACKEND}")
+ENDIF()
+
+# Include POSIX regex when it is required
+IF(WIN32 OR AMIGA OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+ ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/regex" "${libgit2_BINARY_DIR}/deps/regex")
+ LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/regex")
+ LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:regex>)
+ENDIF()
+
+# Optional external dependency: http-parser
+FIND_PACKAGE(HTTP_Parser)
+IF (USE_EXT_HTTP_PARSER AND HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
+ LIST(APPEND LIBGIT2_INCLUDES ${HTTP_PARSER_INCLUDE_DIRS})
+ LIST(APPEND LIBGIT2_LIBS ${HTTP_PARSER_LIBRARIES})
+ LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
+ ADD_FEATURE_INFO(http-parser ON "http-parser support")
+ELSE()
+ MESSAGE(STATUS "http-parser version 2 was not found or disabled; using bundled 3rd-party sources.")
+ ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/http-parser" "${libgit2_BINARY_DIR}/deps/http-parser")
+ LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/http-parser")
+ LIST(APPEND LIBGIT2_OBJECTS "$<TARGET_OBJECTS:http-parser>")
+ ADD_FEATURE_INFO(http-parser ON "http-parser support (bundled)")
+ENDIF()
+
+# Optional external dependency: zlib
+IF(NOT USE_BUNDLED_ZLIB)
+ FIND_PACKAGE(ZLIB)
+ IF(ZLIB_FOUND)
+ LIST(APPEND LIBGIT2_INCLUDES ${ZLIB_INCLUDE_DIRS})
+ LIST(APPEND LIBGIT2_LIBS ${ZLIB_LIBRARIES})
+ IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ LIST(APPEND LIBGIT2_LIBS "z")
+ LIST(APPEND LIBGIT2_PC_LIBS "-lz")
+ ELSE()
+ LIST(APPEND LIBGIT2_PC_REQUIRES "zlib")
+ ENDIF()
+ ADD_FEATURE_INFO(zlib ON "using system zlib")
+ ELSE()
+ MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." )
+ ENDIF()
+ENDIF()
+IF(USE_BUNDLED_ZLIB OR NOT ZLIB_FOUND)
+ ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/zlib" "${libgit2_BINARY_DIR}/deps/zlib")
+ LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/zlib")
+ LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:zlib>)
+ ADD_FEATURE_INFO(zlib ON "using bundled zlib")
+ENDIF()
+
+# Optional external dependency: libssh2
+IF (USE_SSH)
+ PKG_CHECK_MODULES(LIBSSH2 libssh2)
+ENDIF()
+IF (LIBSSH2_FOUND)
+ SET(GIT_SSH 1)
+ LIST(APPEND LIBGIT2_INCLUDES ${LIBSSH2_INCLUDE_DIRS})
+ LIST(APPEND LIBGIT2_LIBS ${LIBSSH2_LIBRARIES})
+ LIST(APPEND LIBGIT2_LIBDIRS ${LIBSSH2_LIBRARY_DIRS})
+ LIST(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
+ #SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}")
+
+ CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
+ IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS)
+ SET(GIT_SSH_MEMORY_CREDENTIALS 1)
+ ENDIF()
+ELSE()
+ MESSAGE(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
+ENDIF()
+ADD_FEATURE_INFO(SSH GIT_SSH "SSH transport support")
+
+# Optional external dependency: libgssapi
+IF (USE_GSSAPI)
+ FIND_PACKAGE(GSSAPI)
+ENDIF()
+IF (GSSAPI_FOUND)
+ SET(GIT_GSSAPI 1)
+ LIST(APPEND LIBGIT2_LIBS ${GSSAPI_LIBRARIES})
+ENDIF()
+ADD_FEATURE_INFO(SPNEGO GIT_GSSAPI "SPNEGO authentication support")
+
+# Optional external dependency: iconv
+IF (USE_ICONV)
+ FIND_PACKAGE(Iconv)
+ENDIF()
+IF (ICONV_FOUND)
+ SET(GIT_USE_ICONV 1)
+ LIST(APPEND LIBGIT2_INCLUDES ${ICONV_INCLUDE_DIR})
+ LIST(APPEND LIBGIT2_LIBS ${ICONV_LIBRARIES})
+ LIST(APPEND LIBGIT2_PC_LIBS ${ICONV_LIBRARIES})
+ENDIF()
+ADD_FEATURE_INFO(iconv GIT_USE_ICONV "iconv encoding conversion support")
+
+
+IF (THREADSAFE)
+ IF (NOT WIN32)
+ FIND_PACKAGE(Threads REQUIRED)
+ ENDIF()
+
+ SET(GIT_THREADS 1)
+ENDIF()
+
+IF (USE_NSEC)
+ SET(GIT_USE_NSEC 1)
+ENDIF()
+
+IF (HAVE_STRUCT_STAT_ST_MTIM)
+ SET(GIT_USE_STAT_MTIM 1)
+ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC)
+ SET(GIT_USE_STAT_MTIMESPEC 1)
+ELSEIF (HAVE_STRUCT_STAT_ST_MTIME_NSEC)
+ SET(GIT_USE_STAT_MTIME_NSEC 1)
+ENDIF()
+
+ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
+
+# Collect sourcefiles
+FILE(GLOB SRC_H
+ "${libgit2_SOURCE_DIR}/include/git2.h"
+ "${libgit2_SOURCE_DIR}/include/git2/*.h"
+ "${libgit2_SOURCE_DIR}/include/git2/sys/*.h")
+
+# On Windows use specific platform sources
+IF (WIN32 AND NOT CYGWIN)
+ ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
+
+ IF(MSVC)
+ SET(WIN_RC "win32/git2.rc")
+ ENDIF()
+
+ FILE(GLOB SRC_OS win32/*.c win32/*.h)
+ELSEIF (AMIGA)
+ ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
+ELSE()
+ IF (VALGRIND)
+ ADD_DEFINITIONS(-DNO_MMAP)
+ ENDIF()
+ FILE(GLOB SRC_OS unix/*.c unix/*.h)
+ENDIF()
+FILE(GLOB SRC_GIT2 *.c *.h
+ streams/*.c streams/*.h
+ transports/*.c transports/*.h
+ xdiff/*.c xdiff/*.h)
+
+# Determine architecture of the machine
+IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
+ SET(GIT_ARCH_64 1)
+ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4)
+ SET(GIT_ARCH_32 1)
+ELSEIF (CMAKE_SIZEOF_VOID_P)
+ MESSAGE(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
+ELSE()
+ MESSAGE(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
+ENDIF()
+
+CONFIGURE_FILE(features.h.in git2/sys/features.h)
+
+SET(LIBGIT2_SOURCES ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_SSH} ${SRC_SHA1})
+
+ADD_LIBRARY(git2internal OBJECT ${LIBGIT2_SOURCES})
+IDE_SPLIT_SOURCES(git2internal)
+LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:git2internal>)
+
+IF (${CMAKE_VERSION} VERSION_LESS 2.8.12)
+ INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES})
+ELSE()
+ TARGET_INCLUDE_DIRECTORIES(git2internal
+ PRIVATE ${LIBGIT2_INCLUDES}
+ PUBLIC ${libgit2_SOURCE_DIR}/include)
+ENDIF()
+
+SET(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
+SET(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
+SET(LIBGIT2_LIBS ${LIBGIT2_LIBS} PARENT_SCOPE)
+SET(LIBGIT2_LIBDIRS ${LIBGIT2_LIBDIRS} PARENT_SCOPE)
+
+IF(XCODE_VERSION)
+ # This is required for Xcode to actually link the libgit2 library
+ # when using only object libraries.
+ FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.c "")
+ LIST(APPEND LIBGIT2_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/dummy.c)
+ENDIF()
+
+# Compile and link libgit2
+LINK_DIRECTORIES(${LIBGIT2_LIBDIRS})
+ADD_LIBRARY(git2 ${WIN_RC} ${LIBGIT2_OBJECTS})
+TARGET_LINK_LIBRARIES(git2 ${LIBGIT2_LIBS})
+
+SET_TARGET_PROPERTIES(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+SET_TARGET_PROPERTIES(git2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+SET_TARGET_PROPERTIES(git2 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+
+# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
+# Win64+MSVC+static libs = linker error
+IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
+ SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
+ENDIF()
+
+IDE_SPLIT_SOURCES(git2)
+
+IF (SONAME)
+ SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
+ SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_SOVERSION})
+ IF (LIBGIT2_FILENAME)
+ ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
+ SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
+ ELSEIF (DEFINED LIBGIT2_PREFIX)
+ SET_TARGET_PROPERTIES(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
+ ENDIF()
+ENDIF()
+
+LIST(REMOVE_DUPLICATES LIBGIT2_PC_REQUIRES)
+STRING(REPLACE ";" " " LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES}")
+STRING(REPLACE ";" " " LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS}")
+CONFIGURE_FILE(${libgit2_SOURCE_DIR}/libgit2.pc.in ${libgit2_BINARY_DIR}/libgit2.pc @ONLY)
+
+IF (MSVC_IDE)
+ # Precompiled headers
+ SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+ SET_SOURCE_FILES_PROPERTIES(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
+ENDIF ()
+
+# Install
+INSTALL(TARGETS git2
+ RUNTIME DESTINATION ${BIN_INSTALL_DIR}
+ LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
+)
+INSTALL(FILES ${libgit2_BINARY_DIR}/libgit2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
+INSTALL(DIRECTORY ${libgit2_SOURCE_DIR}/include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} )
+INSTALL(FILES ${libgit2_SOURCE_DIR}/include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "annotated_commit.h"
+
#include "refs.h"
#include "cache.h"
#ifndef INCLUDE_annotated_commit_h__
#define INCLUDE_annotated_commit_h__
+#include "common.h"
+
#include "oidarray.h"
#include "git2/oid.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "apply.h"
+
#include <assert.h>
#include "git2/patch.h"
#include "array.h"
#include "patch.h"
#include "fileops.h"
-#include "apply.h"
#include "delta.h"
#include "zstream.h"
#ifndef INCLUDE_apply_h__
#define INCLUDE_apply_h__
+#include "common.h"
+
#include "git2/patch.h"
#include "buffer.h"
-#include "common.h"
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "attr.h"
+
#include "repository.h"
#include "sysdir.h"
#include "config.h"
git_attr_file *file;
git_attr_name attr;
git_attr_rule *rule;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
assert(value && repo && name);
*value = NULL;
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0)
git_attr_rule *rule;
attr_get_many_info *info = NULL;
size_t num_found = 0;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
if (!num_attr)
return 0;
assert(values && repo && names);
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0)
git_attr_rule *rule;
git_attr_assignment *assign;
git_strmap *seen = NULL;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
assert(repo && callback);
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 ||
#ifndef INCLUDE_attr_h__
#define INCLUDE_attr_h__
+#include "common.h"
+
#include "attr_file.h"
#include "attrcache.h"
-#include "common.h"
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "attr_file.h"
+
#include "repository.h"
#include "filebuf.h"
-#include "attr_file.h"
#include "attrcache.h"
#include "git2/blob.h"
#include "git2/tree.h"
#ifndef INCLUDE_attr_file_h__
#define INCLUDE_attr_file_h__
+#include "common.h"
+
#include "git2/oid.h"
#include "git2/attr.h"
#include "vector.h"
-#include "common.h"
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "attrcache.h"
+
#include "repository.h"
#include "attr_file.h"
#include "config.h"
#ifndef INCLUDE_attrcache_h__
#define INCLUDE_attrcache_h__
+#include "common.h"
+
#include "attr_file.h"
#include "strmap.h"
*/
#include "blame.h"
+
#include "git2/commit.h"
#include "git2/revparse.h"
#include "git2/revwalk.h"
#ifndef INCLUDE_blame_h__
#define INCLUDE_blame_h__
-#include "git2/blame.h"
#include "common.h"
+
+#include "git2/blame.h"
#include "vector.h"
#include "diff.h"
#include "array.h"
*/
#include "blame_git.h"
+
#include "commit.h"
#include "blob.h"
#include "xdiff/xinclude.h"
#ifndef INCLUDE_blame_git__
#define INCLUDE_blame_git__
+#include "common.h"
+
#include "blame.h"
int git_blame__get_origin(
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "blob.h"
+
#include "git2/common.h"
#include "git2/object.h"
#include "git2/repository.h"
#include "git2/odb_backend.h"
-#include "common.h"
#include "filebuf.h"
-#include "blob.h"
#include "filter.h"
#include "buf_text.h"
#ifndef INCLUDE_blob_h__
#define INCLUDE_blob_h__
+#include "common.h"
+
#include "git2/blob.h"
#include "repository.h"
#include "odb.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "branch.h"
+
#include "commit.h"
#include "tag.h"
#include "config.h"
assert(branch_name && commit && ref_out);
assert(git_object_owner((const git_object *)commit) == repository);
+ if (!git__strcmp(branch_name, "HEAD")) {
+ giterr_set(GITERR_REFERENCE, "'HEAD' is not a valid branch name");
+ error = -1;
+ goto cleanup;
+ }
+
if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
error = git_branch_is_head(branch);
git_reference_free(branch);
#ifndef INCLUDE_branch_h__
#define INCLUDE_branch_h__
+#include "common.h"
+
#include "buffer.h"
int git_branch_upstream__name(
git_bom_t bom;
int printable = 0, nonprintable = 0;
- scan += git_buf_text_detect_bom(&bom, buf, 0);
+ scan += git_buf_text_detect_bom(&bom, buf);
if (bom > GIT_BOM_UTF8)
return 1;
return (memchr(buf->ptr, '\0', buf->size) != NULL);
}
-int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf, size_t offset)
+int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf)
{
const char *ptr;
size_t len;
*bom = GIT_BOM_NONE;
- /* need at least 2 bytes after offset to look for any BOM */
- if (buf->size < offset + 2)
+ /* need at least 2 bytes to look for any BOM */
+ if (buf->size < 2)
return 0;
- ptr = buf->ptr + offset;
- len = buf->size - offset;
+ ptr = buf->ptr;
+ len = buf->size;
switch (*ptr++) {
case 0:
memset(stats, 0, sizeof(*stats));
/* BOM detection */
- skip = git_buf_text_detect_bom(&stats->bom, buf, 0);
+ skip = git_buf_text_detect_bom(&stats->bom, buf);
if (skip_bom)
scan += skip;
#ifndef INCLUDE_buf_text_h__
#define INCLUDE_buf_text_h__
+#include "common.h"
+
#include "buffer.h"
typedef enum {
*
* @param bom Set to the type of BOM detected or GIT_BOM_NONE
* @param buf Buffer in which to check the first bytes for a BOM
- * @param offset Offset into buffer to look for BOM
* @return Number of bytes of BOM data (or 0 if no BOM found)
*/
-extern int git_buf_text_detect_bom(
- git_bom_t *bom, const git_buf *buf, size_t offset);
+extern int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf);
/**
* Gather stats for a piece of text
size_t new_size;
assert(data);
-
+
GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
ENSURE_SIZE(buf, new_size);
return -1;
}
+#define HEX_DECODE(c) ((c | 32) % 39 - 9)
+
+int git_buf_decode_percent(
+ git_buf *buf,
+ const char *str,
+ size_t str_len)
+{
+ size_t str_pos, new_size;
+
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, str_len);
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
+
+ for (str_pos = 0; str_pos < str_len; buf->size++, str_pos++) {
+ if (str[str_pos] == '%' &&
+ str_len > str_pos + 2 &&
+ isxdigit(str[str_pos + 1]) &&
+ isxdigit(str[str_pos + 2])) {
+ buf->ptr[buf->size] = (HEX_DECODE(str[str_pos + 1]) << 4) +
+ HEX_DECODE(str[str_pos + 2]);
+ str_pos += 2;
+ } else {
+ buf->ptr[buf->size] = str[str_pos];
+ }
+ }
+
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
size_t expected_size, new_size;
/* Decode the given "base85" and write the result to the buffer */
int git_buf_decode_base85(git_buf *buf, const char *base64, size_t len, size_t output_len);
+/* Decode the given percent-encoded string and write the result to the buffer */
+int git_buf_decode_percent(git_buf *buf, const char *str, size_t len);
+
/*
* Insert, remove or replace a portion of the buffer.
*
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "cache.h"
+
#include "repository.h"
#include "commit.h"
#include "thread-utils.h"
#include "util.h"
-#include "cache.h"
#include "odb.h"
#include "object.h"
#include "git2/oid.h"
#ifndef INCLUDE_cache_h__
#define INCLUDE_cache_h__
+#include "common.h"
+
#include "git2/common.h"
#include "git2/oid.h"
#include "git2/odb.h"
* 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_compat_h__
-#define INCLUDE_compat_h__
+#ifndef INCLUDE_cc_compat_h__
+#define INCLUDE_cc_compat_h__
#include <stdarg.h>
# endif
#endif
-#endif /* INCLUDE_compat_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include <assert.h>
-
#include "checkout.h"
+#include <assert.h>
+
#include "git2/repository.h"
#include "git2/refs.h"
#include "git2/tree.h"
git_buf tmp;
unsigned int strategy;
int can_symlink;
+ int respect_filemode;
bool reload_submodules;
size_t total_steps;
size_t completed_steps;
git_oid__cmp(&newitem->id, workdir_id) == 0);
}
+GIT_INLINE(bool) is_filemode_changed(git_filemode_t a, git_filemode_t b, int respect_filemode)
+{
+ /* If core.filemode = false, ignore links in the repository and executable bit changes */
+ if (!respect_filemode) {
+ if (a == S_IFLNK)
+ a = GIT_FILEMODE_BLOB;
+ if (b == S_IFLNK)
+ b = GIT_FILEMODE_BLOB;
+
+ a &= ~0111;
+ b &= ~0111;
+ }
+
+ return (a != b);
+}
+
static bool checkout_is_workdir_modified(
checkout_data *data,
const git_diff_file *baseitem,
return rval;
}
- /* Look at the cache to decide if the workdir is modified. If not,
- * we can simply compare the oid in the cache to the baseitem instead
- * of hashing the file. If so, we allow the checkout to proceed if the
- * oid is identical (ie, the staged item is what we're trying to check
- * out.)
+ /*
+ * Look at the cache to decide if the workdir is modified: if the
+ * cache contents match the workdir contents, then we do not need
+ * to examine the working directory directly, instead we can
+ * examine the cache to see if _it_ has been modified. This allows
+ * us to avoid touching the disk.
*/
- if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
- 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);
+ ie = git_index_get_bypath(data->index, wditem->path, 0);
+
+ if (ie != NULL &&
+ git_index_time_eq(&wditem->mtime, &ie->mtime) &&
+ wditem->file_size == ie->file_size &&
+ !is_filemode_changed(wditem->mode, ie->mode, data->respect_filemode)) {
+
+ /* The workdir is modified iff the index entry is modified */
+ return !is_workdir_base_or_new(&ie->id, baseitem, newitem) ||
+ is_filemode_changed(baseitem->mode, ie->mode, data->respect_filemode);
}
/* depending on where base is coming from, we may or may not know
if (S_ISDIR(wditem->mode))
return false;
+ if (is_filemode_changed(baseitem->mode, wditem->mode, data->respect_filemode))
+ return true;
+
if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
return false;
(error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0)
return error;
- return checkout_write_content(data,
- &side->id, fullpath->ptr, hint_path, side->mode, &st);
+ if (!S_ISGITLINK(side->mode))
+ return checkout_write_content(data,
+ &side->id, fullpath->ptr, hint_path, side->mode, &st);
+
+ return 0;
}
static int checkout_write_entries(
&data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0)
goto cleanup;
+ if ((error = git_repository__cvar(
+ &data->respect_filemode, repo, GIT_CVAR_FILEMODE)) < 0)
+ goto cleanup;
+
if (!data->opts.baseline && !data->opts.baseline_index) {
data->opts_free_baseline = true;
error = 0;
#ifndef INCLUDE_checkout_h__
#define INCLUDE_checkout_h__
+#include "common.h"
+
#include "git2/checkout.h"
#include "iterator.h"
*/
#include "common.h"
+
#include "repository.h"
#include "filebuf.h"
#include "merge.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "clone.h"
+
#include <assert.h>
#include "git2/clone.h"
#include "git2/commit.h"
#include "git2/tree.h"
-#include "common.h"
#include "remote.h"
#include "fileops.h"
#include "refs.h"
#ifndef INCLUDE_clone_h__
#define INCLUDE_clone_h__
+#include "common.h"
+
+#include "git2/clone.h"
+
extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "commit.h"
+
#include "git2/common.h"
#include "git2/object.h"
#include "git2/repository.h"
#include "git2/signature.h"
#include "git2/sys/commit.h"
-#include "common.h"
#include "odb.h"
#include "commit.h"
#include "signature.h"
#ifndef INCLUDE_commit_h__
#define INCLUDE_commit_h__
+#include "common.h"
+
#include "git2/commit.h"
#include "tree.h"
#include "repository.h"
*/
#include "commit_list.h"
-#include "common.h"
+
#include "revwalk.h"
#include "pool.h"
#include "odb.h"
#ifndef INCLUDE_commit_list_h__
#define INCLUDE_commit_list_h__
+#include "common.h"
+
#include "git2/oid.h"
#define PARENT1 (1 << 0)
#ifndef INCLUDE_common_h__
#define INCLUDE_common_h__
+#ifndef LIBGIT2_NO_FEATURES_H
+# include "git2/sys/features.h"
+#endif
+
#include "git2/common.h"
#include "cc-compat.h"
# ifdef GIT_THREADS
# include "win32/thread.h"
# endif
-# if defined(GIT_MSVC_CRTDBG)
-# include "win32/w32_stack.h"
-# include "win32/w32_crtdbg_stacktrace.h"
-# endif
#else
GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; }
+#define GITERR_CHECK_ALLOC_ADD5(out, one, two, three, four, five) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), four) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), five)) { 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; }
#include "util.h"
-#endif /* INCLUDE_common_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "sysdir.h"
#include "config.h"
+
+#include "sysdir.h"
#include "git2/config.h"
#include "git2/sys/config.h"
#include "vector.h"
git_config *cfg,
const char *path,
git_config_level_t level,
+ const git_repository *repo,
int force)
{
git_config_backend *file = NULL;
if (git_config_file__ondisk(&file, path) < 0)
return -1;
- if ((res = git_config_add_backend(cfg, file, level, force)) < 0) {
+ if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) {
/*
* free manually; the file is not owned by the config
* instance yet and will not be freed on cleanup
if (git_config_new(&config) < 0)
return -1;
- if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0)
+ if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, NULL, 0)) < 0)
git_config_free(config);
else
*out = config;
if ((error = internal->file->snapshot(&b, internal->file)) < 0)
break;
- if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) {
+ if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) {
b->free(b);
break;
}
git_config *cfg,
git_config_backend *file,
git_config_level_t level,
+ const git_repository *repo,
int force)
{
file_internal *internal;
GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
- if ((result = file->open(file, level)) < 0)
+ if ((result = file->open(file, level, repo)) < 0)
return result;
internal = git__malloc(sizeof(file_internal));
if (!git_config_find_global(&buf) || !git_config__global_location(&buf)) {
error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_GLOBAL, 0);
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
}
if (!error && !git_config_find_xdg(&buf))
error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_XDG, 0);
+ GIT_CONFIG_LEVEL_XDG, NULL, 0);
if (!error && !git_config_find_system(&buf))
error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_SYSTEM, 0);
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0);
if (!error && !git_config_find_programdata(&buf))
error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_PROGRAMDATA, 0);
+ GIT_CONFIG_LEVEL_PROGRAMDATA, NULL, 0);
git_buf_free(&buf);
#ifndef INCLUDE_config_h__
#define INCLUDE_config_h__
+#include "common.h"
+
#include "git2.h"
#include "git2/config.h"
#include "vector.h"
*/
#include "common.h"
+
#include "fileops.h"
#include "repository.h"
#include "config.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "config_file.h"
+
#include "config.h"
#include "filebuf.h"
#include "sysdir.h"
#include "git2/types.h"
#include "strmap.h"
#include "array.h"
+#include "config_parse.h"
#include <ctype.h>
#include <sys/types.h>
(iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
(iter) = (tmp))
-struct reader {
- git_oid checksum;
- char *file_path;
- git_buf buffer;
- char *read_ptr;
- int line_number;
- int eof;
-};
-
typedef struct {
git_atomic refcount;
git_strmap *values;
/* mutex to coordinate accessing the values */
git_mutex values_mutex;
refcounted_strmap *values;
+ const git_repository *repo;
+ git_config_level_t level;
} diskfile_header;
typedef struct {
diskfile_header header;
- git_config_level_t level;
-
- git_array_t(struct reader) readers;
+ git_array_t(git_config_parser) readers;
bool locked;
git_filebuf locked_buf;
git_buf locked_content;
- char *file_path;
+ struct config_file file;
} diskfile_backend;
typedef struct {
diskfile_backend *snapshot_from;
} diskfile_readonly_backend;
-static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
-static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
+static int config_read(git_strmap *values, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth);
+static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
static char *escape_value(const char *ptr);
int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
static int config_snapshot(git_config_backend **out, git_config_backend *in);
-static void set_parse_error(struct reader *reader, int col, const char *error_str)
-{
- giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)",
- error_str, reader->file_path, reader->line_number, col);
-}
-
static int config_error_readonly(void)
{
giterr_set(GITERR_CONFIG, "this backend is read-only");
return error;
}
-static int config_open(git_config_backend *cfg, git_config_level_t level)
+static void config_file_clear(struct config_file *file)
+{
+ struct config_file *include;
+ uint32_t i;
+
+ if (file == NULL)
+ return;
+
+ git_array_foreach(file->includes, i, include) {
+ config_file_clear(include);
+ }
+ git_array_clear(file->includes);
+
+ git__free(file->path);
+}
+
+static int config_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
{
int res;
- struct reader *reader;
diskfile_backend *b = (diskfile_backend *)cfg;
- b->level = level;
+ b->header.level = level;
+ b->header.repo = repo;
if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
return res;
- git_array_init(b->readers);
- reader = git_array_alloc(b->readers);
- if (!reader) {
- refcounted_strmap_free(b->header.values);
- return -1;
- }
- memset(reader, 0, sizeof(struct reader));
-
- reader->file_path = git__strdup(b->file_path);
- GITERR_CHECK_ALLOC(reader->file_path);
-
- git_buf_init(&reader->buffer, 0);
- res = git_futils_readbuffer_updated(
- &reader->buffer, b->file_path, &reader->checksum, NULL);
-
- /* It's fine if the file doesn't exist */
- if (res == GIT_ENOTFOUND)
+ if (!git_path_exists(b->file.path))
return 0;
- if (res < 0 || (res = config_read(b->header.values->values, b, reader, level, 0)) < 0) {
+ if (res < 0 || (res = config_read(b->header.values->values, repo, &b->file, level, 0)) < 0) {
refcounted_strmap_free(b->header.values);
b->header.values = NULL;
}
- reader = git_array_get(b->readers, 0);
- git_buf_free(&reader->buffer);
-
return res;
}
-/* The meat of the refresh, as we want to use it in different places */
-static int config__refresh(git_config_backend *cfg)
+static int config_is_modified(int *modified, struct config_file *file)
{
- refcounted_strmap *values = NULL, *tmp;
- diskfile_backend *b = (diskfile_backend *)cfg;
- struct reader *reader = NULL;
+ git_config_file *include;
+ git_buf buf = GIT_BUF_INIT;
+ git_oid hash;
+ uint32_t i;
int error = 0;
- if ((error = refcounted_strmap_alloc(&values)) < 0)
- goto out;
+ *modified = 0;
- reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
- GITERR_CHECK_ALLOC(reader);
+ if ((error = git_futils_readbuffer(&buf, file->path)) < 0)
+ goto out;
- if ((error = config_read(values->values, b, reader, b->level, 0)) < 0)
+ if ((error = git_hash_buf(&hash, buf.ptr, buf.size)) < 0)
goto out;
- if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
- giterr_set(GITERR_OS, "failed to lock config backend");
+ if (!git_oid_equal(&hash, &file->checksum)) {
+ *modified = 1;
goto out;
}
- tmp = b->header.values;
- b->header.values = values;
- values = tmp;
-
- git_mutex_unlock(&b->header.values_mutex);
+ git_array_foreach(file->includes, i, include) {
+ if ((error = config_is_modified(modified, include)) < 0 || *modified)
+ goto out;
+ }
out:
- refcounted_strmap_free(values);
- if (reader)
- git_buf_free(&reader->buffer);
+ git_buf_free(&buf);
+
return error;
}
static int config_refresh(git_config_backend *cfg)
{
- int error = 0, updated = 0, any_updated = 0;
diskfile_backend *b = (diskfile_backend *)cfg;
- struct reader *reader = NULL;
+ refcounted_strmap *values = NULL, *tmp;
+ git_config_file *include;
+ int error, modified;
uint32_t i;
- for (i = 0; i < git_array_size(b->readers); i++) {
- reader = git_array_get(b->readers, i);
- error = git_futils_readbuffer_updated(
- &reader->buffer, reader->file_path,
- &reader->checksum, &updated);
+ if (b->header.parent.readonly)
+ return config_error_readonly();
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
+ error = config_is_modified(&modified, &b->file);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto out;
+
+ if (!modified)
+ return 0;
+
+ if ((error = refcounted_strmap_alloc(&values)) < 0)
+ goto out;
- if (updated)
- any_updated = 1;
+ /* Reparse the current configuration */
+ git_array_foreach(b->file.includes, i, include) {
+ config_file_clear(include);
+ }
+ git_array_clear(b->file.includes);
+
+ if ((error = config_read(values->values, b->header.repo, &b->file, b->header.level, 0)) < 0)
+ goto out;
+
+ if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
+ giterr_set(GITERR_OS, "failed to lock config backend");
+ goto out;
}
- if (!any_updated)
- return (error == GIT_ENOTFOUND) ? 0 : error;
+ tmp = b->header.values;
+ b->header.values = values;
+ values = tmp;
- return config__refresh(cfg);
+ git_mutex_unlock(&b->header.values_mutex);
+
+out:
+ refcounted_strmap_free(values);
+
+ return (error == GIT_ENOTFOUND) ? 0 : error;
}
static void backend_free(git_config_backend *_backend)
{
diskfile_backend *backend = (diskfile_backend *)_backend;
- uint32_t i;
if (backend == NULL)
return;
- for (i = 0; i < git_array_size(backend->readers); i++) {
- struct reader *r = git_array_get(backend->readers, i);
- git__free(r->file_path);
- }
- git_array_clear(backend->readers);
-
- git__free(backend->file_path);
+ config_file_clear(&backend->file);
refcounted_strmap_free(backend->header.values);
git_mutex_free(&backend->header.values_mutex);
git__free(backend);
diskfile_header *h;
git_config_file_iter *it;
git_config_backend *snapshot;
- diskfile_backend *b = (diskfile_backend *) backend;
+ diskfile_header *bh = (diskfile_header *) backend;
int error;
if ((error = config_snapshot(&snapshot, backend)) < 0)
return error;
- if ((error = snapshot->open(snapshot, b->level)) < 0)
+ if ((error = snapshot->open(snapshot, bh->level, bh->repo)) < 0)
return error;
it = git__calloc(1, sizeof(git_config_file_iter));
goto out;
}
+ if (existing->included) {
+ giterr_set(GITERR_CONFIG, "modifying included variable is not supported");
+ ret = -1;
+ goto out;
+ }
+
/* don't update if old and new values already match */
if ((!existing->entry->value && !value) ||
(existing->entry->value && value &&
GITERR_CHECK_ALLOC(esc_value);
}
- if ((ret = config_write(b, key, NULL, esc_value)) < 0)
+ if ((ret = config_write(b, name, key, NULL, esc_value)) < 0)
goto out;
ret = config_refresh(cfg);
}
/* If we do have it, set call config_write() and reload */
- if ((result = config_write(b, key, &preg, value)) < 0)
+ if ((result = config_write(b, name, key, &preg, value)) < 0)
goto out;
result = config_refresh(cfg);
var = git_strmap_value_at(values, pos);
refcounted_strmap_free(map);
+ if (var->included) {
+ giterr_set(GITERR_CONFIG, "cannot delete included variable");
+ return -1;
+ }
+
if (var->next != NULL) {
giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete");
return -1;
}
- if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
+ if ((result = config_write(b, name, var->entry->name, NULL, NULL)) < 0)
return result;
return config_refresh(cfg);
goto out;
}
- if ((result = config_write(b, key, &preg, NULL)) < 0)
+ if ((result = config_write(b, name, key, &preg, NULL)) < 0)
goto out;
result = config_refresh(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)
+ 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);
+ error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path);
if (error < 0 && error != GIT_ENOTFOUND) {
git_filebuf_cleanup(&cfg->locked_buf);
return error;
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
git_mutex_init(&backend->header.values_mutex);
- backend->file_path = git__strdup(path);
- GITERR_CHECK_ALLOC(backend->file_path);
+ backend->file.path = git__strdup(path);
+ GITERR_CHECK_ALLOC(backend->file.path);
+ git_array_init(backend->file.includes);
backend->header.parent.open = config_open;
backend->header.parent.get = config_get;
git__free(backend);
}
-static int config_readonly_open(git_config_backend *cfg, git_config_level_t level)
+static int config_readonly_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
{
diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
diskfile_backend *src = b->snapshot_from;
if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
return error;
- /* We're just copying data, don't care about the level */
+ /* We're just copying data, don't care about the level or repo*/
GIT_UNUSED(level);
+ GIT_UNUSED(repo);
if ((src_map = refcounted_strmap_take(src_header)) == NULL)
return -1;
return 0;
}
-static int reader_getchar_raw(struct reader *reader)
-{
- int c;
-
- c = *reader->read_ptr++;
-
- /*
- Win 32 line breaks: if we find a \r\n sequence,
- return only the \n as a newline
- */
- if (c == '\r' && *reader->read_ptr == '\n') {
- reader->read_ptr++;
- c = '\n';
- }
-
- if (c == '\n')
- reader->line_number++;
-
- if (c == 0) {
- reader->eof = 1;
- c = '\0';
- }
-
- return c;
-}
-
-#define SKIP_WHITESPACE (1 << 1)
-#define SKIP_COMMENTS (1 << 2)
-
-static int reader_getchar(struct reader *reader, int flags)
-{
- const int skip_whitespace = (flags & SKIP_WHITESPACE);
- const int skip_comments = (flags & SKIP_COMMENTS);
- int c;
-
- assert(reader->read_ptr);
-
- do {
- c = reader_getchar_raw(reader);
- } while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
-
- if (skip_comments && (c == '#' || c == ';')) {
- do {
- c = reader_getchar_raw(reader);
- } while (c != '\n' && c != '\0');
- }
-
- return c;
-}
-
-/*
- * Read the next char, but don't move the reading pointer.
- */
-static int reader_peek(struct reader *reader, int flags)
-{
- void *old_read_ptr;
- int old_lineno, old_eof;
- int ret;
-
- assert(reader->read_ptr);
-
- old_read_ptr = reader->read_ptr;
- old_lineno = reader->line_number;
- old_eof = reader->eof;
-
- ret = reader_getchar(reader, flags);
-
- reader->read_ptr = old_read_ptr;
- reader->line_number = old_lineno;
- reader->eof = old_eof;
-
- return ret;
-}
-
-/*
- * Read and consume a line, returning it in newly-allocated memory.
- */
-static char *reader_readline(struct reader *reader, bool skip_whitespace)
-{
- char *line = NULL;
- char *line_src, *line_end;
- size_t line_len, alloc_len;
-
- line_src = reader->read_ptr;
-
- if (skip_whitespace) {
- /* Skip empty empty lines */
- while (git__isspace(*line_src))
- ++line_src;
- }
-
- line_end = strchr(line_src, '\n');
-
- /* no newline at EOF */
- if (line_end == NULL)
- line_end = strchr(line_src, 0);
-
- line_len = line_end - line_src;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, line_len, 1) ||
- (line = git__malloc(alloc_len)) == NULL) {
- return NULL;
- }
-
- memcpy(line, line_src, line_len);
-
- do line[line_len] = '\0';
- while (line_len-- > 0 && git__isspace(line[line_len]));
-
- if (*line_end == '\n')
- line_end++;
-
- if (*line_end == '\0')
- reader->eof = 1;
-
- reader->line_number++;
- reader->read_ptr = line_end;
-
- return line;
-}
-
-/*
- * Consume a line, without storing it anywhere
- */
-static void reader_consume_line(struct reader *reader)
-{
- char *line_start, *line_end;
-
- line_start = reader->read_ptr;
- line_end = strchr(line_start, '\n');
- /* No newline at EOF */
- if(line_end == NULL){
- line_end = strchr(line_start, '\0');
- }
-
- if (*line_end == '\n')
- line_end++;
-
- if (*line_end == '\0')
- reader->eof = 1;
-
- reader->line_number++;
- reader->read_ptr = line_end;
-}
-
-GIT_INLINE(int) config_keychar(int c)
-{
- return isalnum(c) || c == '-';
-}
-
-static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
-{
- int c, rpos;
- char *first_quote, *last_quote;
- git_buf buf = GIT_BUF_INIT;
- size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
-
- /*
- * base_name is what came before the space. We should be at the
- * first quotation mark, except for now, line isn't being kept in
- * sync so we only really use it to calculate the length.
- */
-
- first_quote = strchr(line, '"');
- if (first_quote == NULL) {
- set_parse_error(reader, 0, "Missing quotation marks in section header");
- goto end_error;
- }
-
- last_quote = strrchr(line, '"');
- quoted_len = last_quote - first_quote;
-
- if (quoted_len == 0) {
- set_parse_error(reader, 0, "Missing closing quotation mark in section header");
- goto end_error;
- }
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
-
- if (git_buf_grow(&buf, alloc_len) < 0 ||
- git_buf_printf(&buf, "%s.", base_name) < 0)
- goto end_error;
-
- rpos = 0;
-
- line = first_quote;
- c = line[++rpos];
-
- /*
- * At the end of each iteration, whatever is stored in c will be
- * added to the string. In case of error, jump to out
- */
- do {
-
- switch (c) {
- case 0:
- set_parse_error(reader, 0, "Unexpected end-of-line in section header");
- goto end_error;
-
- case '"':
- goto end_parse;
-
- case '\\':
- c = line[++rpos];
-
- if (c == 0) {
- set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
- goto end_error;
- }
-
- default:
- break;
- }
-
- git_buf_putc(&buf, (char)c);
- c = line[++rpos];
- } while (line + rpos < last_quote);
-
-end_parse:
- if (git_buf_oom(&buf))
- goto end_error;
-
- if (line[rpos] != '"' || line[rpos + 1] != ']') {
- set_parse_error(reader, rpos, "Unexpected text after closing quotes");
- git_buf_free(&buf);
- return -1;
- }
-
- *section_name = git_buf_detach(&buf);
- return 0;
-
-end_error:
- git_buf_free(&buf);
-
- return -1;
-}
-
-static int parse_section_header(struct reader *reader, char **section_out)
-{
- char *name, *name_end;
- int name_length, c, pos;
- int result;
- char *line;
- size_t line_len;
-
- line = reader_readline(reader, true);
- if (line == NULL)
- return -1;
-
- /* find the end of the variable's name */
- name_end = strrchr(line, ']');
- if (name_end == NULL) {
- git__free(line);
- set_parse_error(reader, 0, "Missing ']' in section header");
- return -1;
- }
-
- GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
- name = git__malloc(line_len);
- GITERR_CHECK_ALLOC(name);
-
- name_length = 0;
- pos = 0;
-
- /* Make sure we were given a section header */
- c = line[pos++];
- assert(c == '[');
-
- c = line[pos++];
-
- do {
- if (git__isspace(c)){
- name[name_length] = '\0';
- result = parse_section_header_ext(reader, line, name, section_out);
- git__free(line);
- git__free(name);
- return result;
- }
-
- if (!config_keychar(c) && c != '.') {
- set_parse_error(reader, pos, "Unexpected character in header");
- goto fail_parse;
- }
-
- name[name_length++] = (char)git__tolower(c);
-
- } while ((c = line[pos++]) != ']');
-
- if (line[pos - 1] != ']') {
- set_parse_error(reader, pos, "Unexpected end of file");
- goto fail_parse;
- }
-
- git__free(line);
-
- name[name_length] = 0;
- *section_out = name;
-
- return 0;
-
-fail_parse:
- git__free(line);
- git__free(name);
- return -1;
-}
-
-static int skip_bom(struct reader *reader)
-{
- git_bom_t bom;
- int bom_offset = git_buf_text_detect_bom(&bom,
- &reader->buffer, reader->read_ptr - reader->buffer.ptr);
-
- if (bom == GIT_BOM_UTF8)
- reader->read_ptr += bom_offset;
-
- /* TODO: reference implementation is pretty stupid with BoM */
-
- return 0;
-}
-
-/*
- (* basic types *)
- digit = "0".."9"
- integer = digit { digit }
- alphabet = "a".."z" + "A" .. "Z"
-
- section_char = alphabet | "." | "-"
- extension_char = (* any character except newline *)
- any_char = (* any character *)
- variable_char = "alphabet" | "-"
-
-
- (* actual grammar *)
- config = { section }
-
- section = header { definition }
-
- header = "[" section [subsection | subsection_ext] "]"
-
- subsection = "." section
- subsection_ext = "\"" extension "\""
-
- section = section_char { section_char }
- extension = extension_char { extension_char }
-
- definition = variable_name ["=" variable_value] "\n"
-
- variable_name = variable_char { variable_char }
- variable_value = string | boolean | integer
-
- string = quoted_string | plain_string
- quoted_string = "\"" plain_string "\""
- plain_string = { any_char }
-
- boolean = boolean_true | boolean_false
- boolean_true = "yes" | "1" | "true" | "on"
- boolean_false = "no" | "0" | "false" | "off"
-*/
-
-static int strip_comments(char *line, int in_quotes)
-{
- int quote_count = in_quotes, backslash_count = 0;
- char *ptr;
-
- for (ptr = line; *ptr; ++ptr) {
- if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
- quote_count++;
-
- if ((ptr[0] == ';' || ptr[0] == '#') &&
- (quote_count % 2) == 0 &&
- (backslash_count % 2) == 0) {
- ptr[0] = '\0';
- break;
- }
-
- if (ptr[0] == '\\')
- backslash_count++;
- else
- backslash_count = 0;
- }
-
- /* skip any space at the end */
- while (ptr > line && git__isspace(ptr[-1])) {
- ptr--;
- }
- ptr[0] = '\0';
-
- return quote_count;
-}
-
static int included_path(git_buf *out, const char *dir, const char *path)
{
/* From the user's home */
return git_path_join_unrooted(out, path, dir, NULL);
}
-static const char *escapes = "ntb\"\\";
-static const char *escaped = "\n\t\b\"\\";
-
/* Escape the values to write them to the file */
static char *escape_value(const char *ptr)
{
return NULL;
while (*ptr != '\0') {
- if ((esc = strchr(escaped, *ptr)) != NULL) {
+ if ((esc = strchr(git_config_escaped, *ptr)) != NULL) {
git_buf_putc(&buf, '\\');
- git_buf_putc(&buf, escapes[esc - escaped]);
+ git_buf_putc(&buf, git_config_escapes[esc - git_config_escaped]);
} else {
git_buf_putc(&buf, *ptr);
}
return git_buf_detach(&buf);
}
-/* '\"' -> '"' etc */
-static int unescape_line(
- char **out, bool *is_multi, const char *ptr, int quote_count)
-{
- char *str, *fixed, *esc;
- size_t ptr_len = strlen(ptr), alloc_len;
-
- *is_multi = false;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
- (str = git__malloc(alloc_len)) == NULL) {
- return -1;
- }
-
- fixed = str;
-
- while (*ptr != '\0') {
- if (*ptr == '"') {
- quote_count++;
- } else if (*ptr != '\\') {
- *fixed++ = *ptr;
- } else {
- /* backslash, check the next char */
- ptr++;
- /* if we're at the end, it's a multiline, so keep the backslash */
- if (*ptr == '\0') {
- *is_multi = true;
- goto done;
- }
- if ((esc = strchr(escapes, *ptr)) != NULL) {
- *fixed++ = escaped[esc - escapes];
- } else {
- git__free(str);
- giterr_set(GITERR_CONFIG, "invalid escape at %s", ptr);
- return -1;
- }
- }
- ptr++;
- }
-
-done:
- *fixed = '\0';
- *out = str;
-
- return 0;
-}
+struct parse_data {
+ const git_repository *repo;
+ const char *file_path;
+ git_strmap *values;
+ git_config_level_t level;
+ int depth;
+};
-static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
+static int parse_include(git_config_parser *reader,
+ struct parse_data *parse_data, const char *file)
{
- char *line = NULL, *proc_line = NULL;
- int quote_count;
- bool multiline;
-
- /* Check that the next line exists */
- line = reader_readline(reader, false);
- if (line == NULL)
- return -1;
-
- /* We've reached the end of the file, there is no continuation.
- * (this is not an error).
- */
- if (line[0] == '\0') {
- git__free(line);
- return 0;
- }
+ struct config_file *include;
+ git_buf path = GIT_BUF_INIT;
+ char *dir;
+ int result;
- quote_count = strip_comments(line, !!in_quotes);
+ if ((result = git_path_dirname_r(&path, reader->file->path)) < 0)
+ return result;
- /* If it was just a comment, pretend it didn't exist */
- if (line[0] == '\0') {
- git__free(line);
- return parse_multiline_variable(reader, value, quote_count);
- /* TODO: unbounded recursion. This **could** be exploitable */
- }
+ dir = git_buf_detach(&path);
+ result = included_path(&path, dir, file);
+ git__free(dir);
- if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
- git__free(line);
- return -1;
- }
- /* add this line to the multiline var */
+ if (result < 0)
+ return result;
- git_buf_puts(value, proc_line);
- git__free(line);
- git__free(proc_line);
+ include = git_array_alloc(reader->file->includes);
+ memset(include, 0, sizeof(*include));
+ git_array_init(include->includes);
+ include->path = git_buf_detach(&path);
- /*
- * If we need to continue reading the next line, let's just
- * keep putting stuff in the buffer
- */
- if (multiline)
- return parse_multiline_variable(reader, value, quote_count);
+ result = config_read(parse_data->values, parse_data->repo,
+ include, parse_data->level, parse_data->depth+1);
- return 0;
-}
+ if (result == GIT_ENOTFOUND) {
+ giterr_clear();
+ result = 0;
+ }
-GIT_INLINE(bool) is_namechar(char c)
-{
- return isalnum(c) || c == '-';
+ return result;
}
-static int parse_name(
- char **name, const char **value, struct reader *reader, const char *line)
+static int do_match_gitdir(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *value,
+ bool case_insensitive)
{
- const char *name_end = line, *value_start;
-
- *name = NULL;
- *value = NULL;
+ git_buf path = GIT_BUF_INIT;
+ int error, fnmatch_flags;
- while (*name_end && is_namechar(*name_end))
- name_end++;
+ if (value[0] == '.' && git_path_is_dirsep(value[1])) {
+ git_path_dirname_r(&path, cfg_file);
+ git_buf_joinpath(&path, path.ptr, value + 2);
+ } else if (value[0] == '~' && git_path_is_dirsep(value[1]))
+ git_sysdir_expand_global_file(&path, value + 1);
+ else if (!git_path_is_absolute(value))
+ git_buf_joinpath(&path, "**", value);
+ else
+ git_buf_sets(&path, value);
- if (line == name_end) {
- set_parse_error(reader, 0, "Invalid configuration key");
- return -1;
+ if (git_buf_oom(&path)) {
+ error = -1;
+ goto out;
}
- value_start = name_end;
+ if (git_path_is_dirsep(value[strlen(value) - 1]))
+ git_buf_puts(&path, "**");
- while (*value_start && git__isspace(*value_start))
- value_start++;
+ fnmatch_flags = FNM_PATHNAME|FNM_LEADING_DIR;
+ if (case_insensitive)
+ fnmatch_flags |= FNM_IGNORECASE;
- if (*value_start == '=') {
- *value = value_start + 1;
- } else if (*value_start) {
- set_parse_error(reader, 0, "Invalid configuration key");
- return -1;
- }
+ if ((error = p_fnmatch(path.ptr, git_repository_path(repo), fnmatch_flags)) < 0)
+ goto out;
- if ((*name = git__strndup(line, name_end - line)) == NULL)
- return -1;
+ *matches = (error == 0);
- return 0;
+out:
+ git_buf_free(&path);
+ return error;
}
-static int parse_variable(struct reader *reader, char **var_name, char **var_value)
+static int conditional_match_gitdir(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *value)
{
- const char *value_start = NULL;
- char *line;
- int quote_count;
- bool multiline;
-
- line = reader_readline(reader, true);
- if (line == NULL)
- return -1;
-
- quote_count = strip_comments(line, 0);
-
- /* If there is no value, boolean true is assumed */
- *var_value = NULL;
-
- if (parse_name(var_name, &value_start, reader, line) < 0)
- goto on_error;
-
- /*
- * Now, let's try to parse the value
- */
- if (value_start != NULL) {
- while (git__isspace(value_start[0]))
- value_start++;
-
- if (unescape_line(var_value, &multiline, value_start, 0) < 0)
- goto on_error;
-
- if (multiline) {
- git_buf multi_value = GIT_BUF_INIT;
- git_buf_attach(&multi_value, *var_value, 0);
-
- if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
- git_buf_oom(&multi_value)) {
- git_buf_free(&multi_value);
- goto on_error;
- }
-
- *var_value = git_buf_detach(&multi_value);
- }
- }
-
- git__free(line);
- return 0;
-
-on_error:
- git__free(*var_name);
- git__free(line);
- return -1;
+ return do_match_gitdir(matches, repo, cfg_file, value, false);
}
-static int config_parse(
- struct reader *reader,
- int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data),
- int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data),
- int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data),
- int (*on_eof)(struct reader **reader, const char *current_section, void *data),
- void *data)
+static int conditional_match_gitdir_i(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *value)
{
- char *current_section = NULL, *var_name, *var_value, *line_start;
- char c;
- size_t line_len;
- int result = 0;
+ return do_match_gitdir(matches, repo, cfg_file, value, true);
+}
- skip_bom(reader);
+static const struct {
+ const char *prefix;
+ int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value);
+} conditions[] = {
+ { "gitdir:", conditional_match_gitdir },
+ { "gitdir/i:", conditional_match_gitdir_i }
+};
- while (result == 0 && !reader->eof) {
- line_start = reader->read_ptr;
+static int parse_conditional_include(git_config_parser *reader,
+ struct parse_data *parse_data, const char *section, const char *file)
+{
+ char *condition;
+ size_t i;
+ int error = 0, matches;
- c = reader_peek(reader, SKIP_WHITESPACE);
+ if (!parse_data->repo)
+ return 0;
- switch (c) {
- case '\0': /* EOF when peeking, set EOF in the reader to exit the loop */
- reader->eof = 1;
- break;
+ condition = git__substrdup(section + strlen("includeIf."),
+ strlen(section) - strlen("includeIf.") - strlen(".path"));
- case '[': /* section header, new section begins */
- git__free(current_section);
- current_section = NULL;
+ for (i = 0; i < ARRAY_SIZE(conditions); i++) {
+ if (git__prefixcmp(condition, conditions[i].prefix))
+ continue;
- if ((result = parse_section_header(reader, ¤t_section)) == 0 && on_section) {
- line_len = reader->read_ptr - line_start;
- result = on_section(&reader, current_section, line_start, line_len, data);
- }
+ if ((error = conditions[i].matches(&matches,
+ parse_data->repo,
+ parse_data->file_path,
+ condition + strlen(conditions[i].prefix))) < 0)
break;
- case '\n': /* comment or whitespace-only */
- case ';':
- case '#':
- reader_consume_line(reader);
-
- if (on_comment) {
- line_len = reader->read_ptr - line_start;
- result = on_comment(&reader, line_start, line_len, data);
- }
- break;
+ if (matches)
+ error = parse_include(reader, parse_data, file);
- default: /* assume variable declaration */
- if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) {
- line_len = reader->read_ptr - line_start;
- result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data);
- }
- break;
- }
+ break;
}
- if (on_eof)
- result = on_eof(&reader, current_section, data);
-
- git__free(current_section);
- return result;
+ git__free(condition);
+ return error;
}
-struct parse_data {
- git_strmap *values;
- diskfile_backend *cfg_file;
- uint32_t reader_idx;
- git_config_level_t level;
- int depth;
-};
-
static int read_on_variable(
- struct reader **reader,
+ git_config_parser *reader,
const char *current_section,
char *var_name,
char *var_value,
result = 0;
/* Add or append the new config option */
- if (!git__strcmp(var->entry->name, "include.path")) {
- struct reader *r;
- git_buf path = GIT_BUF_INIT;
- char *dir;
- uint32_t index;
-
- r = git_array_alloc(parse_data->cfg_file->readers);
- /* The reader may have been reallocated */
- *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
- memset(r, 0, sizeof(struct reader));
-
- if ((result = git_path_dirname_r(&path, (*reader)->file_path)) < 0)
- return result;
-
- /* We need to know our index in the array, as the next config_parse call may realloc */
- index = git_array_size(parse_data->cfg_file->readers) - 1;
- dir = git_buf_detach(&path);
- result = included_path(&path, dir, var->entry->value);
- git__free(dir);
+ if (!git__strcmp(var->entry->name, "include.path"))
+ result = parse_include(reader, parse_data, var->entry->value);
+ else if (!git__prefixcmp(var->entry->name, "includeif.") &&
+ !git__suffixcmp(var->entry->name, ".path"))
+ result = parse_conditional_include(reader, parse_data,
+ var->entry->name, var->entry->value);
- if (result < 0)
- return result;
-
- r->file_path = git_buf_detach(&path);
- git_buf_init(&r->buffer, 0);
-
- result = git_futils_readbuffer_updated(
- &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);
- r = git_array_get(parse_data->cfg_file->readers, index);
- *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
- } else if (result == GIT_ENOTFOUND) {
- giterr_clear();
- result = 0;
- }
-
- git_buf_free(&r->buffer);
- }
return result;
}
-static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
+static int config_read(
+ git_strmap *values,
+ const git_repository *repo,
+ git_config_file *file,
+ git_config_level_t level,
+ int depth)
{
struct parse_data parse_data;
+ git_config_parser reader;
+ git_buf contents = GIT_BUF_INIT;
+ int error;
if (depth >= MAX_INCLUDE_DEPTH) {
giterr_set(GITERR_CONFIG, "maximum config include depth reached");
return -1;
}
+ if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
+ goto out;
+
+ git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
+
+ if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
+ goto out;
+
/* Initialize the reading position */
- reader->read_ptr = reader->buffer.ptr;
- reader->eof = 0;
+ reader.file = file;
+ git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
/* If the file is empty, there's nothing for us to do */
- if (*reader->read_ptr == '\0')
- return 0;
+ if (!reader.ctx.content || *reader.ctx.content == '\0')
+ goto out;
+ parse_data.repo = repo;
+ parse_data.file_path = file->path;
parse_data.values = values;
- parse_data.cfg_file = cfg_file;
- parse_data.reader_idx = git_array_size(cfg_file->readers) - 1;
parse_data.level = level;
parse_data.depth = depth;
- return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data);
+ error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
+
+out:
+ git_buf_free(&contents);
+ return error;
}
static int write_section(git_buf *fbuf, const char *key)
git_buf buffered_comment;
unsigned int in_section : 1,
preg_replaced : 1;
+ const char *orig_section;
const char *section;
+ const char *orig_name;
const char *name;
const regex_t *preg;
const char *value;
q = quotes_for_value(write_data->value);
result = git_buf_printf(write_data->buf,
- "\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
+ "\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q);
/* If we are updating a single name/value, we're done. Setting `value`
* to `NULL` will prevent us from trying to write it again later (in
}
static int write_on_section(
- struct reader **reader,
+ git_config_parser *reader,
const char *current_section,
const char *line,
size_t line_len,
}
static int write_on_variable(
- struct reader **reader,
+ git_config_parser *reader,
const char *current_section,
char *var_name,
char *var_value,
return write_value(write_data);
}
-static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data)
+static int write_on_comment(git_config_parser *reader, const char *line, size_t line_len, void *data)
{
struct write_data *write_data;
}
static int write_on_eof(
- struct reader **reader, const char *current_section, void *data)
+ git_config_parser *reader, const char *current_section, void *data)
{
struct write_data *write_data = (struct write_data *)data;
int result = 0;
if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
/* write the section header unless we're already in it */
if (!current_section || strcmp(current_section, write_data->section))
- result = write_section(write_data->buf, write_data->section);
+ result = write_section(write_data->buf, write_data->orig_section);
if (!result)
result = write_value(write_data);
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
-static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
+static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char* value)
{
int result;
- char *section, *name, *ldot;
+ char *orig_section, *section, *orig_name, *name, *ldot;
git_filebuf file = GIT_FILEBUF_INIT;
- git_buf buf = GIT_BUF_INIT;
- struct reader *reader = git_array_get(cfg->readers, 0);
+ git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT;
+ git_config_parser reader;
struct write_data write_data;
+ memset(&reader, 0, sizeof(reader));
+ reader.file = &cfg->file;
+
if (cfg->locked) {
- result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content));
+ result = git_buf_puts(&contents, 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);
+ &file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
+ git_buf_free(&contents);
return result;
}
/* We need to read in our own config file */
- result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
+ result = git_futils_readbuffer(&contents, cfg->file.path);
}
/* Initialise the reading position */
- if (result == GIT_ENOTFOUND) {
- reader->read_ptr = NULL;
- reader->eof = 1;
- git_buf_clear(&reader->buffer);
- } else if (result == 0) {
- reader->read_ptr = reader->buffer.ptr;
- reader->eof = 0;
+ if (result == 0 || result == GIT_ENOTFOUND) {
+ git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
} else {
git_filebuf_cleanup(&file);
return -1; /* OS error when reading the file */
ldot = strrchr(key, '.');
name = ldot + 1;
section = git__strndup(key, ldot - key);
+ GITERR_CHECK_ALLOC(section);
+
+ ldot = strrchr(orig_key, '.');
+ orig_name = ldot + 1;
+ orig_section = git__strndup(orig_key, ldot - orig_key);
+ GITERR_CHECK_ALLOC(orig_section);
write_data.buf = &buf;
git_buf_init(&write_data.buffered_comment, 0);
+ write_data.orig_section = orig_section;
write_data.section = section;
write_data.in_section = 0;
write_data.preg_replaced = 0;
+ write_data.orig_name = orig_name;
write_data.name = name;
write_data.preg = preg;
write_data.value = value;
- result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
+ result = git_config_parse(&reader,
+ write_on_section,
+ write_on_variable,
+ write_on_comment,
+ write_on_eof,
+ &write_data);
git__free(section);
+ git__free(orig_section);
git_buf_free(&write_data.buffered_comment);
if (result < 0) {
done:
git_buf_free(&buf);
- git_buf_free(&reader->buffer);
+ git_buf_free(&contents);
+ git_parse_ctx_clear(&reader.ctx);
return result;
}
#ifndef INCLUDE_config_file_h__
#define INCLUDE_config_file_h__
+#include "common.h"
+
#include "git2/sys/config.h"
#include "git2/config.h"
-GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level)
+GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
{
- return cfg->open(cfg, level);
+ return cfg->open(cfg, level, repo);
}
GIT_INLINE(void) git_config_file_free(git_config_backend *cfg)
extern int git_config_file_normalize_section(char *start, char *end);
#endif
-
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "config_parse.h"
+
+#include "buf_text.h"
+
+#include <ctype.h>
+
+static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
+{
+ giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
+ error_str, reader->file->path, reader->ctx.line_num, col);
+}
+
+
+GIT_INLINE(int) config_keychar(int c)
+{
+ return isalnum(c) || c == '-';
+}
+
+static int strip_comments(char *line, int in_quotes)
+{
+ int quote_count = in_quotes, backslash_count = 0;
+ char *ptr;
+
+ for (ptr = line; *ptr; ++ptr) {
+ if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
+ quote_count++;
+
+ if ((ptr[0] == ';' || ptr[0] == '#') &&
+ (quote_count % 2) == 0 &&
+ (backslash_count % 2) == 0) {
+ ptr[0] = '\0';
+ break;
+ }
+
+ if (ptr[0] == '\\')
+ backslash_count++;
+ else
+ backslash_count = 0;
+ }
+
+ /* skip any space at the end */
+ while (ptr > line && git__isspace(ptr[-1])) {
+ ptr--;
+ }
+ ptr[0] = '\0';
+
+ return quote_count;
+}
+
+
+static int parse_section_header_ext(git_config_parser *reader, const char *line, const char *base_name, char **section_name)
+{
+ int c, rpos;
+ char *first_quote, *last_quote;
+ git_buf buf = GIT_BUF_INIT;
+ size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
+
+ /*
+ * base_name is what came before the space. We should be at the
+ * first quotation mark, except for now, line isn't being kept in
+ * sync so we only really use it to calculate the length.
+ */
+
+ first_quote = strchr(line, '"');
+ if (first_quote == NULL) {
+ set_parse_error(reader, 0, "Missing quotation marks in section header");
+ goto end_error;
+ }
+
+ last_quote = strrchr(line, '"');
+ quoted_len = last_quote - first_quote;
+
+ if (quoted_len == 0) {
+ set_parse_error(reader, 0, "Missing closing quotation mark in section header");
+ goto end_error;
+ }
+
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+ if (git_buf_grow(&buf, alloc_len) < 0 ||
+ git_buf_printf(&buf, "%s.", base_name) < 0)
+ goto end_error;
+
+ rpos = 0;
+
+ line = first_quote;
+ c = line[++rpos];
+
+ /*
+ * At the end of each iteration, whatever is stored in c will be
+ * added to the string. In case of error, jump to out
+ */
+ do {
+
+ switch (c) {
+ case 0:
+ set_parse_error(reader, 0, "Unexpected end-of-line in section header");
+ goto end_error;
+
+ case '"':
+ goto end_parse;
+
+ case '\\':
+ c = line[++rpos];
+
+ if (c == 0) {
+ set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
+ goto end_error;
+ }
+
+ default:
+ break;
+ }
+
+ git_buf_putc(&buf, (char)c);
+ c = line[++rpos];
+ } while (line + rpos < last_quote);
+
+end_parse:
+ if (git_buf_oom(&buf))
+ goto end_error;
+
+ if (line[rpos] != '"' || line[rpos + 1] != ']') {
+ set_parse_error(reader, rpos, "Unexpected text after closing quotes");
+ git_buf_free(&buf);
+ return -1;
+ }
+
+ *section_name = git_buf_detach(&buf);
+ return 0;
+
+end_error:
+ git_buf_free(&buf);
+
+ return -1;
+}
+
+static int parse_section_header(git_config_parser *reader, char **section_out)
+{
+ char *name, *name_end;
+ int name_length, c, pos;
+ int result;
+ char *line;
+ size_t line_len;
+
+ git_parse_advance_ws(&reader->ctx);
+ line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+ if (line == NULL)
+ return -1;
+
+ /* find the end of the variable's name */
+ name_end = strrchr(line, ']');
+ if (name_end == NULL) {
+ git__free(line);
+ set_parse_error(reader, 0, "Missing ']' in section header");
+ return -1;
+ }
+
+ GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
+ name = git__malloc(line_len);
+ GITERR_CHECK_ALLOC(name);
+
+ name_length = 0;
+ pos = 0;
+
+ /* Make sure we were given a section header */
+ c = line[pos++];
+ assert(c == '[');
+
+ c = line[pos++];
+
+ do {
+ if (git__isspace(c)){
+ name[name_length] = '\0';
+ result = parse_section_header_ext(reader, line, name, section_out);
+ git__free(line);
+ git__free(name);
+ return result;
+ }
+
+ if (!config_keychar(c) && c != '.') {
+ set_parse_error(reader, pos, "Unexpected character in header");
+ goto fail_parse;
+ }
+
+ name[name_length++] = (char)git__tolower(c);
+
+ } while ((c = line[pos++]) != ']');
+
+ if (line[pos - 1] != ']') {
+ set_parse_error(reader, pos, "Unexpected end of file");
+ goto fail_parse;
+ }
+
+ git__free(line);
+
+ name[name_length] = 0;
+ *section_out = name;
+
+ return 0;
+
+fail_parse:
+ git__free(line);
+ git__free(name);
+ return -1;
+}
+
+static int skip_bom(git_parse_ctx *parser)
+{
+ git_buf buf = GIT_BUF_INIT_CONST(parser->content, parser->content_len);
+ git_bom_t bom;
+ int bom_offset = git_buf_text_detect_bom(&bom, &buf);
+
+ if (bom == GIT_BOM_UTF8)
+ git_parse_advance_chars(parser, bom_offset);
+
+ /* TODO: reference implementation is pretty stupid with BoM */
+
+ return 0;
+}
+
+/*
+ (* basic types *)
+ digit = "0".."9"
+ integer = digit { digit }
+ alphabet = "a".."z" + "A" .. "Z"
+
+ section_char = alphabet | "." | "-"
+ extension_char = (* any character except newline *)
+ any_char = (* any character *)
+ variable_char = "alphabet" | "-"
+
+
+ (* actual grammar *)
+ config = { section }
+
+ section = header { definition }
+
+ header = "[" section [subsection | subsection_ext] "]"
+
+ subsection = "." section
+ subsection_ext = "\"" extension "\""
+
+ section = section_char { section_char }
+ extension = extension_char { extension_char }
+
+ definition = variable_name ["=" variable_value] "\n"
+
+ variable_name = variable_char { variable_char }
+ variable_value = string | boolean | integer
+
+ string = quoted_string | plain_string
+ quoted_string = "\"" plain_string "\""
+ plain_string = { any_char }
+
+ boolean = boolean_true | boolean_false
+ boolean_true = "yes" | "1" | "true" | "on"
+ boolean_false = "no" | "0" | "false" | "off"
+*/
+
+/* '\"' -> '"' etc */
+static int unescape_line(
+ char **out, bool *is_multi, const char *ptr, int quote_count)
+{
+ char *str, *fixed, *esc;
+ size_t ptr_len = strlen(ptr), alloc_len;
+
+ *is_multi = false;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
+ (str = git__malloc(alloc_len)) == NULL) {
+ return -1;
+ }
+
+ fixed = str;
+
+ while (*ptr != '\0') {
+ if (*ptr == '"') {
+ quote_count++;
+ } else if (*ptr != '\\') {
+ *fixed++ = *ptr;
+ } else {
+ /* backslash, check the next char */
+ ptr++;
+ /* if we're at the end, it's a multiline, so keep the backslash */
+ if (*ptr == '\0') {
+ *is_multi = true;
+ goto done;
+ }
+ if ((esc = strchr(git_config_escapes, *ptr)) != NULL) {
+ *fixed++ = git_config_escaped[esc - git_config_escapes];
+ } else {
+ git__free(str);
+ giterr_set(GITERR_CONFIG, "invalid escape at %s", ptr);
+ return -1;
+ }
+ }
+ ptr++;
+ }
+
+done:
+ *fixed = '\0';
+ *out = str;
+
+ return 0;
+}
+
+static int parse_multiline_variable(git_config_parser *reader, git_buf *value, int in_quotes)
+{
+ char *line = NULL, *proc_line = NULL;
+ int quote_count;
+ bool multiline;
+
+ /* Check that the next line exists */
+ git_parse_advance_line(&reader->ctx);
+ line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+ if (line == NULL)
+ return -1;
+
+ /* We've reached the end of the file, there is no continuation.
+ * (this is not an error).
+ */
+ if (line[0] == '\0') {
+ git__free(line);
+ return 0;
+ }
+
+ quote_count = strip_comments(line, !!in_quotes);
+
+ /* If it was just a comment, pretend it didn't exist */
+ if (line[0] == '\0') {
+ git__free(line);
+ return parse_multiline_variable(reader, value, quote_count);
+ /* TODO: unbounded recursion. This **could** be exploitable */
+ }
+
+ if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
+ git__free(line);
+ return -1;
+ }
+ /* add this line to the multiline var */
+
+ git_buf_puts(value, proc_line);
+ git__free(line);
+ git__free(proc_line);
+
+ /*
+ * If we need to continue reading the next line, let's just
+ * keep putting stuff in the buffer
+ */
+ if (multiline)
+ return parse_multiline_variable(reader, value, quote_count);
+
+ return 0;
+}
+
+GIT_INLINE(bool) is_namechar(char c)
+{
+ return isalnum(c) || c == '-';
+}
+
+static int parse_name(
+ char **name, const char **value, git_config_parser *reader, const char *line)
+{
+ const char *name_end = line, *value_start;
+
+ *name = NULL;
+ *value = NULL;
+
+ while (*name_end && is_namechar(*name_end))
+ name_end++;
+
+ if (line == name_end) {
+ set_parse_error(reader, 0, "Invalid configuration key");
+ return -1;
+ }
+
+ value_start = name_end;
+
+ while (*value_start && git__isspace(*value_start))
+ value_start++;
+
+ if (*value_start == '=') {
+ *value = value_start + 1;
+ } else if (*value_start) {
+ set_parse_error(reader, 0, "Invalid configuration key");
+ return -1;
+ }
+
+ if ((*name = git__strndup(line, name_end - line)) == NULL)
+ return -1;
+
+ return 0;
+}
+
+static int parse_variable(git_config_parser *reader, char **var_name, char **var_value)
+{
+ const char *value_start = NULL;
+ char *line;
+ int quote_count;
+ bool multiline;
+
+ git_parse_advance_ws(&reader->ctx);
+ line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+ if (line == NULL)
+ return -1;
+
+ quote_count = strip_comments(line, 0);
+
+ /* If there is no value, boolean true is assumed */
+ *var_value = NULL;
+
+ if (parse_name(var_name, &value_start, reader, line) < 0)
+ goto on_error;
+
+ /*
+ * Now, let's try to parse the value
+ */
+ if (value_start != NULL) {
+ while (git__isspace(value_start[0]))
+ value_start++;
+
+ if (unescape_line(var_value, &multiline, value_start, 0) < 0)
+ goto on_error;
+
+ if (multiline) {
+ git_buf multi_value = GIT_BUF_INIT;
+ git_buf_attach(&multi_value, *var_value, 0);
+
+ if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
+ git_buf_oom(&multi_value)) {
+ git_buf_free(&multi_value);
+ goto on_error;
+ }
+
+ *var_value = git_buf_detach(&multi_value);
+ }
+ }
+
+ git__free(line);
+ return 0;
+
+on_error:
+ git__free(*var_name);
+ git__free(line);
+ return -1;
+}
+
+int git_config_parse(
+ git_config_parser *parser,
+ git_config_parser_section_cb on_section,
+ git_config_parser_variable_cb on_variable,
+ git_config_parser_comment_cb on_comment,
+ git_config_parser_eof_cb on_eof,
+ void *data)
+{
+ git_parse_ctx *ctx;
+ char *current_section = NULL, *var_name, *var_value;
+ int result = 0;
+
+ ctx = &parser->ctx;
+
+ skip_bom(ctx);
+
+ for (; ctx->remain_len > 0; git_parse_advance_line(ctx)) {
+ const char *line_start = parser->ctx.line;
+ size_t line_len = parser->ctx.line_len;
+ char c;
+
+ /*
+ * Get either first non-whitespace character or, if that does
+ * not exist, the first whitespace character. This is required
+ * to preserve whitespaces when writing back the file.
+ */
+ if (git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE) < 0 &&
+ git_parse_peek(&c, ctx, 0) < 0)
+ continue;
+
+ switch (c) {
+ case '[': /* section header, new section begins */
+ git__free(current_section);
+ current_section = NULL;
+
+ if ((result = parse_section_header(parser, ¤t_section)) == 0 && on_section) {
+ result = on_section(parser, current_section, line_start, line_len, data);
+ }
+ break;
+
+ case '\n': /* comment or whitespace-only */
+ case '\r':
+ case ' ':
+ case '\t':
+ case ';':
+ case '#':
+ if (on_comment) {
+ result = on_comment(parser, line_start, line_len, data);
+ }
+ break;
+
+ default: /* assume variable declaration */
+ if ((result = parse_variable(parser, &var_name, &var_value)) == 0 && on_variable) {
+ result = on_variable(parser, current_section, var_name, var_value, line_start, line_len, data);
+ }
+ break;
+ }
+
+ if (result < 0)
+ goto out;
+ }
+
+ if (on_eof)
+ result = on_eof(parser, current_section, data);
+
+out:
+ git__free(current_section);
+ return result;
+}
--- /dev/null
+/*
+ * 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_config_parse_h__
+#define INCLUDE_config_parse_h__
+
+#include "common.h"
+#include "array.h"
+#include "oid.h"
+#include "parse.h"
+
+static const char *git_config_escapes = "ntb\"\\";
+static const char *git_config_escaped = "\n\t\b\"\\";
+
+typedef struct config_file {
+ git_oid checksum;
+ char *path;
+ git_array_t(struct config_file) includes;
+} git_config_file;
+
+typedef struct {
+ struct config_file *file;
+ git_parse_ctx ctx;
+} git_config_parser;
+
+typedef int (*git_config_parser_section_cb)(
+ git_config_parser *parser,
+ const char *current_section,
+ const char *line,
+ size_t line_len,
+ void *data);
+
+typedef int (*git_config_parser_variable_cb)(
+ git_config_parser *parser,
+ const char *current_section,
+ char *var_name,
+ char *var_value,
+ const char *line,
+ size_t line_len,
+ void *data);
+
+typedef int (*git_config_parser_comment_cb)(
+ git_config_parser *parser,
+ const char *line,
+ size_t line_len,
+ void *data);
+
+typedef int (*git_config_parser_eof_cb)(
+ git_config_parser *parser,
+ const char *current_section,
+ void *data);
+
+int git_config_parse(
+ git_config_parser *parser,
+ git_config_parser_section_cb on_section,
+ git_config_parser_variable_cb on_variable,
+ git_config_parser_comment_cb on_comment,
+ git_config_parser_eof_cb on_eof,
+ void *data);
+
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "common.h"
+
#include "git2/attr.h"
#include "git2/blob.h"
#include "git2/index.h"
#include "git2/sys/filter.h"
-#include "common.h"
#include "fileops.h"
#include "hash.h"
#include "filter.h"
+++ /dev/null
-/*
- * 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.
- */
-
-#ifdef GIT_CURL
-
-#include <curl/curl.h>
-
-#include "stream.h"
-#include "git2/transport.h"
-#include "buffer.h"
-#include "vector.h"
-#include "proxy.h"
-
-/* This is for backwards compatibility with curl<7.45.0. */
-#ifndef CURLINFO_ACTIVESOCKET
-# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
-# define GIT_CURL_BADSOCKET -1
-# define git_activesocket_t long
-#else
-# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
-# define git_activesocket_t curl_socket_t
-#endif
-
-typedef struct {
- git_stream parent;
- CURL *handle;
- curl_socket_t socket;
- char curl_error[CURL_ERROR_SIZE + 1];
- git_cert_x509 cert_info;
- git_strarray cert_info_strings;
- git_proxy_options proxy;
- git_cred *proxy_cred;
-} curl_stream;
-
-static int seterr_curl(curl_stream *s)
-{
- giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
- return -1;
-}
-
-GIT_INLINE(int) error_no_credentials(void)
-{
- giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
- return GIT_EAUTH;
-}
-
-static int apply_proxy_creds(curl_stream *s)
-{
- CURLcode res;
- git_cred_userpass_plaintext *userpass;
-
- if (!s->proxy_cred)
- return GIT_ENOTFOUND;
-
- userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
- return seterr_curl(s);
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
- return seterr_curl(s);
-
- return 0;
-}
-
-static int ask_and_apply_proxy_creds(curl_stream *s)
-{
- int error;
- git_proxy_options *opts = &s->proxy;
-
- if (!opts->credentials)
- return error_no_credentials();
-
- /* TODO: see if PROXYAUTH_AVAIL helps us here */
- git_cred_free(s->proxy_cred);
- s->proxy_cred = NULL;
- giterr_clear();
- error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
- if (error == GIT_PASSTHROUGH)
- return error_no_credentials();
- if (error < 0) {
- if (!giterr_last())
- giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
- return error;
- }
-
- if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
- giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
- return -1;
- }
-
- return apply_proxy_creds(s);
-}
-
-static int curls_connect(git_stream *stream)
-{
- curl_stream *s = (curl_stream *) stream;
- git_activesocket_t sockextr;
- long connect_last = 0;
- int failed_cert = 0, error;
- bool retry_connect;
- CURLcode res;
-
- /* Apply any credentials we've already established */
- error = apply_proxy_creds(s);
- if (error < 0 && error != GIT_ENOTFOUND)
- return seterr_curl(s);
-
- do {
- retry_connect = 0;
- res = curl_easy_perform(s->handle);
-
- curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
-
- /* HTTP 407 Proxy Authentication Required */
- if (connect_last == 407) {
- if ((error = ask_and_apply_proxy_creds(s)) < 0)
- return error;
-
- retry_connect = true;
- }
- } while (retry_connect);
-
- if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
- return seterr_curl(s);
- if (res == CURLE_PEER_FAILED_VERIFICATION)
- failed_cert = 1;
-
- if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
- return seterr_curl(s);
- }
-
- if (sockextr == GIT_CURL_BADSOCKET) {
- giterr_set(GITERR_NET, "curl socket is no longer valid");
- return -1;
- }
-
- s->socket = sockextr;
-
- if (s->parent.encrypted && failed_cert)
- return GIT_ECERTIFICATE;
-
- return 0;
-}
-
-static int curls_certificate(git_cert **out, git_stream *stream)
-{
- int error;
- CURLcode res;
- struct curl_slist *slist;
- struct curl_certinfo *certinfo;
- git_vector strings = GIT_VECTOR_INIT;
- curl_stream *s = (curl_stream *) stream;
-
- if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
- return seterr_curl(s);
-
- /* No information is available, can happen with SecureTransport */
- if (certinfo->num_of_certs == 0) {
- s->cert_info.parent.cert_type = GIT_CERT_NONE;
- s->cert_info.data = NULL;
- s->cert_info.len = 0;
- return 0;
- }
-
- if ((error = git_vector_init(&strings, 8, NULL)) < 0)
- return error;
-
- 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.parent.cert_type = GIT_CERT_STRARRAY;
- s->cert_info.data = &s->cert_info_strings;
- s->cert_info.len = strings.length;
-
- *out = &s->cert_info.parent;
-
- return 0;
-}
-
-static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
-{
- int error;
- CURLcode res;
- curl_stream *s = (curl_stream *) stream;
-
- if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
- return error;
-
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
- return seterr_curl(s);
-
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
- return seterr_curl(s);
-
- return 0;
-}
-
-static int wait_for(curl_socket_t fd, bool reading)
-{
- int ret;
- fd_set infd, outfd, errfd;
-
- FD_ZERO(&infd);
- FD_ZERO(&outfd);
- FD_ZERO(&errfd);
-
- assert(fd >= 0);
- FD_SET(fd, &errfd);
- if (reading)
- FD_SET(fd, &infd);
- else
- FD_SET(fd, &outfd);
-
- if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
- giterr_set(GITERR_OS, "error in select");
- return -1;
- }
-
- return 0;
-}
-
-static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
-{
- int error;
- size_t off = 0, sent;
- CURLcode res;
- curl_stream *s = (curl_stream *) stream;
-
- GIT_UNUSED(flags);
-
- do {
- if ((error = wait_for(s->socket, false)) < 0)
- return error;
-
- res = curl_easy_send(s->handle, data + off, len - off, &sent);
- if (res == CURLE_OK)
- off += sent;
- } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
-
- if (res != CURLE_OK)
- return seterr_curl(s);
-
- return len;
-}
-
-static ssize_t curls_read(git_stream *stream, void *data, size_t len)
-{
- int error;
- size_t read;
- CURLcode res;
- curl_stream *s = (curl_stream *) stream;
-
- do {
- if ((error = wait_for(s->socket, true)) < 0)
- return error;
-
- res = curl_easy_recv(s->handle, data, len, &read);
- } while (res == CURLE_AGAIN);
-
- if (res != CURLE_OK)
- return seterr_curl(s);
-
- return read;
-}
-
-static int curls_close(git_stream *stream)
-{
- curl_stream *s = (curl_stream *) stream;
-
- if (!s->handle)
- return 0;
-
- curl_easy_cleanup(s->handle);
- s->handle = NULL;
- s->socket = 0;
-
- return 0;
-}
-
-static void curls_free(git_stream *stream)
-{
- curl_stream *s = (curl_stream *) stream;
-
- curls_close(stream);
- git_strarray_free(&s->cert_info_strings);
- git__free(s);
-}
-
-int git_curl_stream_new(git_stream **out, const char *host, const char *port)
-{
- curl_stream *st;
- CURL *handle;
- int iport = 0, error;
-
- st = git__calloc(1, sizeof(curl_stream));
- GITERR_CHECK_ALLOC(st);
-
- 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) {
- git__free(st);
- return error;
- }
-
- curl_easy_setopt(handle, CURLOPT_URL, host);
- curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
- curl_easy_setopt(handle, CURLOPT_PORT, iport);
- curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
- 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); */
-
- st->parent.version = GIT_STREAM_VERSION;
- st->parent.encrypted = 0; /* we don't encrypt ourselves */
- st->parent.proxy_support = 1;
- st->parent.connect = curls_connect;
- st->parent.certificate = curls_certificate;
- st->parent.set_proxy = curls_set_proxy;
- st->parent.read = curls_read;
- st->parent.write = curls_write;
- st->parent.close = curls_close;
- st->parent.free = curls_free;
- st->handle = handle;
-
- *out = (git_stream *) st;
- return 0;
-}
-
-#else
-
-#include "stream.h"
-
-int git_curl_stream_new(git_stream **out, const char *host, const char *port)
-{
- GIT_UNUSED(out);
- GIT_UNUSED(host);
- GIT_UNUSED(port);
-
- giterr_set(GITERR_NET, "curl is not supported in this version");
- return -1;
-}
-
-
-#endif
+++ /dev/null
-/*
- * 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_curl_stream_h__
-#define INCLUDE_curl_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
#define INCLUDE_git_delta_h__
#include "common.h"
+
#include "pack.h"
typedef struct git_delta_index git_delta_index;
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
+#include "common.h"
+
#include "git2/describe.h"
#include "git2/strarray.h"
#include "git2/diff.h"
#include "git2/status.h"
-#include "common.h"
#include "commit.h"
#include "commit_list.h"
#include "oidmap.h"
* 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/version.h"
-#include "common.h"
+
#include "diff.h"
+
+#include "git2/version.h"
#include "diff_generate.h"
#include "patch.h"
#include "commit.h"
#include "index.h"
-#define DIFF_FLAG_IS_SET(DIFF,FLAG) \
- (((DIFF)->opts.flags & (FLAG)) != 0)
-#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) \
- (((DIFF)->opts.flags & (FLAG)) == 0)
-#define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->opts.flags = \
- (VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
+struct patch_id_args {
+ git_hash_ctx ctx;
+ git_oid result;
+ int first_file;
+};
GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
{
return 0;
}
+static int flush_hunk(git_oid *result, git_hash_ctx *ctx)
+{
+ git_oid hash;
+ unsigned short carry = 0;
+ int error, i;
+
+ if ((error = git_hash_final(&hash, ctx)) < 0 ||
+ (error = git_hash_init(ctx)) < 0)
+ return error;
+
+ for (i = 0; i < GIT_OID_RAWSZ; i++) {
+ carry += result->id[i] + hash.id[i];
+ result->id[i] = carry;
+ carry >>= 8;
+ }
+
+ return 0;
+}
+
+static void strip_spaces(git_buf *buf)
+{
+ char *src = buf->ptr, *dst = buf->ptr;
+ char c;
+ size_t len = 0;
+
+ while ((c = *src++) != '\0') {
+ if (!git__isspace(c)) {
+ *dst++ = c;
+ len++;
+ }
+ }
+
+ git_buf_truncate(buf, len);
+}
+
+static int file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ struct patch_id_args *args = (struct patch_id_args *) payload;
+ git_buf buf = GIT_BUF_INIT;
+ int error;
+
+ GIT_UNUSED(progress);
+
+ if (!args->first_file &&
+ (error = flush_hunk(&args->result, &args->ctx)) < 0)
+ goto out;
+ args->first_file = 0;
+
+ if ((error = git_buf_printf(&buf,
+ "diff--gita/%sb/%s---a/%s+++b/%s",
+ delta->old_file.path,
+ delta->new_file.path,
+ delta->old_file.path,
+ delta->new_file.path)) < 0)
+ goto out;
+
+ strip_spaces(&buf);
+
+ if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
+ goto out;
+
+out:
+ git_buf_free(&buf);
+ return error;
+}
+
+static int line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ struct patch_id_args *args = (struct patch_id_args *) payload;
+ git_buf buf = GIT_BUF_INIT;
+ int error;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk);
+
+ switch (line->origin) {
+ case GIT_DIFF_LINE_ADDITION:
+ git_buf_putc(&buf, '+');
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ git_buf_putc(&buf, '-');
+ break;
+ case GIT_DIFF_LINE_CONTEXT:
+ break;
+ default:
+ giterr_set(GITERR_PATCH, "invalid line origin for patch");
+ return -1;
+ }
+
+ git_buf_put(&buf, line->content, line->content_len);
+ strip_spaces(&buf);
+
+ if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
+ goto out;
+
+out:
+ git_buf_free(&buf);
+ return error;
+}
+
+int git_diff_patchid_init_options(git_diff_patchid_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_INIT);
+ return 0;
+}
+
+int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts)
+{
+ struct patch_id_args args;
+ int error;
+
+ GITERR_CHECK_VERSION(
+ opts, GIT_DIFF_PATCHID_OPTIONS_VERSION, "git_diff_patchid_options");
+
+ memset(&args, 0, sizeof(args));
+ args.first_file = 1;
+ if ((error = git_hash_ctx_init(&args.ctx)) < 0)
+ goto out;
+
+ if ((error = git_diff_foreach(diff, file_cb, NULL, NULL, line_cb, &args)) < 0)
+ goto out;
+
+ if ((error = (flush_hunk(&args.result, &args.ctx))) < 0)
+ goto out;
+
+ git_oid_cpy(out, &args.result);
+
+out:
+ git_hash_ctx_cleanup(&args.ctx);
+ return error;
+}
#ifndef INCLUDE_diff_h__
#define INCLUDE_diff_h__
+#include "common.h"
+
#include "git2/diff.h"
#include "git2/patch.h"
#include "git2/sys/diff.h"
struct git_diff {
git_refcount rc;
git_repository *repo;
+ git_attr_session attrsession;
git_diff_origin_t type;
git_diff_options opts;
git_vector deltas; /* vector of git_diff_delta */
extern int git_diff__entry_icmp(const void *a, const void *b);
#endif
-
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
+#include "diff_driver.h"
#include "git2/attr.h"
#include "diff.h"
-#include "diff_driver.h"
#include "strmap.h"
#include "map.h"
#include "buf_text.h"
}
int git_diff_driver_lookup(
- git_diff_driver **out, git_repository *repo, const char *path)
+ git_diff_driver **out, git_repository *repo,
+ git_attr_session *attrsession, const char *path)
{
int error = 0;
- const char *value;
+ const char *values[1], *attrs[] = { "diff" };
assert(out);
*out = NULL;
if (!repo || !path || !strlen(path))
/* just use the auto value */;
- else if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0)
+ else if ((error = git_attr_get_many_with_session(values, repo,
+ attrsession, 0, path, 1, attrs)) < 0)
/* return error below */;
- else if (GIT_ATTR_UNSPECIFIED(value))
+
+ else if (GIT_ATTR_UNSPECIFIED(values[0]))
/* just use the auto value */;
- else if (GIT_ATTR_FALSE(value))
+ else if (GIT_ATTR_FALSE(values[0]))
*out = &global_drivers[DIFF_DRIVER_BINARY];
- else if (GIT_ATTR_TRUE(value))
+ else if (GIT_ATTR_TRUE(values[0]))
*out = &global_drivers[DIFF_DRIVER_TEXT];
/* otherwise look for driver information in config and build driver */
- else if ((error = git_diff_driver_load(out, repo, value)) < 0) {
+ else if ((error = git_diff_driver_load(out, repo, values[0])) < 0) {
if (error == GIT_ENOTFOUND) {
error = 0;
giterr_clear();
#define INCLUDE_diff_driver_h__
#include "common.h"
+
+#include "attr_file.h"
#include "buffer.h"
typedef struct git_diff_driver_registry git_diff_driver_registry;
typedef struct git_diff_driver git_diff_driver;
-int git_diff_driver_lookup(git_diff_driver **, git_repository *, const char *);
+int git_diff_driver_lookup(git_diff_driver **, git_repository *,
+ git_attr_session *attrsession, const char *);
void git_diff_driver_free(git_diff_driver *);
/* diff option flags to force off and on for this driver */
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
+#include "diff_file.h"
+
#include "git2/blob.h"
#include "git2/submodule.h"
#include "diff.h"
#include "diff_generate.h"
-#include "diff_file.h"
#include "odb.h"
#include "fileops.h"
#include "filter.h"
fc->src = GIT_ITERATOR_TYPE_TREE;
if (!fc->driver &&
- git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
+ git_diff_driver_lookup(&fc->driver, fc->repo,
+ NULL, fc->file->path) < 0)
return -1;
/* give driver a chance to modify options */
fc->file = use_old ? &delta->old_file : &delta->new_file;
fc->src = use_old ? diff->old_src : diff->new_src;
- if (git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
+ if (git_diff_driver_lookup(&fc->driver, fc->repo,
+ &diff->attrsession, fc->file->path) < 0)
return -1;
switch (delta->status) {
memset(fc, 0, sizeof(*fc));
fc->repo = repo;
fc->file = as_file;
- fc->blob = src->blob;
if (!src->blob && !src->buf) {
fc->flags |= GIT_DIFF_FLAG__NO_DATA;
fc->file->mode = GIT_FILEMODE_BLOB;
if (src->blob) {
+ git_blob_dup((git_blob **)&fc->blob, (git_blob *) src->blob);
fc->file->size = git_blob_rawsize(src->blob);
git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
fc->file->id_abbrev = GIT_OID_HEXSZ;
fc->map.len = (size_t)fc->file->size;
fc->map.data = (char *)git_blob_rawcontent(src->blob);
+
+ fc->flags |= GIT_DIFF_FLAG__FREE_BLOB;
} else {
fc->file->size = src->buflen;
git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);
#define INCLUDE_diff_file_h__
#include "common.h"
+
#include "diff.h"
#include "diff_driver.h"
#include "map.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "diff.h"
+
#include "diff_generate.h"
+
+#include "diff.h"
#include "patch_generate.h"
#include "fileops.h"
#include "config.h"
(((DIFF)->base.opts.flags & (FLAG)) == 0)
#define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->base.opts.flags = \
(VAL) ? ((DIFF)->base.opts.flags | (FLAG)) : \
- ((DIFF)->base.opts.flags & ~(VAL))
+ ((DIFF)->base.opts.flags & ~(FLAG))
typedef struct {
struct git_diff base;
{
git_diff_generated *diff = (git_diff_generated *)d;
+ git_attr_session__free(&diff->base.attrsession);
git_vector_free_deep(&diff->base.deltas);
git_pathspec__vfree(&diff->pathspec);
if ((diff = git__calloc(1, sizeof(git_diff_generated))) == NULL)
return NULL;
- GIT_REFCOUNT_INC(diff);
+ GIT_REFCOUNT_INC(&diff->base);
diff->base.type = GIT_DIFF_TYPE_GENERATED;
diff->base.repo = repo;
diff->base.old_src = old_iter->type;
diff->base.new_src = new_iter->type;
diff->base.patch_fn = git_patch_generated_from_diff;
diff->base.free_fn = diff_generated_free;
+ git_attr_session__init(&diff->base.attrsession, repo);
memcpy(&diff->base.opts, &dflt, sizeof(git_diff_options));
git_pool_init(&diff->base.pool, 1);
#ifndef INCLUDE_diff_generate_h__
#define INCLUDE_diff_generate_h__
+#include "common.h"
+
+#include "diff.h"
+#include "pool.h"
+#include "index.h"
+
enum {
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */
}
#endif
-
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "diff.h"
+
#include "diff_parse.h"
+
+#include "diff.h"
#include "patch.h"
#include "patch_parse.h"
if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL)
return NULL;
- GIT_REFCOUNT_INC(diff);
+ GIT_REFCOUNT_INC(&diff->base);
diff->base.type = GIT_DIFF_TYPE_PARSED;
diff->base.strcomp = git__strcmp;
diff->base.strncomp = git__strncmp;
ctx = git_patch_parse_ctx_init(content, content_len, NULL);
GITERR_CHECK_ALLOC(ctx);
- while (ctx->remain_len) {
+ while (ctx->parse_ctx.remain_len) {
if ((error = git_patch_parse(&patch, ctx)) < 0)
break;
#ifndef INCLUDE_diff_parse_h__
#define INCLUDE_diff_parse_h__
+#include "common.h"
+
#include "diff.h"
typedef struct {
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
#include "common.h"
+
#include "diff.h"
#include "diff_file.h"
#include "patch_generate.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
#include "common.h"
+
#include "vector.h"
#include "diff.h"
#include "patch_generate.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
+#include "diff_tform.h"
#include "git2/config.h"
#include "git2/blob.h"
break;
}
if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
- delta->similarity < opts->rename_from_rewrite_threshold)
+ delta->similarity < opts->rename_from_rewrite_threshold) {
+ delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
break;
+ }
return false;
#ifndef INCLUDE_diff_tform_h__
#define INCLUDE_diff_tform_h__
+#include "common.h"
+
+#include "diff_file.h"
+
extern int git_diff_find_similar__hashsig_for_file(
void **out, const git_diff_file *f, const char *path, void *p);
int *score, void *siga, void *sigb, void *payload);
#endif
-
* 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 "diff_xdiff.h"
+
#include "git2/errors.h"
-#include "common.h"
#include "diff.h"
#include "diff_driver.h"
-#include "diff_xdiff.h"
#include "patch_generate.h"
static int git_xdiff_scan_int(const char **str, int *value)
xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+ if (flags & GIT_DIFF_INDENT_HEURISTIC)
+ xo->params.flags |= XDF_INDENT_HEURISTIC;
if (flags & GIT_DIFF_PATIENCE)
xo->params.flags |= XDF_PATIENCE_DIFF;
#ifndef INCLUDE_diff_xdiff_h__
#define INCLUDE_diff_xdiff_h__
+#include "common.h"
+
#include "diff.h"
#include "xdiff/xdiff.h"
#include "patch_generate.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
#include "common.h"
+
#include "global.h"
#include "posix.h"
#include "buffer.h"
--- /dev/null
+#ifndef INCLUDE_features_h__
+#define INCLUDE_features_h__
+
+#cmakedefine GIT_DEBUG_POOL 1
+#cmakedefine GIT_TRACE 1
+#cmakedefine GIT_THREADS 1
+#cmakedefine GIT_MSVC_CRTDBG 1
+
+#cmakedefine GIT_ARCH_64 1
+#cmakedefine GIT_ARCH_32 1
+
+#cmakedefine GIT_USE_ICONV 1
+#cmakedefine GIT_USE_NSEC 1
+#cmakedefine GIT_USE_STAT_MTIM 1
+#cmakedefine GIT_USE_STAT_MTIMESPEC 1
+#cmakedefine GIT_USE_STAT_MTIME_NSEC 1
+#cmakedefine GIT_USE_FUTIMENS 1
+#cmakedefine GIT_USE_REGCOMP_L 1
+
+#cmakedefine GIT_SSH 1
+#cmakedefine GIT_SSH_MEMORY_CREDENTIALS 1
+
+#cmakedefine GIT_GSSAPI 1
+#cmakedefine GIT_WINHTTP 1
+#cmakedefine GIT_CURL 1
+
+#cmakedefine GIT_HTTPS 1
+#cmakedefine GIT_OPENSSL 1
+#cmakedefine GIT_SECURE_TRANSPORT 1
+
+#cmakedefine GIT_SHA1_COLLISIONDETECT 1
+#cmakedefine GIT_SHA1_WIN32 1
+#cmakedefine GIT_SHA1_COMMON_CRYPTO 1
+#cmakedefine GIT_SHA1_OPENSSL 1
+
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "fetch.h"
+
#include "git2/oid.h"
#include "git2/refs.h"
#include "git2/revwalk.h"
#include "git2/transport.h"
-#include "common.h"
#include "remote.h"
#include "refspec.h"
#include "pack.h"
-#include "fetch.h"
#include "netops.h"
#include "repository.h"
#include "refs.h"
#ifndef INCLUDE_fetch_h__
#define INCLUDE_fetch_h__
+#include "common.h"
+
+#include "git2/remote.h"
+
#include "netops.h"
int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts);
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "fetchhead.h"
+
#include "git2/types.h"
#include "git2/oid.h"
-#include "fetchhead.h"
-#include "common.h"
#include "buffer.h"
#include "fileops.h"
#include "filebuf.h"
if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
return -1;
- if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
+ if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_APPEND, GIT_REFS_FILE_MODE) < 0) {
git_buf_free(&path);
return -1;
}
#ifndef INCLUDE_fetchhead_h__
#define INCLUDE_fetchhead_h__
+#include "common.h"
+
+#include "oid.h"
#include "vector.h"
typedef struct git_fetchhead_ref {
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
#include "filebuf.h"
+
#include "fileops.h"
static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
#ifndef INCLUDE_filebuf_h__
#define INCLUDE_filebuf_h__
+#include "common.h"
+
#include "fileops.h"
#include "hash.h"
#include <zlib.h>
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
#include "fileops.h"
+
#include "global.h"
#include "strmap.h"
#include <ctype.h>
return fd;
}
+int git_futils_truncate(const char *path, int mode)
+{
+ int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
+ if (fd < 0)
+ return git_path_set_error(errno, path, "open");
+
+ close(fd);
+ return 0;
+}
+
git_off_t git_futils_filesize(git_file fd)
{
struct stat sb;
#define INCLUDE_fileops_h__
#include "common.h"
+
#include "map.h"
#include "posix.h"
#include "path.h"
*/
extern int git_futils_open_ro(const char *path);
+/**
+ * Truncate a file, creating it if it doesn't exist.
+ */
+extern int git_futils_truncate(const char *path, int mode);
+
/**
* Get the filesize in bytes of a file
*/
*/
extern int git_futils_fsync_parent(const char *path);
-#endif /* INCLUDE_fileops_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "filter.h"
+
#include "common.h"
#include "fileops.h"
#include "hash.h"
-#include "filter.h"
#include "repository.h"
#include "global.h"
#include "git2/sys/filter.h"
#define INCLUDE_filter_h__
#include "common.h"
+
#include "attr_file.h"
#include "git2/filter.h"
* Compares a filename or pathname to a pattern.
*/
+#include "fnmatch.h"
+
#include <ctype.h>
#include <stdio.h>
#include <string.h>
-#include "fnmatch.h"
-
#define EOS '\0'
#define RANGE_MATCH 1
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-#ifndef INCLUDE_fnmatch__compat_h__
-#define INCLUDE_fnmatch__compat_h__
+#ifndef INCLUDE_fnmatch_h__
+#define INCLUDE_fnmatch_h__
#include "common.h"
extern int p_fnmatch(const char *pattern, const char *string, int flags);
-#endif /* _FNMATCH_H */
-
+#endif
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
#include "global.h"
+
#include "hash.h"
#include "sysdir.h"
#include "filter.h"
#include "merge_driver.h"
-#include "openssl_stream.h"
+#include "streams/curl.h"
+#include "streams/openssl.h"
#include "thread-utils.h"
#include "git2/global.h"
#include "transports/ssh.h"
git_mutex git__mwindow_mutex;
-#define MAX_SHUTDOWN_CB 9
+#define MAX_SHUTDOWN_CB 10
static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
static git_atomic git__n_shutdown_callbacks;
(ret = git_filter_global_init()) == 0 &&
(ret = git_merge_driver_global_init()) == 0 &&
(ret = git_transport_ssh_global_init()) == 0 &&
- (ret = git_openssl_stream_global_init()) == 0)
+ (ret = git_openssl_stream_global_init()) == 0 &&
+ (ret = git_curl_stream_global_init()) == 0)
ret = git_mwindow_global_init();
GIT_MEMORY_BARRIER;
#define INCLUDE_global_h__
#include "common.h"
+
#include "mwindow.h"
#include "hash.h"
git_thread *current_thread;
} git_global_st;
-#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
-extern SSL_CTX *git__ssl_ctx;
-#endif
-
git_global_st *git__global_state(void);
extern git_mutex git__mwindow_mutex;
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "common.h"
+
#include "revwalk.h"
#include "merge.h"
#include "git2/graph.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "hash.h"
int git_hash_buf(git_oid *out, const void *data, size_t len)
#ifndef INCLUDE_hash_h__
#define INCLUDE_hash_h__
+#include "common.h"
+
#include "git2/oid.h"
typedef struct git_hash_prov git_hash_prov;
int git_hash_buf(git_oid *out, const void *data, size_t len);
int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n);
-#endif /* INCLUDE_hash_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_collisiondetect_h__
-#define INCLUDE_hash_collisiondetect_h__
+#ifndef INCLUDE_hash_hash_collisiondetect_h__
+#define INCLUDE_hash_hash_collisiondetect_h__
#include "hash.h"
#include "sha1dc/sha1.h"
return 0;
}
-#endif /* INCLUDE_hash_collisiondetect_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_common_crypto_h__
-#define INCLUDE_hash_common_crypto_h__
+#ifndef INCLUDE_hash_hash_common_crypto_h__
+#define INCLUDE_hash_hash_common_crypto_h__
#include "hash.h"
CC_SHA1_CTX c;
};
+#define CC_LONG_MAX ((CC_LONG)-1)
+
#define git_hash_global_init() 0
#define git_hash_ctx_init(ctx) git_hash_init(ctx)
#define git_hash_ctx_cleanup(ctx)
return 0;
}
-GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *_data, size_t len)
{
+ const unsigned char *data = _data;
+
assert(ctx);
- CC_SHA1_Update(&ctx->c, data, len);
+
+ while (len > 0) {
+ CC_LONG chunk = (len > CC_LONG_MAX) ? CC_LONG_MAX : (CC_LONG)len;
+
+ CC_SHA1_Update(&ctx->c, data, chunk);
+
+ data += chunk;
+ len -= chunk;
+ }
+
return 0;
}
return 0;
}
-#endif /* INCLUDE_hash_common_crypto_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "hash_generic.h"
+
#include "hash.h"
-#include "hash/hash_generic.h"
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_generic_h__
-#define INCLUDE_hash_generic_h__
+#ifndef INCLUDE_hash_hash_generic_h__
+#define INCLUDE_hash_hash_generic_h__
+
+#include "common.h"
#include "hash.h"
#define git_hash_ctx_init(ctx) git_hash_init(ctx)
#define git_hash_ctx_cleanup(ctx)
-#endif /* INCLUDE_hash_generic_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_openssl_h__
-#define INCLUDE_hash_openssl_h__
+#ifndef INCLUDE_hash_hash_openssl_h__
+#define INCLUDE_hash_hash_openssl_h__
#include "hash.h"
GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx)
{
assert(ctx);
- SHA1_Init(&ctx->c);
+
+ if (SHA1_Init(&ctx->c) != 1) {
+ giterr_set(GITERR_SHA1, "hash_openssl: failed to initialize hash context");
+ return -1;
+ }
+
return 0;
}
GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
{
assert(ctx);
- SHA1_Update(&ctx->c, data, len);
+
+ if (SHA1_Update(&ctx->c, data, len) != 1) {
+ giterr_set(GITERR_SHA1, "hash_openssl: failed to update hash");
+ return -1;
+ }
+
return 0;
}
GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx)
{
assert(ctx);
- SHA1_Final(out->id, &ctx->c);
+
+ if (SHA1_Final(out->id, &ctx->c) != 1) {
+ giterr_set(GITERR_SHA1, "hash_openssl: failed to finalize hash");
+ return -1;
+ }
+
return 0;
}
-#endif /* INCLUDE_hash_openssl_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "hash_win32.h"
+
#include "global.h"
#include "hash.h"
-#include "hash/hash_win32.h"
#include <wincrypt.h>
#include <strsafe.h>
DWORD dll_path_len, size_len;
/* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
- if (!git_has_win32_version(6, 0, 1))
+ if (!git_has_win32_version(6, 0, 1)) {
+ giterr_set(GITERR_SHA1, "CryptoNG is not supported on this platform");
return -1;
+ }
/* Load bcrypt.dll explicitly from the system directory */
if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
dll_path_len > MAX_PATH ||
StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
- (hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL)
+ (hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL) {
+ giterr_set(GITERR_SHA1, "CryptoNG library could not be loaded");
return -1;
+ }
/* Load the function addresses */
if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL ||
(hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL ||
(hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) {
FreeLibrary(hash_prov.prov.cng.dll);
+
+ giterr_set(GITERR_OS, "CryptoNG functions could not be loaded");
return -1;
}
/* Load the SHA1 algorithm */
if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) {
FreeLibrary(hash_prov.prov.cng.dll);
+
+ giterr_set(GITERR_OS, "algorithm provider could not be initialized");
return -1;
}
if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) {
hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
FreeLibrary(hash_prov.prov.cng.dll);
+
+ giterr_set(GITERR_OS, "algorithm handle could not be found");
return -1;
}
/* Initialize CryptoAPI */
GIT_INLINE(int) hash_cryptoapi_prov_init()
{
- if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ giterr_set(GITERR_OS, "legacy hash context could not be started");
return -1;
+ }
hash_prov.type = CRYPTOAPI;
return 0;
if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
ctx->ctx.cryptoapi.valid = 0;
+ giterr_set(GITERR_OS, "legacy hash implementation could not be created");
return -1;
}
return 0;
}
-GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *data, size_t len)
+GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *_data, size_t len)
{
+ const BYTE *data = (BYTE *)_data;
+
assert(ctx->ctx.cryptoapi.valid);
- if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, (const BYTE *)data, (DWORD)len, 0))
- return -1;
+ while (len > 0) {
+ DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
+
+ if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) {
+ giterr_set(GITERR_OS, "legacy hash data could not be updated");
+ return -1;
+ }
+
+ data += chunk;
+ len -= chunk;
+ }
return 0;
}
assert(ctx->ctx.cryptoapi.valid);
- if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0))
+ if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0)) {
+ giterr_set(GITERR_OS, "legacy hash data could not be finished");
error = -1;
+ }
CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
ctx->ctx.cryptoapi.valid = 0;
if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) {
git__free(ctx->ctx.cng.hash_object);
+
+ giterr_set(GITERR_OS, "hash implementation could not be created");
return -1;
}
return 0;
/* CNG needs to be finished to restart */
- if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0)
+ if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0) {
+ giterr_set(GITERR_OS, "hash implementation could not be finished");
return -1;
+ }
ctx->ctx.cng.updated = 0;
return 0;
}
-GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *data, size_t len)
+GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *_data, size_t len)
{
- if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, (PBYTE)data, (ULONG)len, 0) < 0)
- return -1;
+ PBYTE data = (PBYTE)_data;
+
+ while (len > 0) {
+ ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len;
+
+ if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) {
+ giterr_set(GITERR_OS, "hash could not be updated");
+ return -1;
+ }
+
+ data += chunk;
+ len -= chunk;
+ }
return 0;
}
GIT_INLINE(int) hash_cng_final(git_oid *out, git_hash_ctx *ctx)
{
- if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0)
+ if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0) {
+ giterr_set(GITERR_OS, "hash could not be finished");
return -1;
+ }
ctx->ctx.cng.updated = 0;
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_win32_h__
-#define INCLUDE_hash_win32_h__
+#ifndef INCLUDE_hash_hash_win32_h__
+#define INCLUDE_hash_hash_win32_h__
#include "common.h"
+
#include "hash.h"
#include <wincrypt.h>
} ctx;
};
-#endif /* INCLUDE_hash_openssl_h__ */
+#endif
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
+#include "common.h"
+
#include "git2/sys/hashsig.h"
#include "fileops.h"
#include "util.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "common.h"
+
#include "git2/sys/filter.h"
#include "filter.h"
#include "buffer.h"
#ifndef INCLUDE_idxmap_h__
#define INCLUDE_idxmap_h__
-#include <ctype.h>
#include "common.h"
+
+#include <ctype.h>
#include "git2/index.h"
#define kmalloc git__malloc
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "ignore.h"
+
#include "git2/ignore.h"
#include "common.h"
-#include "ignore.h"
#include "attrcache.h"
#include "path.h"
#include "config.h"
*/
static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
{
+ int (*cmp)(const char *, const char *, size_t);
git_attr_fnmatch *longer, *shorter;
char *p;
- if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
- && (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
-
- /* If lengths match we need to have an exact match */
- if (rule->length == neg->length) {
- return strcmp(rule->pattern, neg->pattern) == 0;
- } else if (rule->length < neg->length) {
- shorter = rule;
- longer = neg;
- } else {
- shorter = neg;
- longer = rule;
- }
-
- /* Otherwise, we need to check if the shorter
- * rule is a basename only (that is, it contains
- * no path separator) and, if so, if it
- * matches the tail of the longer rule */
- p = longer->pattern + longer->length - shorter->length;
+ if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0
+ || (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0)
+ return false;
+
+ if (neg->flags & GIT_ATTR_FNMATCH_ICASE)
+ cmp = git__strncasecmp;
+ else
+ cmp = git__strncmp;
+
+ /* If lengths match we need to have an exact match */
+ if (rule->length == neg->length) {
+ return cmp(rule->pattern, neg->pattern, rule->length) == 0;
+ } else if (rule->length < neg->length) {
+ shorter = rule;
+ longer = neg;
+ } else {
+ shorter = neg;
+ longer = rule;
+ }
- if (p[-1] != '/')
- return false;
- if (memchr(shorter->pattern, '/', shorter->length) != NULL)
- return false;
+ /* Otherwise, we need to check if the shorter
+ * rule is a basename only (that is, it contains
+ * no path separator) and, if so, if it
+ * matches the tail of the longer rule */
+ p = longer->pattern + longer->length - shorter->length;
- return memcmp(p, shorter->pattern, shorter->length) == 0;
- }
+ if (p[-1] != '/')
+ return false;
+ if (memchr(shorter->pattern, '/', shorter->length) != NULL)
+ return false;
- return false;
+ return cmp(p, shorter->pattern, shorter->length) == 0;
}
/**
*/
static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match)
{
- int error = 0;
+ int error = 0, fnflags;
size_t i;
git_attr_fnmatch *rule;
char *path;
*out = 0;
+ fnflags = FNM_PATHNAME;
+ if (match->flags & GIT_ATTR_FNMATCH_ICASE)
+ fnflags |= FNM_IGNORECASE;
+
/* path of the file relative to the workdir, so we match the rules in subdirs */
if (match->containing_dir) {
git_buf_puts(&buf, match->containing_dir);
continue;
}
- /*
- * 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.
- */
+ /*
+ * 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 (error < 0)
goto out;
- if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) {
+ if ((error = p_fnmatch(git_buf_cstr(&buf), path, fnflags)) < 0) {
giterr_set(GITERR_INVALID, "error matching pattern");
goto out;
}
if (ignore_case)
match->flags |= GIT_ATTR_FNMATCH_ICASE;
+ while (match->length > 0) {
+ if (match->pattern[match->length - 1] == ' ' ||
+ match->pattern[match->length - 1] == '\t') {
+ match->pattern[match->length - 1] = 0;
+ match->length --;
+ } else {
+ break;
+ }
+ }
+
scan = git__next_line(scan);
- /* if a negative match doesn't actually do anything, throw it away */
- if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE)
+ /*
+ * If a negative match doesn't actually do anything,
+ * throw it away. As we cannot always verify whether a
+ * rule containing wildcards negates another rule, we
+ * do not optimize away these rules, though.
+ * */
+ if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE
+ && !(match->flags & GIT_ATTR_FNMATCH_HASWILD))
error = does_negate_rule(&valid_rule, &attrs->rules, match);
if (!error && valid_rule)
git_ignores ignores;
unsigned int i;
git_attr_file *file;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
assert(repo && ignored && pathname);
memset(&path, 0, sizeof(path));
memset(&ignores, 0, sizeof(ignores));
- if ((error = git_attr_path__init(&path, pathname, workdir, GIT_DIR_FLAG_UNKNOWN)) < 0 ||
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if ((error = git_attr_path__init(&path, pathname, workdir, dir_flag)) < 0 ||
(error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
goto cleanup;
#ifndef INCLUDE_ignore_h__
#define INCLUDE_ignore_h__
+#include "common.h"
+
#include "repository.h"
#include "vector.h"
#include "attr_file.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "index.h"
+
#include <stddef.h>
-#include "common.h"
#include "repository.h"
-#include "index.h"
#include "tree.h"
#include "tree-cache.h"
#include "hash.h"
return ret;
}
-static bool valid_filemode(const int filemode)
+GIT_INLINE(bool) is_file_or_link(const int filemode)
{
return (filemode == GIT_FILEMODE_BLOB ||
filemode == GIT_FILEMODE_BLOB_EXECUTABLE ||
- filemode == GIT_FILEMODE_LINK ||
- filemode == GIT_FILEMODE_COMMIT);
+ filemode == GIT_FILEMODE_LINK);
+}
+
+GIT_INLINE(bool) valid_filemode(const int filemode)
+{
+ return (is_file_or_link(filemode) || filemode == GIT_FILEMODE_COMMIT);
}
int git_index_add_frombuffer(
"could not initialize index entry. "
"Index is not backed up by an existing repository.");
- if (!valid_filemode(source_entry->mode)) {
+ if (!is_file_or_link(source_entry->mode)) {
giterr_set(GITERR_INDEX, "invalid filemode");
return -1;
}
assert(index && source_entry && source_entry->path);
if (!valid_filemode(source_entry->mode)) {
- giterr_set(GITERR_INDEX, "invalid filemode");
+ giterr_set(GITERR_INDEX, "invalid entry mode");
return -1;
}
}
}
-static size_t read_entry(
+static int read_entry(
git_index_entry **out,
+ size_t *out_size,
git_index *index,
const void *buffer,
size_t buffer_size,
char *tmp_path = NULL;
if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
- return 0;
+ return -1;
/* buffer is not guaranteed to be aligned */
memcpy(&source, buffer, sizeof(struct entry_short));
path_end = memchr(path_ptr, '\0', buffer_size);
if (path_end == NULL)
- return 0;
+ return -1;
path_length = path_end - path_ptr;
}
entry_size = index_entry_size(path_length, 0, entry.flags);
entry.path = (char *)path_ptr;
} else {
- size_t varint_len;
- size_t strip_len = git_decode_varint((const unsigned char *)path_ptr,
- &varint_len);
- size_t last_len = strlen(last);
- size_t prefix_len = last_len - strip_len;
- size_t suffix_len = strlen(path_ptr + varint_len);
- size_t path_len;
-
- if (varint_len == 0)
+ size_t varint_len, last_len, prefix_len, suffix_len, path_len;
+ uintmax_t strip_len;
+
+ strip_len = git_decode_varint((const unsigned char *)path_ptr, &varint_len);
+ last_len = strlen(last);
+
+ if (varint_len == 0 || last_len < strip_len)
return index_error_invalid("incorrect prefix length");
+ prefix_len = last_len - strip_len;
+ suffix_len = strlen(path_ptr + varint_len);
+
GITERR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len);
GITERR_CHECK_ALLOC_ADD(&path_len, path_len, 1);
+
+ if (path_len > GIT_PATH_MAX)
+ return index_error_invalid("unreasonable path length");
+
tmp_path = git__malloc(path_len);
GITERR_CHECK_ALLOC(tmp_path);
entry.path = tmp_path;
}
+ if (entry_size == 0)
+ return -1;
+
if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
- return 0;
+ return -1;
if (index_entry_dup(out, index, &entry) < 0) {
git__free(tmp_path);
- return 0;
+ return -1;
}
git__free(tmp_path);
- return entry_size;
+ *out_size = entry_size;
+ return 0;
}
static int read_header(struct index_header *dest, const void *buffer)
/* Parse all the entries */
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
- git_index_entry *entry;
- size_t entry_size = read_entry(&entry, index, buffer, buffer_size, last);
+ git_index_entry *entry = NULL;
+ size_t entry_size;
- /* 0 bytes read means an object corruption */
- if (entry_size == 0) {
+ if ((error = read_entry(&entry, &entry_size, index, buffer, buffer_size, last)) < 0) {
error = index_error_invalid("invalid entry");
goto done;
}
#ifndef INCLUDE_index_h__
#define INCLUDE_index_h__
+#include "common.h"
+
#include "fileops.h"
#include "filebuf.h"
#include "vector.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "indexer.h"
+
#include "git2/indexer.h"
#include "git2/object.h"
-#include "common.h"
#include "pack.h"
#include "mwindow.h"
#include "posix.h"
return 0;
}
-static void hash_header(git_hash_ctx *ctx, git_off_t len, git_otype type)
+static int hash_header(git_hash_ctx *ctx, git_off_t len, git_otype type)
{
char buffer[64];
size_t hdrlen;
+ int error;
+
+ if ((error = git_odb__format_object_header(&hdrlen,
+ buffer, sizeof(buffer), (size_t)len, type)) < 0)
+ return error;
- hdrlen = git_odb__format_object_header(buffer, sizeof(buffer), (size_t)len, type);
- git_hash_update(ctx, buffer, hdrlen);
+ return git_hash_update(ctx, buffer, hdrlen);
}
static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream)
idx->have_delta = 1;
} else {
idx->have_delta = 0;
- hash_header(&idx->hash_ctx, entry_size, type);
+
+ error = hash_header(&idx->hash_ctx, entry_size, type);
+ if (error < 0)
+ goto on_error;
}
idx->have_stream = 1;
static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
{
unsigned int i;
+ int error;
struct delta_info *delta;
int progressed = 0, non_null = 0, progress_cb_result;
non_null = 1;
idx->off = delta->delta_off;
- if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
- continue;
+ if ((error = git_packfile_unpack(&obj, idx->pack, &idx->off)) < 0) {
+ if (error == GIT_PASSTHROUGH) {
+ /* We have not seen the base object, we'll try again later. */
+ continue;
+ }
+ return -1;
+ }
if (hash_and_save(idx, &obj, delta->delta_off) < 0)
continue;
giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
return -1;
}
+ if (idx->off + 20 > idx->pack->mwf.size) {
+ giterr_set(GITERR_INDEXER, "missing trailer at the end of the pack");
+ return -1;
+ }
packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
if (packfile_trailer == NULL) {
if (idx == NULL)
return;
+ if (idx->have_stream)
+ git_packfile_stream_free(&idx->stream);
+
git_vector_free_deep(&idx->objects);
if (idx->pack->idx_cache) {
#ifndef INCLUDE_indexer_h__
#define INCLUDE_indexer_h__
-extern int git_indexer__set_fsync(git_indexer *idx, int do_fsync);
+#include "common.h"
+
+#include "git2/indexer.h"
+
+extern void git_indexer__set_fsync(git_indexer *idx, int do_fsync);
#endif
#endif
-#endif /* INCLUDE_integer_h__ */
+#endif
*/
#include "iterator.h"
+
#include "tree.h"
#include "index.h"
#define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
#define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES)
#define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT)
+#define iterator__descend_symlinks(I) iterator__flag(I,DESCEND_SYMLINKS)
static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
filesystem_iterator_entry *entry)
{
iter->entry.ctime.seconds = entry->st.st_ctime;
- iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec;
-
iter->entry.mtime.seconds = entry->st.st_mtime;
+
+#if defined(GIT_USE_NSEC)
+ iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec;
iter->entry.mtime.nanoseconds = entry->st.st_mtime_nsec;
+#else
+ iter->entry.ctime.nanoseconds = 0;
+ iter->entry.mtime.nanoseconds = 0;
+#endif
iter->entry.dev = entry->st.st_dev;
iter->entry.ino = entry->st.st_ino;
return 0;
}
+static int filesystem_iterator_is_dir(
+ bool *is_dir,
+ const filesystem_iterator *iter,
+ const filesystem_iterator_entry *entry)
+{
+ struct stat st;
+ git_buf fullpath = GIT_BUF_INIT;
+ int error = 0;
+
+ if (S_ISDIR(entry->st.st_mode)) {
+ *is_dir = 1;
+ goto done;
+ }
+
+ if (!iterator__descend_symlinks(iter) || !S_ISLNK(entry->st.st_mode)) {
+ *is_dir = 0;
+ goto done;
+ }
+
+ if ((error = git_buf_joinpath(&fullpath, iter->root, entry->path)) < 0 ||
+ (error = p_stat(fullpath.ptr, &st)) < 0)
+ goto done;
+
+ *is_dir = S_ISDIR(st.st_mode);
+
+done:
+ git_buf_free(&fullpath);
+ return error;
+}
+
static int filesystem_iterator_advance(
const git_index_entry **out, git_iterator *i)
{
filesystem_iterator *iter = (filesystem_iterator *)i;
+ bool is_dir;
int error = 0;
iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
entry = frame->entries.contents[frame->next_idx];
frame->next_idx++;
- if (S_ISDIR(entry->st.st_mode)) {
+ if ((error = filesystem_iterator_is_dir(&is_dir, iter, entry)) < 0)
+ break;
+
+ if (is_dir) {
if (iterator__do_autoexpand(iter)) {
error = filesystem_iterator_frame_push(iter, entry);
#define INCLUDE_iterator_h__
#include "common.h"
+
#include "git2/index.h"
#include "vector.h"
#include "buffer.h"
GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE = (1u << 5),
/** include conflicts */
GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 6),
+ /** descend into symlinked directories */
+ GIT_ITERATOR_DESCEND_SYMLINKS = (1u << 7),
} git_iterator_flag_t;
typedef enum {
extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
extern int p_munmap(git_map *map);
-#endif /* INCLUDE_map_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "merge.h"
+
#include "posix.h"
#include "buffer.h"
#include "repository.h"
#include "revwalk.h"
#include "commit_list.h"
-#include "merge.h"
#include "path.h"
#include "refs.h"
#include "object.h"
#include "commit.h"
#include "oidarray.h"
#include "merge_driver.h"
+#include "oidmap.h"
+#include "array.h"
#include "git2/types.h"
#include "git2/repository.h"
size_t other_idx;
};
-static int index_entry_similarity_exact(
- git_repository *repo,
- git_index_entry *a,
- size_t a_idx,
- git_index_entry *b,
- size_t b_idx,
- void **cache,
- const git_merge_options *opts)
-{
- GIT_UNUSED(repo);
- GIT_UNUSED(a_idx);
- GIT_UNUSED(b_idx);
- GIT_UNUSED(cache);
- GIT_UNUSED(opts);
-
- if (git_oid__cmp(&a->id, &b->id) == 0)
- return 100;
-
- return 0;
-}
-
static int index_entry_similarity_calc(
void **out,
git_repository *repo,
return score;
}
-static int merge_diff_mark_similarity(
+/* Tracks deletes by oid for merge_diff_mark_similarity_exact(). This is a
+* non-shrinking queue where next_pos is the next position to dequeue.
+*/
+typedef struct {
+ git_array_t(size_t) arr;
+ size_t next_pos;
+ size_t first_entry;
+} deletes_by_oid_queue;
+
+static void deletes_by_oid_free(git_oidmap *map) {
+ deletes_by_oid_queue *queue;
+
+ if (!map)
+ return;
+
+ git_oidmap_foreach_value(map, queue, {
+ git_array_clear(queue->arr);
+ });
+ git_oidmap_free(map);
+}
+
+static int deletes_by_oid_enqueue(git_oidmap *map, git_pool* pool, const git_oid *id, size_t idx) {
+ khint_t pos;
+ deletes_by_oid_queue *queue;
+ size_t *array_entry;
+ int error;
+
+ pos = git_oidmap_lookup_index(map, id);
+ if (!git_oidmap_valid_index(map, pos)) {
+ queue = git_pool_malloc(pool, sizeof(deletes_by_oid_queue));
+ GITERR_CHECK_ALLOC(queue);
+
+ git_array_init(queue->arr);
+ queue->next_pos = 0;
+ queue->first_entry = idx;
+
+ git_oidmap_insert(map, id, queue, &error);
+ if (error < 0)
+ return -1;
+ } else {
+ queue = git_oidmap_value_at(map, pos);
+ array_entry = git_array_alloc(queue->arr);
+ GITERR_CHECK_ALLOC(array_entry);
+ *array_entry = idx;
+ }
+
+ return 0;
+}
+
+static int deletes_by_oid_dequeue(size_t *idx, git_oidmap *map, const git_oid *id) {
+ khint_t pos;
+ deletes_by_oid_queue *queue;
+ size_t *array_entry;
+
+ pos = git_oidmap_lookup_index(map, id);
+
+ if (!git_oidmap_valid_index(map, pos))
+ return GIT_ENOTFOUND;
+
+ queue = git_oidmap_value_at(map, pos);
+
+ if (queue->next_pos == 0) {
+ *idx = queue->first_entry;
+ } else {
+ array_entry = git_array_get(queue->arr, queue->next_pos - 1);
+ if (array_entry == NULL)
+ return GIT_ENOTFOUND;
+
+ *idx = *array_entry;
+ }
+
+ queue->next_pos++;
+ return 0;
+}
+
+static int merge_diff_mark_similarity_exact(
+ git_merge_diff_list *diff_list,
+ struct merge_diff_similarity *similarity_ours,
+ struct merge_diff_similarity *similarity_theirs)
+{
+ size_t i, j;
+ git_merge_diff *conflict_src, *conflict_tgt;
+ git_oidmap *ours_deletes_by_oid = NULL, *theirs_deletes_by_oid = NULL;
+ int error = 0;
+
+ if (!(ours_deletes_by_oid = git_oidmap_alloc()) ||
+ !(theirs_deletes_by_oid = git_oidmap_alloc())) {
+ error = -1;
+ goto done;
+ }
+
+ /* Build a map of object ids to conflicts */
+ git_vector_foreach(&diff_list->conflicts, i, conflict_src) {
+ /* Items can be the source of a rename iff they have an item in the
+ * ancestor slot and lack an item in the ours or theirs slot. */
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry))
+ continue;
+
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
+ error = deletes_by_oid_enqueue(ours_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i);
+ if (error < 0)
+ goto done;
+ }
+
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
+ error = deletes_by_oid_enqueue(theirs_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i);
+ if (error < 0)
+ goto done;
+ }
+ }
+
+ git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) {
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry))
+ continue;
+
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry)) {
+ if (deletes_by_oid_dequeue(&i, ours_deletes_by_oid, &conflict_tgt->our_entry.id) == 0) {
+ similarity_ours[i].similarity = 100;
+ similarity_ours[i].other_idx = j;
+
+ similarity_ours[j].similarity = 100;
+ similarity_ours[j].other_idx = i;
+ }
+ }
+
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry)) {
+ if (deletes_by_oid_dequeue(&i, theirs_deletes_by_oid, &conflict_tgt->their_entry.id) == 0) {
+ similarity_theirs[i].similarity = 100;
+ similarity_theirs[i].other_idx = j;
+
+ similarity_theirs[j].similarity = 100;
+ similarity_theirs[j].other_idx = i;
+ }
+ }
+ }
+
+done:
+ deletes_by_oid_free(ours_deletes_by_oid);
+ deletes_by_oid_free(theirs_deletes_by_oid);
+
+ return error;
+}
+
+static int merge_diff_mark_similarity_inexact(
git_repository *repo,
git_merge_diff_list *diff_list,
struct merge_diff_similarity *similarity_ours,
struct merge_diff_similarity *similarity_theirs,
- int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_options *),
void **cache,
const git_merge_options *opts)
{
if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) &&
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
- similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
+ similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
if (similarity == GIT_EBUFS)
continue;
if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) &&
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
- similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts);
+ similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts);
if (similarity > similarity_theirs[i].similarity &&
similarity > similarity_theirs[j].similarity) {
/* Calculate similarity between items that were deleted from the ancestor
* and added in the other branch.
*/
- if ((error = merge_diff_mark_similarity(repo, diff_list, similarity_ours,
- similarity_theirs, index_entry_similarity_exact, NULL, opts)) < 0)
+ if ((error = merge_diff_mark_similarity_exact(diff_list, similarity_ours, similarity_theirs)) < 0)
goto done;
- if (diff_list->conflicts.length <= opts->target_limit) {
+ if (opts->rename_threshold < 100 && diff_list->conflicts.length <= opts->target_limit) {
GITERR_CHECK_ALLOC_MULTIPLY(&cache_size, diff_list->conflicts.length, 3);
cache = git__calloc(cache_size, sizeof(void *));
GITERR_CHECK_ALLOC(cache);
if (src_count > opts->target_limit || tgt_count > opts->target_limit) {
/* TODO: report! */
} else {
- if ((error = merge_diff_mark_similarity(
- repo, diff_list, similarity_ours, similarity_theirs,
- index_entry_similarity_inexact, cache, opts)) < 0)
+ if ((error = merge_diff_mark_similarity_inexact(
+ repo, diff_list, similarity_ours, similarity_theirs, cache, opts)) < 0)
goto done;
}
}
file_opts.our_label = "Temporary merge branch 1";
file_opts.their_label = "Temporary merge branch 2";
file_opts.flags |= GIT_MERGE_FILE_FAVOR__CONFLICTED;
+ file_opts.marker_size = GIT_MERGE_CONFLICT_MARKER_SIZE + 2;
}
diff_list = git_merge_diff_list__alloc(repo);
git_oidarray bases = {0};
git_annotated_commit *base = NULL, *other = NULL, *new_base = NULL;
git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
- size_t i;
+ size_t i, base_count;
int error;
*out = NULL;
if (given_opts)
memcpy(&opts, given_opts, sizeof(git_merge_options));
- if ((error = insert_head_ids(&head_ids, one)) < 0 ||
- (error = insert_head_ids(&head_ids, two)) < 0)
+ /* With more than two commits, merge_bases_many finds the base of
+ * the first commit and a hypothetical merge of the others. Since
+ * "one" may itself be a virtual commit, which insert_head_ids
+ * substitutes multiple ancestors for, it needs to be added
+ * after "two" which is always a single real commit.
+ */
+ if ((error = insert_head_ids(&head_ids, two)) < 0 ||
+ (error = insert_head_ids(&head_ids, one)) < 0 ||
+ (error = git_merge_bases_many(&bases, repo,
+ head_ids.size, head_ids.ptr)) < 0)
goto done;
- if ((error = git_merge_bases_many(&bases, repo,
- head_ids.size, head_ids.ptr)) < 0 ||
- (error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0 ||
- (opts.flags & GIT_MERGE_NO_RECURSIVE))
+ base_count = (opts.flags & GIT_MERGE_NO_RECURSIVE) ? 0 : bases.count;
+
+ if (base_count)
+ git_oidarray__reverse(&bases);
+
+ if ((error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0)
goto done;
- for (i = 1; i < bases.count; i++) {
+ for (i = 1; i < base_count; i++) {
recursion_level++;
if (opts.recursion_limit && recursion_level > opts.recursion_limit)
#ifndef INCLUDE_merge_h__
#define INCLUDE_merge_h__
+#include "common.h"
+
#include "vector.h"
#include "commit_list.h"
#include "pool.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "merge_driver.h"
+
#include "vector.h"
#include "global.h"
#include "merge.h"
-#include "merge_driver.h"
#include "git2/merge.h"
#include "git2/sys/merge.h"
#ifndef INCLUDE_merge_driver_h__
#define INCLUDE_merge_driver_h__
+#include "common.h"
+
#include "git2/merge.h"
#include "git2/index.h"
#include "git2/sys/merge.h"
*/
#include "common.h"
+
#include "repository.h"
#include "posix.h"
#include "fileops.h"
if (options.flags & GIT_MERGE_FILE_DIFF_MINIMAL)
xmparam.xpp.flags |= XDF_NEED_MINIMAL;
+ xmparam.marker_size = options.marker_size;
+
if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile,
&their_mmfile, &xmparam, &mmbuffer)) < 0) {
giterr_set(GITERR_MERGE, "failed to merge files");
#ifndef INCLUDE_message_h__
#define INCLUDE_message_h__
+#include "common.h"
+
#include "git2/message.h"
#include "buffer.h"
int git_message__prettify(git_buf *message_out, const char *message, int strip_comments);
-#endif /* INCLUDE_message_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "mwindow.h"
+
#include "vector.h"
#include "fileops.h"
#include "map.h"
#ifndef INCLUDE_mwindow__
#define INCLUDE_mwindow__
+#include "common.h"
+
#include "map.h"
#include "vector.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "netops.h"
+
#include <ctype.h>
#include "git2/errors.h"
-#include "common.h"
-#include "netops.h"
#include "posix.h"
#include "buffer.h"
#include "http_parser.h"
git__free(d->pass); d->pass = NULL;
}
-#define hex2c(c) ((c | 32) % 39 - 9)
-static char* unescape(char *str)
-{
- int x, y;
- int len = (int)strlen(str);
-
- for (x=y=0; str[y]; ++x, ++y) {
- if ((str[x] = str[y]) == '%') {
- if (y < len-2 && isxdigit(str[y+1]) && isxdigit(str[y+2])) {
- str[x] = (hex2c(str[y+1]) << 4) + hex2c(str[y+2]);
- y += 2;
- }
- }
- }
- str[x] = '\0';
- return str;
-}
-
int gitno_extract_url_parts(
- char **host,
- char **port,
- char **path,
- char **username,
- char **password,
- const char *url,
- const char *default_port)
+ char **host_out,
+ char **port_out,
+ char **path_out,
+ char **username_out,
+ char **password_out,
+ const char *url,
+ const char *default_port)
{
struct http_parser_url u = {0};
- const char *_host, *_port, *_path, *_userinfo;
+ bool has_host, has_port, has_path, has_userinfo;
+ git_buf host = GIT_BUF_INIT,
+ port = GIT_BUF_INIT,
+ path = GIT_BUF_INIT,
+ username = GIT_BUF_INIT,
+ password = GIT_BUF_INIT;
+ int error = 0;
if (http_parser_parse_url(url, strlen(url), false, &u)) {
giterr_set(GITERR_NET, "malformed URL '%s'", url);
- return GIT_EINVALIDSPEC;
+ error = GIT_EINVALIDSPEC;
+ goto done;
}
- _host = url+u.field_data[UF_HOST].off;
- _port = url+u.field_data[UF_PORT].off;
- _path = url+u.field_data[UF_PATH].off;
- _userinfo = url+u.field_data[UF_USERINFO].off;
+ has_host = !!(u.field_set & (1 << UF_HOST));
+ has_port = !!(u.field_set & (1 << UF_PORT));
+ has_path = !!(u.field_set & (1 << UF_PATH));
+ has_userinfo = !!(u.field_set & (1 << UF_USERINFO));
- if (u.field_set & (1 << UF_HOST)) {
- *host = git__substrdup(_host, u.field_data[UF_HOST].len);
- GITERR_CHECK_ALLOC(*host);
+ if (has_host) {
+ const char *url_host = url + u.field_data[UF_HOST].off;
+ size_t url_host_len = u.field_data[UF_HOST].len;
+ git_buf_decode_percent(&host, url_host, url_host_len);
}
- if (u.field_set & (1 << UF_PORT))
- *port = git__substrdup(_port, u.field_data[UF_PORT].len);
- else
- *port = git__strdup(default_port);
- GITERR_CHECK_ALLOC(*port);
+ if (has_port) {
+ const char *url_port = url + u.field_data[UF_PORT].off;
+ size_t url_port_len = u.field_data[UF_PORT].len;
+ git_buf_put(&port, url_port, url_port_len);
+ } else {
+ git_buf_puts(&port, default_port);
+ }
- if (path) {
- if (u.field_set & (1 << UF_PATH)) {
- *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;
- }
+ if (has_path && path_out) {
+ const char *url_path = url + u.field_data[UF_PATH].off;
+ size_t url_path_len = u.field_data[UF_PATH].len;
+ git_buf_decode_percent(&path, url_path, url_path_len);
+ } else if (path_out) {
+ giterr_set(GITERR_NET, "invalid url, missing path");
+ error = GIT_EINVALIDSPEC;
+ goto done;
}
- if (u.field_set & (1 << UF_USERINFO)) {
- const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
+ if (has_userinfo) {
+ const char *url_userinfo = url + u.field_data[UF_USERINFO].off;
+ size_t url_userinfo_len = u.field_data[UF_USERINFO].len;
+ const char *colon = memchr(url_userinfo, ':', url_userinfo_len);
+
if (colon) {
- *username = unescape(git__substrdup(_userinfo, colon - _userinfo));
- *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
- GITERR_CHECK_ALLOC(*password);
+ const char *url_username = url_userinfo;
+ size_t url_username_len = colon - url_userinfo;
+ const char *url_password = colon + 1;
+ size_t url_password_len = url_userinfo_len - (url_username_len + 1);
+
+ git_buf_decode_percent(&username, url_username, url_username_len);
+ git_buf_decode_percent(&password, url_password, url_password_len);
} else {
- *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
+ git_buf_decode_percent(&username, url_userinfo, url_userinfo_len);
}
- GITERR_CHECK_ALLOC(*username);
-
}
- return 0;
+ if (git_buf_oom(&host) ||
+ git_buf_oom(&port) ||
+ git_buf_oom(&path) ||
+ git_buf_oom(&username) ||
+ git_buf_oom(&password))
+ return -1;
+
+ *host_out = git_buf_detach(&host);
+ *port_out = git_buf_detach(&port);
+ if (path_out)
+ *path_out = git_buf_detach(&path);
+ *username_out = git_buf_detach(&username);
+ *password_out = git_buf_detach(&password);
+
+done:
+ git_buf_free(&host);
+ git_buf_free(&port);
+ git_buf_free(&path);
+ git_buf_free(&username);
+ git_buf_free(&password);
+ return error;
}
#ifndef INCLUDE_netops_h__
#define INCLUDE_netops_h__
-#include "posix.h"
#include "common.h"
+
+#include "posix.h"
#include "stream.h"
#ifdef GIT_OPENSSL
GIT_FILEMODE_BLOB);
}
-static int note_write(git_oid *out,
+static int note_write(
+ git_oid *notes_commit_out,
+ git_oid *notes_blob_out,
git_repository *repo,
const git_signature *author,
const git_signature *committer,
insert_note_in_tree_enotfound_cb)) < 0)
goto cleanup;
- if (out)
- git_oid_cpy(out, &oid);
+ if (notes_blob_out)
+ git_oid_cpy(notes_blob_out, &oid);
+
error = git_commit_create(&oid, repo, notes_ref, author, committer,
NULL, GIT_NOTES_DEFAULT_MSG_ADD,
tree, *parents == NULL ? 0 : 1, (const git_commit **) parents);
+ if (notes_commit_out)
+ git_oid_cpy(notes_commit_out, &oid);
+
cleanup:
git_tree_free(tree);
return error;
return error;
}
-static int note_remove(git_repository *repo,
+static int note_remove(
+ git_oid *notes_commit_out,
+ git_repository *repo,
const git_signature *author, const git_signature *committer,
const char *notes_ref, git_tree *tree,
const char *target, git_commit **parents)
*parents == NULL ? 0 : 1,
(const git_commit **) parents);
+ if (error < 0)
+ goto cleanup;
+
+ if (notes_commit_out)
+ git_oid_cpy(notes_commit_out, &oid);
+
cleanup:
git_tree_free(tree_after_removal);
return error;
return note_get_default_ref(out, repo);
}
-static int retrieve_note_tree_and_commit(
- git_tree **tree_out,
+static int retrieve_note_commit(
git_commit **commit_out,
char **notes_ref_out,
git_repository *repo,
if (git_commit_lookup(commit_out, repo, &oid) < 0)
return error;
- if ((error = git_commit_tree(tree_out, *commit_out)) < 0)
- return error;
-
return 0;
}
+int git_note_commit_read(
+ git_note **out,
+ git_repository *repo,
+ git_commit *notes_commit,
+ const git_oid *oid)
+{
+ int error;
+ git_tree *tree = NULL;
+ char target[GIT_OID_HEXSZ + 1];
+
+ git_oid_tostr(target, sizeof(target), oid);
+
+ if ((error = git_commit_tree(&tree, notes_commit)) < 0)
+ goto cleanup;
+
+ error = note_lookup(out, repo, notes_commit, tree, target);
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
int git_note_read(git_note **out, git_repository *repo,
const char *notes_ref_in, const git_oid *oid)
{
int error;
- char *target = NULL, *notes_ref = NULL;
- git_tree *tree = NULL;
+ char *notes_ref = NULL;
git_commit *commit = NULL;
- target = git_oid_allocfmt(oid);
- GITERR_CHECK_ALLOC(target);
+ error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in);
- if (!(error = retrieve_note_tree_and_commit(
- &tree, &commit, ¬es_ref, repo, notes_ref_in)))
- error = note_lookup(out, repo, commit, tree, target);
+ if (error < 0)
+ goto cleanup;
+ error = git_note_commit_read(out, repo, commit, oid);
+
+cleanup:
git__free(notes_ref);
- git__free(target);
- git_tree_free(tree);
git_commit_free(commit);
return error;
}
+int git_note_commit_create(
+ git_oid *notes_commit_out,
+ git_oid *notes_blob_out,
+ git_repository *repo,
+ git_commit *parent,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid,
+ const char *note,
+ int allow_note_overwrite)
+{
+ int error;
+ git_tree *tree = NULL;
+ char target[GIT_OID_HEXSZ + 1];
+
+ git_oid_tostr(target, sizeof(target), oid);
+
+ if (parent != NULL && (error = git_commit_tree(&tree, parent)) < 0)
+ goto cleanup;
+
+ error = note_write(notes_commit_out, notes_blob_out, repo, author,
+ committer, NULL, note, tree, target, &parent, allow_note_overwrite);
+
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
int git_note_create(
git_oid *out,
git_repository *repo,
int allow_note_overwrite)
{
int error;
- char *target = NULL, *notes_ref = NULL;
- git_commit *commit = NULL;
- git_tree *tree = NULL;
-
- target = git_oid_allocfmt(oid);
- GITERR_CHECK_ALLOC(target);
+ char *notes_ref = NULL;
+ git_commit *existing_notes_commit = NULL;
+ git_reference *ref = NULL;
+ git_oid notes_blob_oid, notes_commit_oid;
- error = retrieve_note_tree_and_commit(&tree, &commit, ¬es_ref, repo, notes_ref_in);
+ error = retrieve_note_commit(&existing_notes_commit, ¬es_ref,
+ repo, notes_ref_in);
if (error < 0 && error != GIT_ENOTFOUND)
goto cleanup;
- error = note_write(out, repo, author, committer, notes_ref,
- note, tree, target, &commit, allow_note_overwrite);
+ error = git_note_commit_create(¬es_commit_oid,
+ ¬es_blob_oid,
+ repo, existing_notes_commit, author,
+ committer, oid, note,
+ allow_note_overwrite);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_reference_create(&ref, repo, notes_ref,
+ ¬es_commit_oid, 1, NULL);
+
+ if (out != NULL)
+ git_oid_cpy(out, ¬es_blob_oid);
cleanup:
git__free(notes_ref);
- git__free(target);
- git_commit_free(commit);
+ git_commit_free(existing_notes_commit);
+ git_reference_free(ref);
+ return error;
+}
+
+int git_note_commit_remove(
+ git_oid *notes_commit_out,
+ git_repository *repo,
+ git_commit *notes_commit,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid)
+{
+ int error;
+ git_tree *tree = NULL;
+ char target[GIT_OID_HEXSZ + 1];
+
+ git_oid_tostr(target, sizeof(target), oid);
+
+ if ((error = git_commit_tree(&tree, notes_commit)) < 0)
+ goto cleanup;
+
+ error = note_remove(notes_commit_out,
+ repo, author, committer, NULL, tree, target, ¬es_commit);
+
+cleanup:
git_tree_free(tree);
return error;
}
const git_oid *oid)
{
int error;
- char *target = NULL, *notes_ref;
- git_commit *commit = NULL;
- git_tree *tree = NULL;
+ char *notes_ref_target = NULL;
+ git_commit *existing_notes_commit = NULL;
+ git_oid new_notes_commit;
+ git_reference *notes_ref = NULL;
- target = git_oid_allocfmt(oid);
- GITERR_CHECK_ALLOC(target);
+ error = retrieve_note_commit(&existing_notes_commit, ¬es_ref_target,
+ repo, notes_ref_in);
- if (!(error = retrieve_note_tree_and_commit(
- &tree, &commit, ¬es_ref, repo, notes_ref_in)))
- error = note_remove(
- repo, author, committer, notes_ref, tree, target, &commit);
+ if (error < 0)
+ goto cleanup;
- git__free(notes_ref);
- git__free(target);
- git_commit_free(commit);
- git_tree_free(tree);
+ error = git_note_commit_remove(&new_notes_commit, repo,
+ existing_notes_commit, author, committer, oid);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_reference_create(¬es_ref, repo, notes_ref_target,
+ &new_notes_commit, 1, NULL);
+
+cleanup:
+ git__free(notes_ref_target);
+ git_reference_free(notes_ref);
+ git_commit_free(existing_notes_commit);
return error;
}
return error;
}
-
void git_note_iterator_free(git_note_iterator *it)
{
if (it == NULL)
git_iterator_free(it);
}
+int git_note_commit_iterator_new(
+ git_note_iterator **it,
+ git_commit *notes_commit)
+{
+ int error;
+ git_tree *tree;
+
+ if ((error = git_commit_tree(&tree, notes_commit)) < 0)
+ goto cleanup;
+
+ if ((error = git_iterator_for_tree(it, tree, NULL)) < 0)
+ git_iterator_free(*it);
+
+cleanup:
+ git_tree_free(tree);
+
+ return error;
+}
int git_note_iterator_new(
git_note_iterator **it,
{
int error;
git_commit *commit = NULL;
- git_tree *tree = NULL;
char *notes_ref;
- error = retrieve_note_tree_and_commit(&tree, &commit, ¬es_ref, repo, notes_ref_in);
+ error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in);
if (error < 0)
goto cleanup;
- if ((error = git_iterator_for_tree(it, tree, NULL)) < 0)
- git_iterator_free(*it);
+ error = git_note_commit_iterator_new(it, commit);
cleanup:
git__free(notes_ref);
- git_tree_free(tree);
git_commit_free(commit);
return error;
char *message;
};
-#endif /* INCLUDE_notes_h__ */
+#endif
* 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 "object.h"
+
#include "git2/object.h"
-#include "common.h"
#include "repository.h"
#include "commit.h"
}
git_otype git_object_string2type(const char *str)
+{
+ if (!str)
+ return GIT_OBJ_BAD;
+
+ return git_object_stringn2type(str, strlen(str));
+}
+
+git_otype git_object_stringn2type(const char *str, size_t len)
{
size_t i;
- if (!str || !*str)
+ if (!str || !len || !*str)
return GIT_OBJ_BAD;
for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
- if (!strcmp(str, git_objects_table[i].str))
+ if (*git_objects_table[i].str &&
+ !git__prefixncmp(str, len, git_objects_table[i].str))
return (git_otype)i;
return GIT_OBJ_BAD;
#ifndef INCLUDE_object_h__
#define INCLUDE_object_h__
+#include "common.h"
+
#include "repository.h"
extern bool git_object__strict_input_validation;
int git_object__resolve_to_type(git_object **obj, git_otype type);
+git_otype git_object_stringn2type(const char *str, size_t len);
+
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
* 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/object.h"
#include "common.h"
-#include "repository.h"
+#include "git2/object.h"
+
+#include "repository.h"
#include "commit.h"
#include "tree.h"
#include "blob.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "odb.h"
+
#include <zlib.h>
#include "git2/object.h"
#include "git2/sys/odb_backend.h"
#include "fileops.h"
#include "hash.h"
-#include "odb.h"
#include "delta.h"
#include "filter.h"
#include "repository.h"
static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id);
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
+static int error_null_oid(int error, const char *message);
static git_otype odb_hardcoded_type(const git_oid *id)
{
return GIT_OBJ_BAD;
}
-static int odb_read_hardcoded(git_rawobj *raw, const git_oid *id)
+static int odb_read_hardcoded(bool *found, git_rawobj *raw, const git_oid *id)
{
- git_otype type = odb_hardcoded_type(id);
- if (type == GIT_OBJ_BAD)
- return -1;
+ git_otype type;
+
+ *found = false;
+
+ if ((type = odb_hardcoded_type(id)) == GIT_OBJ_BAD)
+ return 0;
raw->type = type;
raw->len = 0;
raw->data = git__calloc(1, sizeof(uint8_t));
+ GITERR_CHECK_ALLOC(raw->data);
+
+ *found = true;
return 0;
}
-int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type)
+int git_odb__format_object_header(
+ size_t *written,
+ char *hdr,
+ size_t hdr_size,
+ git_off_t obj_len,
+ git_otype obj_type)
{
const char *type_str = git_object_type2string(obj_type);
- int len = p_snprintf(hdr, n, "%s %lld", type_str, (long long)obj_len);
- assert(len > 0 && len <= (int)n);
- return len+1;
+ int hdr_max = (hdr_size > INT_MAX-2) ? (INT_MAX-2) : (int)hdr_size;
+ int len;
+
+ len = p_snprintf(hdr, hdr_max, "%s %lld", type_str, (long long)obj_len);
+
+ if (len < 0 || len >= hdr_max) {
+ giterr_set(GITERR_OS, "object header creation failed");
+ return -1;
+ }
+
+ *written = (size_t)(len + 1);
+ return 0;
}
int git_odb__hashobj(git_oid *id, git_rawobj *obj)
{
git_buf_vec vec[2];
char header[64];
- int hdrlen;
+ size_t hdrlen;
+ int error;
assert(id && obj);
- if (!git_object_typeisloose(obj->type))
+ if (!git_object_typeisloose(obj->type)) {
+ giterr_set(GITERR_INVALID, "invalid object type");
return -1;
+ }
- if (!obj->data && obj->len != 0)
+ if (!obj->data && obj->len != 0) {
+ giterr_set(GITERR_INVALID, "invalid object");
return -1;
+ }
- hdrlen = git_odb__format_object_header(header, sizeof(header), obj->len, obj->type);
+ if ((error = git_odb__format_object_header(&hdrlen,
+ header, sizeof(header), obj->len, obj->type)) < 0)
+ return error;
vec[0].data = header;
vec[0].len = hdrlen;
vec[1].data = obj->data;
vec[1].len = obj->len;
- git_hash_vec(id, vec, 2);
-
- return 0;
+ return git_hash_vec(id, vec, 2);
}
int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
{
- int hdr_len;
+ size_t hdr_len;
char hdr[64], buffer[FILEIO_BUFSIZE];
git_hash_ctx ctx;
ssize_t read_len = 0;
}
if ((error = git_hash_ctx_init(&ctx)) < 0)
- return -1;
+ return error;
- hdr_len = git_odb__format_object_header(hdr, sizeof(hdr), size, type);
+ if ((error = git_odb__format_object_header(&hdr_len, hdr,
+ sizeof(hdr), size, type)) < 0)
+ goto done;
if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0)
goto done;
{
fake_wstream *stream = (fake_wstream *)_stream;
- if (stream->written + len > stream->size)
- return -1;
+ assert(stream->written + len <= stream->size);
memcpy(stream->buffer + stream->written, data, len);
stream->written += len;
assert(db && id);
+ if (git_oid_iszero(id))
+ return 0;
+
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
git_odb_object_free(object);
return 1;
git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
{
int error;
- git_oid key = {{0}};
+ git_oid key = {{0}};
assert(db && short_id);
assert(db && id && out && len_p && type_p);
+ *out = NULL;
+
+ if (git_oid_iszero(id))
+ return error_null_oid(GIT_ENOTFOUND, "cannot read object");
+
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
*len_p = object->cached.size;
*type_p = object->cached.type;
return 0;
}
- *out = NULL;
error = odb_read_header_1(len_p, type_p, db, id, false);
if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
bool found = false;
int error = 0;
- if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
- found = true;
+ if (!only_refreshed) {
+ if ((error = odb_read_hardcoded(&found, &raw, id)) < 0)
+ return error;
+ }
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
}
giterr_clear();
- if ((object = odb_object__alloc(id, &raw)) == NULL)
+ if ((object = odb_object__alloc(id, &raw)) == NULL) {
+ error = -1;
goto out;
+ }
*out = git_cache_store_raw(odb_cache(db), object);
assert(out && db && id);
+ if (git_oid_iszero(id))
+ return error_null_oid(GIT_ENOTFOUND, "cannot read object");
+
*out = git_cache_get_raw(odb_cache(db), id);
if (*out != NULL)
return 0;
size_t _unused;
int error;
+ if (git_oid_iszero(id))
+ return error_null_oid(GIT_ENOTFOUND, "cannot get object type");
+
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
*type_p = object->cached.type;
return 0;
}
-
+
error = odb_read_header_1(&_unused, type_p, db, id, false);
if (error == GIT_PASSTHROUGH) {
}
}
- if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
+ if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) {
+ error = -1;
goto out;
+ }
*out = git_cache_store_raw(odb_cache(db), object);
assert(oid && db);
git_odb_hash(oid, data, len, type);
+
+ if (git_oid_iszero(oid))
+ return error_null_oid(GIT_EINVALID, "cannot write object");
+
if (git_odb__freshen(db, oid))
return 0;
return error;
}
-static void hash_header(git_hash_ctx *ctx, git_off_t size, git_otype type)
+static int hash_header(git_hash_ctx *ctx, git_off_t size, git_otype type)
{
char header[64];
- int hdrlen;
+ size_t hdrlen;
+ int error;
- hdrlen = git_odb__format_object_header(header, sizeof(header), size, type);
- git_hash_update(ctx, header, hdrlen);
+ if ((error = git_odb__format_object_header(&hdrlen,
+ header, sizeof(header), size, type)) < 0)
+ return error;
+
+ return git_hash_update(ctx, header, hdrlen);
}
int git_odb_open_wstream(
ctx = git__malloc(sizeof(git_hash_ctx));
GITERR_CHECK_ALLOC(ctx);
- if ((error = git_hash_ctx_init(ctx)) < 0)
+ if ((error = git_hash_ctx_init(ctx)) < 0 ||
+ (error = hash_header(ctx, size, type)) < 0)
goto done;
- hash_header(ctx, size, type);
(*stream)->hash_ctx = ctx;
-
(*stream)->declared_size = size;
(*stream)->received_bytes = 0;
done:
+ if (error)
+ git__free(ctx);
return error;
}
stream->free(stream);
}
-int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
+int git_odb_open_rstream(
+ git_odb_stream **stream,
+ size_t *len,
+ git_otype *type,
+ git_odb *db,
+ const git_oid *oid)
{
size_t i, reads = 0;
int error = GIT_ERROR;
if (b->readstream != NULL) {
++reads;
- error = b->readstream(stream, b, oid);
+ error = b->readstream(stream, len, type, b, oid);
}
}
return GIT_ENOTFOUND;
}
+static int error_null_oid(int error, const char *message)
+{
+ giterr_set(GITERR_ODB, "odb: %s: null OID cannot exist", message);
+ return error;
+}
+
int git_odb__error_ambiguous(const char *message)
{
giterr_set(GITERR_ODB, "ambiguous SHA1 prefix - %s", message);
#ifndef INCLUDE_odb_h__
#define INCLUDE_odb_h__
+#include "common.h"
+
#include "git2/odb.h"
#include "git2/oid.h"
#include "git2/types.h"
/*
* Format the object header such as it would appear in the on-disk object
*/
-int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type);
+int git_odb__format_object_header(size_t *out_len, char *hdr, size_t hdr_size, git_off_t obj_len, git_otype obj_type);
/*
* Hash an open file descriptor.
* This is a performance call when the contents of a fd need to be hashed,
*/
#include "common.h"
+
#include <zlib.h>
#include "git2/object.h"
#include "git2/sys/odb_backend.h"
#include "delta.h"
#include "filebuf.h"
#include "object.h"
+#include "zstream.h"
#include "git2/odb_backend.h"
#include "git2/types.h"
+/* maximum possible header length */
+#define MAX_HEADER_LEN 64
+
typedef struct { /* object header data */
git_otype type; /* object type */
size_t size; /* object size */
git_filebuf fbuf;
} loose_writestream;
+typedef struct {
+ git_odb_stream stream;
+ git_map map;
+ char start[MAX_HEADER_LEN];
+ size_t start_len;
+ size_t start_read;
+ git_zstream zstream;
+} loose_readstream;
+
typedef struct loose_backend {
git_odb_backend parent;
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)
+static int parse_header_packlike(
+ obj_hdr *out, size_t *out_len, const unsigned char *data, size_t len)
{
unsigned long c;
- unsigned char *data = (unsigned char *)obj->ptr;
size_t shift, size, used = 0;
- if (git_buf_len(obj) == 0)
- return 0;
+ if (len == 0)
+ goto on_error;
c = data[used++];
- hdr->type = (c >> 4) & 7;
+ out->type = (c >> 4) & 7;
size = c & 15;
shift = 4;
while (c & 0x80) {
- if (git_buf_len(obj) <= used)
- return 0;
+ if (len <= used)
+ goto on_error;
+
if (sizeof(size_t) * 8 <= shift)
- return 0;
+ goto on_error;
+
c = data[used++];
size += (c & 0x7f) << shift;
shift += 7;
}
- hdr->size = size;
-
- return used;
-}
-
-static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
-{
- char c, typename[10];
- size_t size, used = 0;
-
- /*
- * type name string followed by space.
- */
- while ((c = data[used]) != ' ') {
- typename[used++] = c;
- if (used >= sizeof(typename))
- return 0;
- }
- typename[used] = 0;
- if (used == 0)
- return 0;
- hdr->type = git_object_string2type(typename);
- used++; /* consume the space */
-
- /*
- * length follows immediately in decimal (without
- * leading zeros).
- */
- size = data[used++] - '0';
- if (size > 9)
- return 0;
- if (size) {
- while ((c = data[used]) != '\0') {
- size_t d = c - '0';
- if (d > 9)
- break;
- used++;
- size = size * 10 + d;
- }
- }
- hdr->size = size;
-
- /*
- * the length must be followed by a zero byte
- */
- if (data[used++] != '\0')
- return 0;
-
- return used;
-}
-
+ out->size = size;
-/***********************************************************
- *
- * ZLIB RELATED FUNCTIONS
- *
- ***********************************************************/
-
-static void init_stream(z_stream *s, void *out, size_t len)
-{
- memset(s, 0, sizeof(*s));
- s->next_out = out;
- s->avail_out = (uInt)len;
-}
+ if (out_len)
+ *out_len = used;
-static void set_stream_input(z_stream *s, void *in, size_t len)
-{
- s->next_in = in;
- s->avail_in = (uInt)len;
-}
+ return 0;
-static void set_stream_output(z_stream *s, void *out, size_t len)
-{
- s->next_out = out;
- s->avail_out = (uInt)len;
+on_error:
+ giterr_set(GITERR_OBJECT, "failed to parse loose object: invalid header");
+ return -1;
}
-
-static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
+static int parse_header(
+ obj_hdr *out,
+ size_t *out_len,
+ const unsigned char *_data,
+ size_t data_len)
{
- int status;
+ const char *data = (char *)_data;
+ size_t i, typename_len, size_idx, size_len;
+ int64_t size;
- init_stream(s, out, len);
- set_stream_input(s, obj->ptr, git_buf_len(obj));
+ *out_len = 0;
- if ((status = inflateInit(s)) < Z_OK)
- return status;
+ /* find the object type name */
+ for (i = 0, typename_len = 0; i < data_len; i++, typename_len++) {
+ if (data[i] == ' ')
+ break;
+ }
- return inflate(s, 0);
-}
+ if (typename_len == data_len)
+ goto on_error;
-static void abort_inflate(z_stream *s)
-{
- inflateEnd(s);
-}
+ out->type = git_object_stringn2type(data, typename_len);
-static int finish_inflate(z_stream *s)
-{
- int status = Z_OK;
+ size_idx = typename_len + 1;
+ for (i = size_idx, size_len = 0; i < data_len; i++, size_len++) {
+ if (data[i] == '\0')
+ break;
+ }
- while (status == Z_OK)
- status = inflate(s, Z_FINISH);
+ if (i == data_len)
+ goto on_error;
- inflateEnd(s);
+ if (git__strntol64(&size, &data[size_idx], size_len, NULL, 10) < 0 ||
+ size < 0)
+ goto on_error;
- if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
- giterr_set(GITERR_ZLIB, "failed to finish zlib inflation; stream aborted prematurely");
+ if ((uint64_t)size > SIZE_MAX) {
+ giterr_set(GITERR_OBJECT, "object is larger than available memory");
return -1;
}
+ out->size = size;
+
+ if (GIT_ADD_SIZET_OVERFLOW(out_len, i, 1))
+ goto on_error;
+
return 0;
+
+on_error:
+ giterr_set(GITERR_OBJECT, "failed to parse loose object: invalid header");
+ return -1;
}
-static int is_zlib_compressed_data(unsigned char *data)
+static int is_zlib_compressed_data(unsigned char *data, size_t data_len)
{
unsigned int w;
+ if (data_len < 2)
+ return 0;
+
w = ((unsigned int)(data[0]) << 8) + data[1];
return (data[0] & 0x8F) == 0x08 && !(w % 31);
}
-static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
-{
- z_stream zs;
- int status = Z_OK;
-
- memset(&zs, 0x0, sizeof(zs));
-
- zs.next_out = out;
- zs.avail_out = (uInt)outlen;
-
- zs.next_in = in;
- zs.avail_in = (uInt)inlen;
-
- if (inflateInit(&zs) < Z_OK) {
- giterr_set(GITERR_ZLIB, "failed to inflate buffer");
- return -1;
- }
-
- while (status == Z_OK)
- status = inflate(&zs, Z_FINISH);
-
- inflateEnd(&zs);
-
- if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
- zs.total_out != outlen)
- {
- giterr_set(GITERR_ZLIB, "failed to inflate buffer; stream aborted prematurely");
- return -1;
- }
-
- return 0;
-}
-
-static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
-{
- unsigned char *buf, *head = hb;
- size_t tail, alloc_size;
-
- /*
- * allocate a buffer to hold the inflated data and copy the
- * initial sequence of inflated data from the tail of the
- * head buffer, if any.
- */
- if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr->size, 1) ||
- (buf = git__malloc(alloc_size)) == NULL) {
- inflateEnd(s);
- return NULL;
- }
- tail = s->total_out - used;
- if (used > 0 && tail > 0) {
- if (tail > hdr->size)
- tail = hdr->size;
- memcpy(buf, head + used, tail);
- }
- used = tail;
-
- /*
- * inflate the remainder of the object data, if any
- */
- if (hdr->size < used)
- inflateEnd(s);
- else {
- set_stream_output(s, buf + used, hdr->size - used);
- if (finish_inflate(s)) {
- git__free(buf);
- return NULL;
- }
- }
+/***********************************************************
+ *
+ * ODB OBJECT READING & WRITING
+ *
+ * Backend for the public API; read headers and full objects
+ * from the ODB. Write raw data to the ODB.
+ *
+ ***********************************************************/
- return buf;
-}
/*
* At one point, there was a loose object format that was intended to
* of loose object data into packs. This format is no longer used, but
* we must still read it.
*/
-static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
+static int read_loose_packlike(git_rawobj *out, git_buf *obj)
{
- unsigned char *in, *buf;
+ git_buf body = GIT_BUF_INIT;
+ const unsigned char *obj_data;
obj_hdr hdr;
- size_t len, used, alloclen;
+ size_t obj_len, head_len, alloc_size;
+ int error;
+
+ obj_data = (unsigned char *)obj->ptr;
+ obj_len = obj->size;
/*
* read the object header, which is an (uncompressed)
* binary encoding of the object type and size.
*/
- if ((used = get_binary_object_header(&hdr, obj)) == 0 ||
- !git_object_typeisloose(hdr.type)) {
+ if ((error = parse_header_packlike(&hdr, &head_len, obj_data, obj_len)) < 0)
+ goto done;
+
+ if (!git_object_typeisloose(hdr.type) || head_len > obj_len) {
giterr_set(GITERR_ODB, "failed to inflate loose object");
- return -1;
+ error = -1;
+ goto done;
}
+ obj_data += head_len;
+ obj_len -= head_len;
+
/*
* allocate a buffer and inflate the data into it
*/
- GITERR_CHECK_ALLOC_ADD(&alloclen, hdr.size, 1);
- buf = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(buf);
-
- in = ((unsigned char *)obj->ptr) + used;
- len = obj->size - used;
- if (inflate_buffer(in, len, buf, hdr.size) < 0) {
- git__free(buf);
- return -1;
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
+ git_buf_init(&body, alloc_size) < 0) {
+ error = -1;
+ goto done;
}
- buf[hdr.size] = '\0';
- out->data = buf;
+ if ((error = git_zstream_inflatebuf(&body, obj_data, obj_len)) < 0)
+ goto done;
+
out->len = hdr.size;
out->type = hdr.type;
+ out->data = git_buf_detach(&body);
- return 0;
+done:
+ git_buf_free(&body);
+ return error;
}
-static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
+static int read_loose_standard(git_rawobj *out, git_buf *obj)
{
- unsigned char head[64], *buf;
- z_stream zs;
+ git_zstream zstream = GIT_ZSTREAM_INIT;
+ unsigned char head[MAX_HEADER_LEN], *body = NULL;
+ size_t decompressed, head_len, body_len, alloc_size;
obj_hdr hdr;
- size_t used;
+ int error;
- /*
- * check for a pack-like loose object
- */
- if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
- return inflate_packlike_loose_disk_obj(out, obj);
+ if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0 ||
+ (error = git_zstream_set_input(&zstream, git_buf_cstr(obj), git_buf_len(obj))) < 0)
+ goto done;
+
+ decompressed = sizeof(head);
/*
- * inflate the initial part of the io buffer in order
- * to parse the object header (type and size).
+ * inflate the initial part of the compressed buffer in order to
+ * parse the header; read the largest header possible, then push the
+ * remainder into the body buffer.
*/
- if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||
- (used = get_object_header(&hdr, head)) == 0 ||
- !git_object_typeisloose(hdr.type))
- {
- abort_inflate(&zs);
+ if ((error = git_zstream_get_output(head, &decompressed, &zstream)) < 0 ||
+ (error = parse_header(&hdr, &head_len, head, decompressed)) < 0)
+ goto done;
+
+ if (!git_object_typeisloose(hdr.type)) {
giterr_set(GITERR_ODB, "failed to inflate disk object");
- return -1;
+ error = -1;
+ goto done;
}
/*
* allocate a buffer and inflate the object data into it
* (including the initial sequence in the head buffer).
*/
- if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
- return -1;
- buf[hdr.size] = '\0';
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
+ (body = git__malloc(alloc_size)) == NULL) {
+ error = -1;
+ goto done;
+ }
- out->data = buf;
- out->len = hdr.size;
- out->type = hdr.type;
+ assert(decompressed >= head_len);
+ body_len = decompressed - head_len;
- return 0;
-}
+ if (body_len)
+ memcpy(body, head + head_len, body_len);
+ decompressed = hdr.size - body_len;
+ if ((error = git_zstream_get_output(body + body_len, &decompressed, &zstream)) < 0)
+ goto done;
+ if (!git_zstream_done(&zstream)) {
+ giterr_set(GITERR_ZLIB, "failed to finish zlib inflation: stream aborted prematurely");
+ error = -1;
+ goto done;
+ }
+ body[hdr.size] = '\0';
+ out->data = body;
+ out->len = hdr.size;
+ out->type = hdr.type;
+done:
+ if (error < 0)
+ git__free(body);
-/***********************************************************
- *
- * ODB OBJECT READING & WRITING
- *
- * Backend for the public API; read headers and full objects
- * from the ODB. Write raw data to the ODB.
- *
- ***********************************************************/
+ git_zstream_free(&zstream);
+ return error;
+}
static int read_loose(git_rawobj *out, git_buf *loc)
{
out->len = 0;
out->type = GIT_OBJ_BAD;
- if (!(error = git_futils_readbuffer(&obj, loc->ptr)))
- error = inflate_disk_obj(out, &obj);
+ if ((error = git_futils_readbuffer(&obj, loc->ptr)) < 0)
+ goto done;
+ if (!is_zlib_compressed_data((unsigned char *)obj.ptr, obj.size))
+ error = read_loose_packlike(out, &obj);
+ else
+ error = read_loose_standard(out, &obj);
+
+done:
git_buf_free(&obj);
+ return error;
+}
+
+static int read_header_loose_packlike(
+ git_rawobj *out, const unsigned char *data, size_t len)
+{
+ obj_hdr hdr;
+ size_t header_len;
+ int error;
+
+ if ((error = parse_header_packlike(&hdr, &header_len, data, len)) < 0)
+ return error;
+
+ out->len = hdr.size;
+ out->type = hdr.type;
+
+ return error;
+}
+
+static int read_header_loose_standard(
+ git_rawobj *out, const unsigned char *data, size_t len)
+{
+ git_zstream zs = GIT_ZSTREAM_INIT;
+ obj_hdr hdr;
+ unsigned char inflated[MAX_HEADER_LEN];
+ size_t header_len, inflated_len = sizeof(inflated);
+ int error;
+ if ((error = git_zstream_init(&zs, GIT_ZSTREAM_INFLATE)) < 0 ||
+ (error = git_zstream_set_input(&zs, data, len)) < 0 ||
+ (error = git_zstream_get_output_chunk(inflated, &inflated_len, &zs)) < 0 ||
+ (error = parse_header(&hdr, &header_len, inflated, inflated_len)) < 0)
+ goto done;
+
+ out->len = hdr.size;
+ out->type = hdr.type;
+
+done:
+ git_zstream_free(&zs);
return error;
}
static int read_header_loose(git_rawobj *out, git_buf *loc)
{
- int error = 0, z_return = Z_ERRNO, read_bytes;
- git_file fd;
- z_stream zs;
- obj_hdr header_obj;
- unsigned char raw_buffer[16], inflated_buffer[64];
+ unsigned char obj[1024];
+ int fd, obj_len, error;
assert(out && loc);
out->data = NULL;
- if ((fd = git_futils_open_ro(loc->ptr)) < 0)
- return fd;
+ if ((error = fd = git_futils_open_ro(loc->ptr)) < 0 ||
+ (error = obj_len = p_read(fd, obj, sizeof(obj))) < 0)
+ goto done;
- init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
-
- z_return = inflateInit(&zs);
-
- while (z_return == Z_OK) {
- if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
- set_stream_input(&zs, raw_buffer, read_bytes);
- z_return = inflate(&zs, 0);
- } else
- z_return = Z_STREAM_END;
- }
+ if (!is_zlib_compressed_data(obj, (size_t)obj_len))
+ error = read_header_loose_packlike(out, obj, (size_t)obj_len);
+ else
+ error = read_header_loose_standard(out, obj, (size_t)obj_len);
- if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
- || get_object_header(&header_obj, inflated_buffer) == 0
- || git_object_typeisloose(header_obj.type) == 0)
- {
+ if (!error && !git_object_typeisloose(out->type)) {
giterr_set(GITERR_ZLIB, "failed to read loose object header");
error = -1;
- } else {
- out->len = header_obj.size;
- out->type = header_obj.type;
+ goto done;
}
- finish_inflate(&zs);
- p_close(fd);
-
+done:
+ if (fd >= 0)
+ p_close(fd);
return error;
}
return error;
}
-static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid)
+static int loose_backend__writestream_finalize(git_odb_stream *_stream, const git_oid *oid)
{
loose_writestream *stream = (loose_writestream *)_stream;
loose_backend *backend = (loose_backend *)_stream->backend;
return error;
}
-static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
+static int loose_backend__writestream_write(git_odb_stream *_stream, const char *data, size_t len)
{
loose_writestream *stream = (loose_writestream *)_stream;
return git_filebuf_write(&stream->fbuf, data, len);
}
-static void loose_backend__stream_free(git_odb_stream *_stream)
+static void loose_backend__writestream_free(git_odb_stream *_stream)
{
loose_writestream *stream = (loose_writestream *)_stream;
return flags;
}
-static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type)
+static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type)
{
loose_backend *backend;
loose_writestream *stream = NULL;
- char hdr[64];
+ char hdr[MAX_HEADER_LEN];
git_buf tmp_path = GIT_BUF_INIT;
- int hdrlen;
+ size_t hdrlen;
+ int error;
assert(_backend && length >= 0);
backend = (loose_backend *)_backend;
*stream_out = NULL;
- hdrlen = git_odb__format_object_header(hdr, sizeof(hdr), length, type);
+ if ((error = git_odb__format_object_header(&hdrlen,
+ hdr, sizeof(hdr), length, type)) < 0)
+ return error;
stream = git__calloc(1, sizeof(loose_writestream));
GITERR_CHECK_ALLOC(stream);
stream->stream.backend = _backend;
stream->stream.read = NULL; /* read only */
- stream->stream.write = &loose_backend__stream_write;
- stream->stream.finalize_write = &loose_backend__stream_fwrite;
- stream->stream.free = &loose_backend__stream_free;
+ stream->stream.write = &loose_backend__writestream_write;
+ stream->stream.finalize_write = &loose_backend__writestream_finalize;
+ stream->stream.free = &loose_backend__writestream_free;
stream->stream.mode = GIT_STREAM_WRONLY;
if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
return !stream ? -1 : 0;
}
+static int loose_backend__readstream_read(
+ git_odb_stream *_stream,
+ char *buffer,
+ size_t buffer_len)
+{
+ loose_readstream *stream = (loose_readstream *)_stream;
+ size_t start_remain = stream->start_len - stream->start_read;
+ int total = 0, error;
+
+ /*
+ * if we read more than just the header in the initial read, play
+ * that back for the caller.
+ */
+ if (start_remain && buffer_len) {
+ size_t chunk = min(start_remain, buffer_len);
+ memcpy(buffer, stream->start + stream->start_read, chunk);
+
+ buffer += chunk;
+ stream->start_read += chunk;
+
+ total += chunk;
+ buffer_len -= chunk;
+ }
+
+ if (buffer_len) {
+ size_t chunk = min(buffer_len, INT_MAX);
+
+ if ((error = git_zstream_get_output(buffer, &chunk, &stream->zstream)) < 0)
+ return error;
+
+ total += chunk;
+ }
+
+ return total;
+}
+
+static void loose_backend__readstream_free(git_odb_stream *_stream)
+{
+ loose_readstream *stream = (loose_readstream *)_stream;
+
+ git_futils_mmap_free(&stream->map);
+ git_zstream_free(&stream->zstream);
+ git__free(stream);
+}
+
+static int loose_backend__readstream_packlike(
+ obj_hdr *hdr,
+ loose_readstream *stream)
+{
+ const unsigned char *data;
+ size_t data_len, head_len;
+ int error;
+
+ data = stream->map.data;
+ data_len = stream->map.len;
+
+ /*
+ * read the object header, which is an (uncompressed)
+ * binary encoding of the object type and size.
+ */
+ if ((error = parse_header_packlike(hdr, &head_len, data, data_len)) < 0)
+ return error;
+
+ if (!git_object_typeisloose(hdr->type)) {
+ giterr_set(GITERR_ODB, "failed to inflate loose object");
+ return -1;
+ }
+
+ return git_zstream_set_input(&stream->zstream,
+ data + head_len, data_len - head_len);
+}
+
+static int loose_backend__readstream_standard(
+ obj_hdr *hdr,
+ loose_readstream *stream)
+{
+ unsigned char head[MAX_HEADER_LEN];
+ size_t init, head_len;
+ int error;
+
+ if ((error = git_zstream_set_input(&stream->zstream,
+ stream->map.data, stream->map.len)) < 0)
+ return error;
+
+ init = sizeof(head);
+
+ /*
+ * inflate the initial part of the compressed buffer in order to
+ * parse the header; read the largest header possible, then store
+ * it in the `start` field of the stream object.
+ */
+ if ((error = git_zstream_get_output(head, &init, &stream->zstream)) < 0 ||
+ (error = parse_header(hdr, &head_len, head, init)) < 0)
+ return error;
+
+ if (!git_object_typeisloose(hdr->type)) {
+ giterr_set(GITERR_ODB, "failed to inflate disk object");
+ return -1;
+ }
+
+ if (init > head_len) {
+ stream->start_len = init - head_len;
+ memcpy(stream->start, head + head_len, init - head_len);
+ }
+
+ return 0;
+}
+
+static int loose_backend__readstream(
+ git_odb_stream **stream_out,
+ size_t *len_out,
+ git_otype *type_out,
+ git_odb_backend *_backend,
+ const git_oid *oid)
+{
+ loose_backend *backend;
+ loose_readstream *stream = NULL;
+ git_hash_ctx *hash_ctx = NULL;
+ git_buf object_path = GIT_BUF_INIT;
+ obj_hdr hdr;
+ int error = 0;
+
+ assert(stream_out && len_out && type_out && _backend && oid);
+
+ backend = (loose_backend *)_backend;
+ *stream_out = NULL;
+ *len_out = 0;
+ *type_out = GIT_OBJ_BAD;
+
+ if (locate_object(&object_path, backend, oid) < 0) {
+ error = git_odb__error_notfound("no matching loose object",
+ oid, GIT_OID_HEXSZ);
+ goto done;
+ }
+
+ stream = git__calloc(1, sizeof(loose_readstream));
+ GITERR_CHECK_ALLOC(stream);
+
+ hash_ctx = git__malloc(sizeof(git_hash_ctx));
+ GITERR_CHECK_ALLOC(hash_ctx);
+
+ if ((error = git_hash_ctx_init(hash_ctx)) < 0 ||
+ (error = git_futils_mmap_ro_file(&stream->map, object_path.ptr)) < 0 ||
+ (error = git_zstream_init(&stream->zstream, GIT_ZSTREAM_INFLATE)) < 0)
+ goto done;
+
+ /* check for a packlike loose object */
+ if (!is_zlib_compressed_data(stream->map.data, stream->map.len))
+ error = loose_backend__readstream_packlike(&hdr, stream);
+ else
+ error = loose_backend__readstream_standard(&hdr, stream);
+
+ if (error < 0)
+ goto done;
+
+ stream->stream.backend = _backend;
+ stream->stream.hash_ctx = hash_ctx;
+ stream->stream.read = &loose_backend__readstream_read;
+ stream->stream.free = &loose_backend__readstream_free;
+
+ *stream_out = (git_odb_stream *)stream;
+ *len_out = hdr.size;
+ *type_out = hdr.type;
+
+done:
+ if (error < 0) {
+ git_futils_mmap_free(&stream->map);
+ git_zstream_free(&stream->zstream);
+ git_hash_ctx_cleanup(hash_ctx);
+ git__free(hash_ctx);
+ git__free(stream);
+ }
+
+ git_buf_free(&object_path);
+ return error;
+}
+
static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type)
{
- int error = 0, header_len;
+ int error = 0;
git_buf final_path = GIT_BUF_INIT;
- char header[64];
+ char header[MAX_HEADER_LEN];
+ size_t header_len;
git_filebuf fbuf = GIT_FILEBUF_INIT;
loose_backend *backend;
backend = (loose_backend *)_backend;
/* prepare the header for the file */
- header_len = git_odb__format_object_header(header, sizeof(header), len, type);
+ if ((error = git_odb__format_object_header(&header_len,
+ header, sizeof(header), len, type)) < 0)
+ goto cleanup;
if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend),
backend->parent.write = &loose_backend__write;
backend->parent.read_prefix = &loose_backend__read_prefix;
backend->parent.read_header = &loose_backend__read_header;
- backend->parent.writestream = &loose_backend__stream;
+ backend->parent.writestream = &loose_backend__writestream;
+ backend->parent.readstream = &loose_backend__readstream;
backend->parent.exists = &loose_backend__exists;
backend->parent.exists_prefix = &loose_backend__exists_prefix;
backend->parent.foreach = &loose_backend__foreach;
*/
#include "common.h"
+
#include "git2/object.h"
#include "git2/sys/odb_backend.h"
+#include "git2/sys/mempack.h"
#include "fileops.h"
#include "hash.h"
#include "odb.h"
*/
#include "common.h"
+
#include <zlib.h>
#include "git2/repository.h"
#include "git2/indexer.h"
#define INCLUDE_offmap_h__
#include "common.h"
+
#include "git2/types.h"
#define kmalloc git__malloc
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "oid.h"
+
#include "git2/oid.h"
#include "repository.h"
#include "global.h"
#ifndef INCLUDE_oid_h__
#define INCLUDE_oid_h__
+#include "common.h"
+
#include "git2/oid.h"
/**
GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
{
- int i;
-
- for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
- if (*sha1 != *sha2)
- return *sha1 - *sha2;
- }
-
- return 0;
+ return memcmp(sha1, sha2, GIT_OID_RAWSZ);
}
/*
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "git2/oidarray.h"
#include "oidarray.h"
+
+#include "git2/oidarray.h"
#include "array.h"
void git_oidarray_free(git_oidarray *arr)
arr->count = array->size;
arr->ids = array->ptr;
}
+
+void git_oidarray__reverse(git_oidarray *arr)
+{
+ size_t i;
+ git_oid tmp;
+
+ for (i = 0; i < arr->count / 2; i++) {
+ git_oid_cpy(&tmp, &arr->ids[i]);
+ git_oid_cpy(&arr->ids[i], &arr->ids[(arr->count-1)-i]);
+ git_oid_cpy(&arr->ids[(arr->count-1)-i], &tmp);
+ }
+}
#define INCLUDE_oidarray_h__
#include "common.h"
+
#include "git2/oidarray.h"
#include "array.h"
typedef git_array_t(git_oid) git_array_oid_t;
+extern void git_oidarray__reverse(git_oidarray *arr);
extern void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array);
#endif
#define INCLUDE_oidmap_h__
#include "common.h"
+
#include "git2/oid.h"
#define kmalloc git__malloc
+++ /dev/null
-/*
- * 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.
- */
-
-#ifdef GIT_OPENSSL
-
-#include <ctype.h>
-
-#include "global.h"
-#include "posix.h"
-#include "stream.h"
-#include "socket_stream.h"
-#include "openssl_stream.h"
-#include "netops.h"
-#include "git2/transport.h"
-#include "git2/sys/openssl.h"
-
-#ifdef GIT_CURL
-# include "curl_stream.h"
-#endif
-
-#ifndef GIT_WIN32
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-#endif
-
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/x509v3.h>
-#include <openssl/bio.h>
-
-SSL_CTX *git__ssl_ctx;
-
-#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
-
-#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
-
-static git_mutex *openssl_locks;
-
-static void openssl_locking_function(
- int mode, int n, const char *file, int line)
-{
- int lock;
-
- 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[i]);
- git__free(openssl_locks);
-}
-
-#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */
-
-static BIO_METHOD *git_stream_bio_method;
-static int init_bio_method(void);
-
-/**
- * This function aims to clean-up the SSL context which
- * we allocated.
- */
-static void shutdown_ssl(void)
-{
- if (git_stream_bio_method) {
- BIO_meth_free(git_stream_bio_method);
- git_stream_bio_method = NULL;
- }
-
- if (git__ssl_ctx) {
- SSL_CTX_free(git__ssl_ctx);
- git__ssl_ctx = NULL;
- }
-}
-
-int git_openssl_stream_global_init(void)
-{
-#ifdef GIT_OPENSSL
- long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
- const char *ciphers = git_libgit2__ssl_ciphers();
-
- /* Older OpenSSL and MacOS OpenSSL doesn't have this */
-#ifdef SSL_OP_NO_COMPRESSION
- ssl_opts |= SSL_OP_NO_COMPRESSION;
-#endif
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
- SSL_load_error_strings();
- OpenSSL_add_ssl_algorithms();
-#else
- OPENSSL_init_ssl(0, NULL);
-#endif
-
- /*
- * 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;
- return -1;
- }
-
- if (!ciphers) {
- ciphers = GIT_SSL_DEFAULT_CIPHERS;
- }
-
- if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
- SSL_CTX_free(git__ssl_ctx);
- git__ssl_ctx = NULL;
- return -1;
- }
-
- if (init_bio_method() < 0) {
- SSL_CTX_free(git__ssl_ctx);
- git__ssl_ctx = NULL;
- return -1;
- }
-
-#endif
-
- git__on_shutdown(shutdown_ssl);
-
- return 0;
-}
-
-int git_openssl_set_locking(void)
-{
-#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
- 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;
-#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
- return 0;
-#else
- giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
- return -1;
-#endif
-}
-
-
-static int bio_create(BIO *b)
-{
- BIO_set_init(b, 1);
- BIO_set_data(b, NULL);
-
- return 1;
-}
-
-static int bio_destroy(BIO *b)
-{
- if (!b)
- return 0;
-
- BIO_set_data(b, NULL);
-
- return 1;
-}
-
-static int bio_read(BIO *b, char *buf, int len)
-{
- git_stream *io = (git_stream *) BIO_get_data(b);
-
- return (int) git_stream_read(io, buf, len);
-}
-
-static int bio_write(BIO *b, const char *buf, int len)
-{
- git_stream *io = (git_stream *) BIO_get_data(b);
-
- return (int) git_stream_write(io, buf, len, 0);
-}
-
-static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
-{
- GIT_UNUSED(b);
- GIT_UNUSED(num);
- GIT_UNUSED(ptr);
-
- if (cmd == BIO_CTRL_FLUSH)
- return 1;
-
- return 0;
-}
-
-static int bio_gets(BIO *b, char *buf, int len)
-{
- GIT_UNUSED(b);
- GIT_UNUSED(buf);
- GIT_UNUSED(len);
- return -1;
-}
-
-static int bio_puts(BIO *b, const char *str)
-{
- return bio_write(b, str, strlen(str));
-}
-
-static int init_bio_method(void)
-{
- /* Set up the BIO_METHOD we use for wrapping our own stream implementations */
- git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
- GITERR_CHECK_ALLOC(git_stream_bio_method);
-
- BIO_meth_set_write(git_stream_bio_method, bio_write);
- BIO_meth_set_read(git_stream_bio_method, bio_read);
- BIO_meth_set_puts(git_stream_bio_method, bio_puts);
- BIO_meth_set_gets(git_stream_bio_method, bio_gets);
- BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
- BIO_meth_set_create(git_stream_bio_method, bio_create);
- BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);
-
- return 0;
-}
-
-static int ssl_set_error(SSL *ssl, int error)
-{
- int err;
- unsigned long e;
-
- err = SSL_get_error(ssl, error);
-
- assert(err != SSL_ERROR_WANT_READ);
- assert(err != SSL_ERROR_WANT_WRITE);
-
- switch (err) {
- case SSL_ERROR_WANT_CONNECT:
- case SSL_ERROR_WANT_ACCEPT:
- giterr_set(GITERR_NET, "SSL error: connection failure");
- break;
- case SSL_ERROR_WANT_X509_LOOKUP:
- giterr_set(GITERR_NET, "SSL error: x509 error");
- break;
- case SSL_ERROR_SYSCALL:
- e = ERR_get_error();
- if (e > 0) {
- giterr_set(GITERR_NET, "SSL error: %s",
- ERR_error_string(e, NULL));
- break;
- } else if (error < 0) {
- giterr_set(GITERR_OS, "SSL error: syscall failure");
- break;
- }
- giterr_set(GITERR_NET, "SSL error: received early EOF");
- return GIT_EEOF;
- break;
- case SSL_ERROR_SSL:
- e = ERR_get_error();
- giterr_set(GITERR_NET, "SSL error: %s",
- ERR_error_string(e, NULL));
- break;
- case SSL_ERROR_NONE:
- case SSL_ERROR_ZERO_RETURN:
- default:
- giterr_set(GITERR_NET, "SSL error: unknown error");
- break;
- }
- return -1;
-}
-
-static int ssl_teardown(SSL *ssl)
-{
- int ret;
-
- ret = SSL_shutdown(ssl);
- if (ret < 0)
- ret = ssl_set_error(ssl, ret);
- else
- ret = 0;
-
- return ret;
-}
-
-static int check_host_name(const char *name, const char *host)
-{
- if (!strcasecmp(name, host))
- return 0;
-
- if (gitno__match_host(name, host) < 0)
- return -1;
-
- return 0;
-}
-
-static int verify_server_cert(SSL *ssl, const char *host)
-{
- X509 *cert;
- X509_NAME *peer_name;
- ASN1_STRING *str;
- unsigned char *peer_cn = NULL;
- int matched = -1, type = GEN_DNS;
- GENERAL_NAMES *alts;
- struct in6_addr addr6;
- struct in_addr addr4;
- void *addr;
- int i = -1,j;
-
- if (SSL_get_verify_result(ssl) != X509_V_OK) {
- giterr_set(GITERR_SSL, "the SSL certificate is invalid");
- return GIT_ECERTIFICATE;
- }
-
- /* Try to parse the host as an IP address to see if it is */
- if (p_inet_pton(AF_INET, host, &addr4)) {
- type = GEN_IPADD;
- addr = &addr4;
- } else {
- if(p_inet_pton(AF_INET6, host, &addr6)) {
- type = GEN_IPADD;
- addr = &addr6;
- }
- }
-
-
- cert = SSL_get_peer_certificate(ssl);
- if (!cert) {
- giterr_set(GITERR_SSL, "the server did not provide a certificate");
- return -1;
- }
-
- /* Check the alternative names */
- alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
- if (alts) {
- int num;
-
- num = sk_GENERAL_NAME_num(alts);
- for (i = 0; i < num && matched != 1; i++) {
- const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
- const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
- size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
-
- /* Skip any names of a type we're not looking for */
- if (gn->type != type)
- continue;
-
- if (type == GEN_DNS) {
- /* If it contains embedded NULs, don't even try */
- if (memchr(name, '\0', namelen))
- continue;
-
- if (check_host_name(name, host) < 0)
- matched = 0;
- else
- matched = 1;
- } else if (type == GEN_IPADD) {
- /* Here name isn't so much a name but a binary representation of the IP */
- matched = !!memcmp(name, addr, namelen);
- }
- }
- }
- GENERAL_NAMES_free(alts);
-
- if (matched == 0)
- goto cert_fail_name;
-
- if (matched == 1)
- return 0;
-
- /* If no alternative names are available, check the common name */
- peer_name = X509_get_subject_name(cert);
- if (peer_name == NULL)
- goto on_error;
-
- if (peer_name) {
- /* Get the index of the last CN entry */
- while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
- i = j;
- }
-
- if (i < 0)
- goto on_error;
-
- str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
- if (str == NULL)
- goto on_error;
-
- /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
- if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
- int size = ASN1_STRING_length(str);
-
- if (size > 0) {
- peer_cn = OPENSSL_malloc(size + 1);
- GITERR_CHECK_ALLOC(peer_cn);
- memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
- peer_cn[size] = '\0';
- } else {
- goto cert_fail_name;
- }
- } else {
- int size = ASN1_STRING_to_UTF8(&peer_cn, str);
- GITERR_CHECK_ALLOC(peer_cn);
- if (memchr(peer_cn, '\0', size))
- goto cert_fail_name;
- }
-
- if (check_host_name((char *)peer_cn, host) < 0)
- goto cert_fail_name;
-
- OPENSSL_free(peer_cn);
-
- return 0;
-
-on_error:
- OPENSSL_free(peer_cn);
- return ssl_set_error(ssl, 0);
-
-cert_fail_name:
- OPENSSL_free(peer_cn);
- giterr_set(GITERR_SSL, "hostname does not match certificate");
- return GIT_ECERTIFICATE;
-}
-
-typedef struct {
- git_stream parent;
- git_stream *io;
- bool connected;
- char *host;
- SSL *ssl;
- git_cert_x509 cert_info;
-} openssl_stream;
-
-int openssl_close(git_stream *stream);
-
-int openssl_connect(git_stream *stream)
-{
- int ret;
- BIO *bio;
- openssl_stream *st = (openssl_stream *) stream;
-
- if ((ret = git_stream_connect(st->io)) < 0)
- return ret;
-
- st->connected = true;
-
- bio = BIO_new(git_stream_bio_method);
- GITERR_CHECK_ALLOC(bio);
-
- BIO_set_data(bio, st->io);
- SSL_set_bio(st->ssl, bio, bio);
-
- /* specify the host in case SNI is needed */
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- SSL_set_tlsext_host_name(st->ssl, st->host);
-#endif
-
- if ((ret = SSL_connect(st->ssl)) <= 0)
- return ssl_set_error(st->ssl, ret);
-
- return verify_server_cert(st->ssl, st->host);
-}
-
-int openssl_certificate(git_cert **out, git_stream *stream)
-{
- openssl_stream *st = (openssl_stream *) stream;
- int len;
- X509 *cert = SSL_get_peer_certificate(st->ssl);
- unsigned char *guard, *encoded_cert;
-
- /* Retrieve the length of the certificate first */
- len = i2d_X509(cert, NULL);
- if (len < 0) {
- giterr_set(GITERR_NET, "failed to retrieve certificate information");
- return -1;
- }
-
- encoded_cert = git__malloc(len);
- GITERR_CHECK_ALLOC(encoded_cert);
- /* i2d_X509 makes 'guard' point to just after the data */
- guard = encoded_cert;
-
- len = i2d_X509(cert, &guard);
- if (len < 0) {
- git__free(encoded_cert);
- giterr_set(GITERR_NET, "failed to retrieve certificate information");
- return -1;
- }
-
- st->cert_info.parent.cert_type = GIT_CERT_X509;
- st->cert_info.data = encoded_cert;
- st->cert_info.len = len;
-
- *out = &st->cert_info.parent;
-
- return 0;
-}
-
-static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
-{
- openssl_stream *st = (openssl_stream *) stream;
-
- return git_stream_set_proxy(st->io, proxy_opts);
-}
-
-ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
-{
- openssl_stream *st = (openssl_stream *) stream;
- int ret;
-
- GIT_UNUSED(flags);
-
- if ((ret = SSL_write(st->ssl, data, len)) <= 0) {
- return ssl_set_error(st->ssl, ret);
- }
-
- return ret;
-}
-
-ssize_t openssl_read(git_stream *stream, void *data, size_t len)
-{
- openssl_stream *st = (openssl_stream *) stream;
- int ret;
-
- if ((ret = SSL_read(st->ssl, data, len)) <= 0)
- return ssl_set_error(st->ssl, ret);
-
- return ret;
-}
-
-int openssl_close(git_stream *stream)
-{
- openssl_stream *st = (openssl_stream *) stream;
- int ret;
-
- if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
- return -1;
-
- st->connected = false;
-
- return git_stream_close(st->io);
-}
-
-void openssl_free(git_stream *stream)
-{
- openssl_stream *st = (openssl_stream *) stream;
-
- SSL_free(st->ssl);
- git__free(st->host);
- git__free(st->cert_info.data);
- git_stream_free(st->io);
- git__free(st);
-}
-
-int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
-{
- int error;
- openssl_stream *st;
-
- st = git__calloc(1, sizeof(openssl_stream));
- GITERR_CHECK_ALLOC(st);
-
- st->io = NULL;
-#ifdef GIT_CURL
- error = git_curl_stream_new(&st->io, host, port);
-#else
- error = git_socket_stream_new(&st->io, host, port);
-#endif
-
- if (error < 0)
- goto out_err;
-
- st->ssl = SSL_new(git__ssl_ctx);
- if (st->ssl == NULL) {
- giterr_set(GITERR_SSL, "failed to create ssl object");
- error = -1;
- goto out_err;
- }
-
- st->host = git__strdup(host);
- GITERR_CHECK_ALLOC(st->host);
-
- st->parent.version = GIT_STREAM_VERSION;
- st->parent.encrypted = 1;
- st->parent.proxy_support = git_stream_supports_proxy(st->io);
- st->parent.connect = openssl_connect;
- st->parent.certificate = openssl_certificate;
- st->parent.set_proxy = openssl_set_proxy;
- st->parent.read = openssl_read;
- st->parent.write = openssl_write;
- st->parent.close = openssl_close;
- st->parent.free = openssl_free;
-
- *out = (git_stream *) st;
- return 0;
-
-out_err:
- git_stream_free(st->io);
- git__free(st);
-
- return error;
-}
-
-#else
-
-#include "stream.h"
-#include "git2/sys/openssl.h"
-
-int git_openssl_stream_global_init(void)
-{
- return 0;
-}
-
-int git_openssl_set_locking(void)
-{
- giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
- return -1;
-}
-
-int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
-{
- GIT_UNUSED(out);
- GIT_UNUSED(host);
- GIT_UNUSED(port);
-
- giterr_set(GITERR_SSL, "openssl is not supported in this version");
- return -1;
-}
-
-#endif
+++ /dev/null
-/*
- * 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_openssl_stream_h__
-#define INCLUDE_openssl_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_openssl_stream_global_init(void);
-
-extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
-
-/*
- * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
- * which do not exist in previous versions. We define these inline functions so
- * we can program against the interface instead of littering the implementation
- * with ifdefs.
- */
-#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
-# include <openssl/err.h>
-# include <openssl/x509v3.h>
-# include <openssl/bio.h>
-
-
-
-# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-
-GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name)
-{
- BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
- if (!meth) {
- return NULL;
- }
-
- meth->type = type;
- meth->name = name;
-
- return meth;
-}
-
-GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom)
-{
- git__free(biom);
-}
-
-GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
-{
- biom->bwrite = write;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
-{
- biom->bread = read;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
-{
- biom->bputs = puts;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
-
-{
- biom->bgets = gets;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
-{
- biom->ctrl = ctrl;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
-{
- biom->create = create;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
-{
- biom->destroy = destroy;
- return 1;
-}
-
-GIT_INLINE(int) BIO_get_new_index(void)
-{
- /* This exists as of 1.1 so before we'd just have 0 */
- return 0;
-}
-
-GIT_INLINE(void) BIO_set_init(BIO *b, int init)
-{
- b->init = init;
-}
-
-GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr)
-{
- a->ptr = ptr;
-}
-
-GIT_INLINE(void*) BIO_get_data(BIO *a)
-{
- return a->ptr;
-}
-
-GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x)
-{
- return ASN1_STRING_data((ASN1_STRING *)x);
-}
-
-# endif // OpenSSL < 1.1
-#endif // GIT_OPENSSL
-
-#endif
if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0)
return error;
- if (obj->seen)
+ if (obj->seen || obj->uninteresting)
return 0;
obj->seen = 1;
break;
case GIT_OBJ_BLOB:
+ if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0)
+ return error;
+ if (obj->uninteresting)
+ continue;
name = git_tree_entry_name(entry);
if ((error = git_packbuilder_insert(pb, entry_id, name)) < 0)
return error;
int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb);
-#endif /* INCLUDE_pack_objects_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "odb.h"
#include "pack.h"
+
+#include "odb.h"
#include "delta.h"
#include "sha1_lookup.h"
#include "mwindow.h"
error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type);
git_mwindow_close(&w_curs);
- if (error < 0)
+ if (error < 0) {
+ /* We have transferred ownership of the data to the cache. */
+ obj->data = NULL;
break;
+ }
/* the current object becomes the new base, on which we apply the delta */
base = *obj;
if (type == GIT_OBJ_OFS_DELTA) {
unsigned used = 0;
unsigned char c = base_info[used++];
- base_offset = c & 127;
+ size_t unsigned_base_offset = c & 127;
while (c & 128) {
if (left <= used)
return GIT_EBUFS;
- base_offset += 1;
- if (!base_offset || MSB(base_offset, 7))
+ unsigned_base_offset += 1;
+ if (!unsigned_base_offset || MSB(unsigned_base_offset, 7))
return 0; /* overflow */
c = base_info[used++];
- base_offset = (base_offset << 7) + (c & 127);
+ unsigned_base_offset = (unsigned_base_offset << 7) + (c & 127);
}
- base_offset = delta_obj_offset - base_offset;
- if (base_offset <= 0 || base_offset >= delta_obj_offset)
+ if (unsigned_base_offset == 0 || (size_t)delta_obj_offset <= unsigned_base_offset)
return 0; /* out of bound */
+ base_offset = delta_obj_offset - unsigned_base_offset;
*curpos += used;
} else if (type == GIT_OBJ_REF_DELTA) {
/* If we have the cooperative cache, search in it first */
short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
#endif
-#ifdef GIT_USE_LOOKUP
- pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id);
-#else
pos = sha1_position(index, stride, lo, hi, short_oid->id);
-#endif
if (pos >= 0) {
/* An object matching exactly the oid was found */
#ifndef INCLUDE_pack_h__
#define INCLUDE_pack_h__
+#include "common.h"
+
#include <zlib.h>
#include "git2/oid.h"
-#include "common.h"
#include "map.h"
#include "mwindow.h"
#include "odb.h"
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "parse.h"
+
+int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len)
+{
+ if (content_len)
+ ctx->content = content;
+ else
+ ctx->content = NULL;
+
+ ctx->content_len = content_len;
+ ctx->remain = ctx->content;
+ ctx->remain_len = ctx->content_len;
+ ctx->line = ctx->remain;
+ ctx->line_len = git__linenlen(ctx->line, ctx->remain_len);
+ ctx->line_num = 1;
+
+ return 0;
+}
+
+void git_parse_ctx_clear(git_parse_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+void git_parse_advance_line(git_parse_ctx *ctx)
+{
+ ctx->line += ctx->line_len;
+ ctx->remain_len -= ctx->line_len;
+ ctx->line_len = git__linenlen(ctx->line, ctx->remain_len);
+ ctx->line_num++;
+}
+
+void git_parse_advance_chars(git_parse_ctx *ctx, size_t char_cnt)
+{
+ ctx->line += char_cnt;
+ ctx->remain_len -= char_cnt;
+ ctx->line_len -= char_cnt;
+}
+
+int git_parse_advance_expected(
+ git_parse_ctx *ctx,
+ const char *expected,
+ size_t expected_len)
+{
+ if (ctx->line_len < expected_len)
+ return -1;
+
+ if (memcmp(ctx->line, expected, expected_len) != 0)
+ return -1;
+
+ git_parse_advance_chars(ctx, expected_len);
+ return 0;
+}
+
+int git_parse_advance_ws(git_parse_ctx *ctx)
+{
+ int ret = -1;
+
+ while (ctx->line_len > 0 &&
+ ctx->line[0] != '\n' &&
+ git__isspace(ctx->line[0])) {
+ ctx->line++;
+ ctx->line_len--;
+ ctx->remain_len--;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+int git_parse_advance_nl(git_parse_ctx *ctx)
+{
+ if (ctx->line_len != 1 || ctx->line[0] != '\n')
+ return -1;
+
+ git_parse_advance_line(ctx);
+ return 0;
+}
+
+int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base)
+{
+ const char *end;
+ int ret;
+
+ if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))
+ return -1;
+
+ if ((ret = git__strntol64(out, ctx->line, ctx->line_len, &end, base)) < 0)
+ return -1;
+
+ git_parse_advance_chars(ctx, (end - ctx->line));
+ return 0;
+}
+
+int git_parse_peek(char *out, git_parse_ctx *ctx, int flags)
+{
+ size_t remain = ctx->line_len;
+ const char *ptr = ctx->line;
+
+ while (remain) {
+ char c = *ptr;
+
+ if ((flags & GIT_PARSE_PEEK_SKIP_WHITESPACE) &&
+ git__isspace(c)) {
+ remain--;
+ ptr++;
+ continue;
+ }
+
+ *out = c;
+ return 0;
+ }
+
+ return -1;
+}
--- /dev/null
+/*
+ * 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_parse_h__
+#define INCLUDE_parse_h__
+
+#include "common.h"
+
+typedef struct {
+ /* Original content buffer */
+ const char *content;
+ size_t content_len;
+
+ /* The remaining (unparsed) buffer */
+ const char *remain;
+ size_t remain_len;
+
+ const char *line;
+ size_t line_len;
+ size_t line_num;
+} git_parse_ctx;
+
+int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len);
+void git_parse_ctx_clear(git_parse_ctx *ctx);
+
+#define git_parse_err(...) \
+ ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
+
+#define git_parse_ctx_contains_s(ctx, str) \
+ git_parse_ctx_contains(ctx, str, sizeof(str) - 1)
+
+GIT_INLINE(bool) git_parse_ctx_contains(
+ git_parse_ctx *ctx, const char *str, size_t len)
+{
+ return (ctx->line_len >= len && memcmp(ctx->line, str, len) == 0);
+}
+
+void git_parse_advance_line(git_parse_ctx *ctx);
+void git_parse_advance_chars(git_parse_ctx *ctx, size_t char_cnt);
+int git_parse_advance_expected(
+ git_parse_ctx *ctx,
+ const char *expected,
+ size_t expected_len);
+
+#define git_parse_advance_expected_str(ctx, str) \
+ git_parse_advance_expected(ctx, str, strlen(str))
+
+int git_parse_advance_ws(git_parse_ctx *ctx);
+int git_parse_advance_nl(git_parse_ctx *ctx);
+int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base);
+
+enum GIT_PARSE_PEEK_FLAGS {
+ GIT_PARSE_PEEK_SKIP_WHITESPACE = (1 << 0)
+};
+
+int git_parse_peek(char *out, git_parse_ctx *ctx, int flags);
+
+#endif
-#include "git2/patch.h"
-#include "diff.h"
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+
#include "patch.h"
+#include "git2/patch.h"
+#include "diff.h"
int git_patch__invoke_callbacks(
git_patch *patch,
#ifndef INCLUDE_patch_h__
#define INCLUDE_patch_h__
+#include "common.h"
+
#include "git2/patch.h"
#include "array.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
+#include "patch_generate.h"
+
#include "git2/blob.h"
#include "diff.h"
#include "diff_generate.h"
#include "diff_file.h"
#include "diff_driver.h"
-#include "patch_generate.h"
#include "diff_xdiff.h"
#include "delta.h"
#include "zstream.h"
if (!(error = patch_generated_init(patch, diff, delta_index))) {
patch->flags |= GIT_PATCH_GENERATED_ALLOCATED;
- GIT_REFCOUNT_INC(patch);
+ GIT_REFCOUNT_INC(&patch->base);
} else {
git__free(patch);
patch = NULL;
git_patch **out,
const git_blob *old_blob,
const char *old_path,
- const char *buf,
+ const void *buf,
size_t buflen,
const char *buf_path,
const git_diff_options *opts)
const void *old_buf,
size_t old_len,
const char *old_path,
- const char *new_buf,
+ const void *new_buf,
size_t new_len,
const char *new_path,
const git_diff_options *opts)
#define INCLUDE_patch_generate_h__
#include "common.h"
+
#include "diff.h"
#include "diff_file.h"
#include "patch.h"
* 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 "patch_parse.h"
+
#include "git2/patch.h"
#include "patch.h"
-#include "patch_parse.h"
#include "diff_parse.h"
#include "path.h"
-#define parse_err(...) \
- ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
-
typedef struct {
git_patch base;
char *old_prefix, *new_prefix;
} git_patch_parsed;
-
-GIT_INLINE(bool) parse_ctx_contains(
- git_patch_parse_ctx *ctx, const char *str, size_t len)
-{
- return (ctx->line_len >= len && memcmp(ctx->line, str, len) == 0);
-}
-
-#define parse_ctx_contains_s(ctx, str) \
- parse_ctx_contains(ctx, str, sizeof(str) - 1)
-
-static void parse_advance_line(git_patch_parse_ctx *ctx)
-{
- ctx->line += ctx->line_len;
- ctx->remain_len -= ctx->line_len;
- ctx->line_len = git__linenlen(ctx->line, ctx->remain_len);
- ctx->line_num++;
-}
-
-static void parse_advance_chars(git_patch_parse_ctx *ctx, size_t char_cnt)
-{
- ctx->line += char_cnt;
- ctx->remain_len -= char_cnt;
- ctx->line_len -= char_cnt;
-}
-
-static int parse_advance_expected(
- git_patch_parse_ctx *ctx,
- const char *expected,
- size_t expected_len)
-{
- if (ctx->line_len < expected_len)
- return -1;
-
- if (memcmp(ctx->line, expected, expected_len) != 0)
- return -1;
-
- parse_advance_chars(ctx, expected_len);
- return 0;
-}
-
-#define parse_advance_expected_str(ctx, str) \
- parse_advance_expected(ctx, str, strlen(str))
-
-static int parse_advance_ws(git_patch_parse_ctx *ctx)
-{
- int ret = -1;
-
- while (ctx->line_len > 0 &&
- ctx->line[0] != '\n' &&
- git__isspace(ctx->line[0])) {
- ctx->line++;
- ctx->line_len--;
- ctx->remain_len--;
- ret = 0;
- }
-
- return ret;
-}
-
-static int parse_advance_nl(git_patch_parse_ctx *ctx)
-{
- if (ctx->line_len != 1 || ctx->line[0] != '\n')
- return -1;
-
- parse_advance_line(ctx);
- return 0;
-}
-
static int header_path_len(git_patch_parse_ctx *ctx)
{
bool inquote = 0;
- bool quoted = (ctx->line_len > 0 && ctx->line[0] == '"');
+ bool quoted = git_parse_ctx_contains_s(&ctx->parse_ctx, "\"");
size_t len;
- for (len = quoted; len < ctx->line_len; len++) {
- if (!quoted && git__isspace(ctx->line[len]))
+ for (len = quoted; len < ctx->parse_ctx.line_len; len++) {
+ if (!quoted && git__isspace(ctx->parse_ctx.line[len]))
break;
- else if (quoted && !inquote && ctx->line[len] == '"') {
+ else if (quoted && !inquote && ctx->parse_ctx.line[len] == '"') {
len++;
break;
}
- inquote = (!inquote && ctx->line[len] == '\\');
+ inquote = (!inquote && ctx->parse_ctx.line[len] == '\\');
}
return len;
}
-static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx)
+static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx, size_t path_len)
{
- int path_len, error = 0;
-
- path_len = header_path_len(ctx);
+ int error;
- if ((error = git_buf_put(path, ctx->line, path_len)) < 0)
+ if ((error = git_buf_put(path, ctx->parse_ctx.line, path_len)) < 0)
goto done;
- parse_advance_chars(ctx, path_len);
+ git_parse_advance_chars(&ctx->parse_ctx, path_len);
git_buf_rtrim(path);
static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
{
git_buf path = GIT_BUF_INIT;
- int error = parse_header_path_buf(&path, ctx);
+ int error = parse_header_path_buf(&path, ctx, header_path_len(ctx));
*out = git_buf_detach(&path);
static int parse_header_git_oldpath(
git_patch_parsed *patch, git_patch_parse_ctx *ctx)
{
- return parse_header_path(&patch->old_path, ctx);
+ git_buf old_path = GIT_BUF_INIT;
+ int error;
+
+ if ((error = parse_header_path_buf(&old_path, ctx, ctx->parse_ctx.line_len - 1)) < 0)
+ goto out;
+
+ patch->old_path = git_buf_detach(&old_path);
+
+out:
+ git_buf_free(&old_path);
+ return error;
}
static int parse_header_git_newpath(
git_patch_parsed *patch, git_patch_parse_ctx *ctx)
{
- return parse_header_path(&patch->new_path, ctx);
+ git_buf new_path = GIT_BUF_INIT;
+ int error;
+
+ if ((error = parse_header_path_buf(&new_path, ctx, ctx->parse_ctx.line_len - 1)) < 0)
+ goto out;
+
+ patch->new_path = git_buf_detach(&new_path);
+
+out:
+ git_buf_free(&new_path);
+ return error;
}
static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx)
{
- const char *end;
- int32_t m;
- int ret;
-
- if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))
- return parse_err("invalid file mode at line %"PRIuZ, ctx->line_num);
+ int64_t m;
- if ((ret = git__strntol32(&m, ctx->line, ctx->line_len, &end, 8)) < 0)
- return ret;
+ if ((git_parse_advance_digit(&m, &ctx->parse_ctx, 8)) < 0)
+ return git_parse_err("invalid file mode at line %"PRIuZ, ctx->parse_ctx.line_num);
if (m > UINT16_MAX)
return -1;
*mode = (uint16_t)m;
- parse_advance_chars(ctx, (end - ctx->line));
-
- return ret;
+ return 0;
}
static int parse_header_oid(
{
size_t len;
- for (len = 0; len < ctx->line_len && len < GIT_OID_HEXSZ; len++) {
- if (!git__isxdigit(ctx->line[len]))
+ for (len = 0; len < ctx->parse_ctx.line_len && len < GIT_OID_HEXSZ; len++) {
+ if (!git__isxdigit(ctx->parse_ctx.line[len]))
break;
}
if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ ||
- git_oid_fromstrn(oid, ctx->line, len) < 0)
- return parse_err("invalid hex formatted object id at line %"PRIuZ,
- ctx->line_num);
+ git_oid_fromstrn(oid, ctx->parse_ctx.line, len) < 0)
+ return git_parse_err("invalid hex formatted object id at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
- parse_advance_chars(ctx, len);
+ git_parse_advance_chars(&ctx->parse_ctx, len);
*oid_len = (uint16_t)len;
static int parse_header_git_index(
git_patch_parsed *patch, git_patch_parse_ctx *ctx)
{
+ char c;
+
if (parse_header_oid(&patch->base.delta->old_file.id,
&patch->base.delta->old_file.id_abbrev, ctx) < 0 ||
- parse_advance_expected_str(ctx, "..") < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, "..") < 0 ||
parse_header_oid(&patch->base.delta->new_file.id,
&patch->base.delta->new_file.id_abbrev, ctx) < 0)
return -1;
- if (ctx->line_len > 0 && ctx->line[0] == ' ') {
+ if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ' ') {
uint16_t mode;
- parse_advance_chars(ctx, 1);
+ git_parse_advance_chars(&ctx->parse_ctx, 1);
if (parse_header_mode(&mode, ctx) < 0)
return -1;
{
git_buf path = GIT_BUF_INIT;
- if (parse_header_path_buf(&path, ctx) < 0)
+ if (parse_header_path_buf(&path, ctx, header_path_len(ctx)) < 0)
return -1;
/* Note: the `rename from` and `rename to` lines include the literal
static int parse_header_percent(uint16_t *out, git_patch_parse_ctx *ctx)
{
- int32_t val;
- const char *end;
+ int64_t val;
- if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]) ||
- git__strntol32(&val, ctx->line, ctx->line_len, &end, 10) < 0)
+ if (git_parse_advance_digit(&val, &ctx->parse_ctx, 10) < 0)
return -1;
- parse_advance_chars(ctx, (end - ctx->line));
-
- if (parse_advance_expected_str(ctx, "%") < 0)
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "%") < 0)
return -1;
- if (val > 100)
+ if (val < 0 || val > 100)
return -1;
*out = val;
git_patch_parsed *patch, git_patch_parse_ctx *ctx)
{
if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0)
- return parse_err("invalid similarity percentage at line %"PRIuZ,
- ctx->line_num);
+ return git_parse_err("invalid similarity percentage at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
return 0;
}
uint16_t dissimilarity;
if (parse_header_percent(&dissimilarity, ctx) < 0)
- return parse_err("invalid similarity percentage at line %"PRIuZ,
- ctx->line_num);
+ return git_parse_err("invalid similarity percentage at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
patch->base.delta->similarity = 100 - dissimilarity;
return 0;
}
+static int parse_header_start(git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ if (parse_header_path(&patch->header_old_path, ctx) < 0)
+ return git_parse_err("corrupt old path in git diff header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ if (git_parse_advance_ws(&ctx->parse_ctx) < 0 ||
+ parse_header_path(&patch->header_new_path, ctx) < 0)
+ return git_parse_err("corrupt new path in git diff header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ /*
+ * We cannot expect to be able to always parse paths correctly at this
+ * point. Due to the possibility of unquoted names, whitespaces in
+ * filenames and custom prefixes we have to allow that, though, and just
+ * proceeed here. We then hope for the "---" and "+++" lines to fix that
+ * for us.
+ */
+ if (!git_parse_ctx_contains(&ctx->parse_ctx, "\n", 1)) {
+ git_parse_advance_chars(&ctx->parse_ctx, ctx->parse_ctx.line_len - 1);
+
+ git__free(patch->header_old_path);
+ patch->header_old_path = NULL;
+ git__free(patch->header_new_path);
+ patch->header_new_path = NULL;
+ }
+
+ return 0;
+}
+
+typedef enum {
+ STATE_START,
+
+ STATE_DIFF,
+ STATE_FILEMODE,
+ STATE_MODE,
+ STATE_INDEX,
+ STATE_PATH,
+
+ STATE_SIMILARITY,
+ STATE_RENAME,
+ STATE_COPY,
+
+ STATE_END,
+} parse_header_state;
+
typedef struct {
const char *str;
+ parse_header_state expected_state;
+ parse_header_state next_state;
int(*fn)(git_patch_parsed *, git_patch_parse_ctx *);
-} header_git_op;
-
-static const header_git_op header_git_ops[] = {
- { "diff --git ", NULL },
- { "@@ -", NULL },
- { "GIT binary patch", NULL },
- { "Binary files ", NULL },
- { "--- ", parse_header_git_oldpath },
- { "+++ ", parse_header_git_newpath },
- { "index ", parse_header_git_index },
- { "old mode ", parse_header_git_oldmode },
- { "new mode ", parse_header_git_newmode },
- { "deleted file mode ", parse_header_git_deletedfilemode },
- { "new file mode ", parse_header_git_newfilemode },
- { "rename from ", parse_header_renamefrom },
- { "rename to ", parse_header_renameto },
- { "rename old ", parse_header_renamefrom },
- { "rename new ", parse_header_renameto },
- { "copy from ", parse_header_copyfrom },
- { "copy to ", parse_header_copyto },
- { "similarity index ", parse_header_similarity },
- { "dissimilarity index ", parse_header_dissimilarity },
+} parse_header_transition;
+
+static const parse_header_transition transitions[] = {
+ /* Start */
+ { "diff --git " , STATE_START, STATE_DIFF, parse_header_start },
+
+ { "deleted file mode " , STATE_DIFF, STATE_FILEMODE, parse_header_git_deletedfilemode },
+ { "new file mode " , STATE_DIFF, STATE_FILEMODE, parse_header_git_newfilemode },
+ { "old mode " , STATE_DIFF, STATE_MODE, parse_header_git_oldmode },
+ { "new mode " , STATE_MODE, STATE_END, parse_header_git_newmode },
+
+ { "index " , STATE_FILEMODE, STATE_INDEX, parse_header_git_index },
+ { "index " , STATE_DIFF, STATE_INDEX, parse_header_git_index },
+ { "index " , STATE_END, STATE_INDEX, parse_header_git_index },
+
+ { "--- " , STATE_INDEX, STATE_PATH, parse_header_git_oldpath },
+ { "+++ " , STATE_PATH, STATE_END, parse_header_git_newpath },
+ { "GIT binary patch" , STATE_INDEX, STATE_END, NULL },
+ { "Binary files " , STATE_INDEX, STATE_END, NULL },
+
+ { "similarity index " , STATE_DIFF, STATE_SIMILARITY, parse_header_similarity },
+ { "dissimilarity index ", STATE_DIFF, STATE_SIMILARITY, parse_header_dissimilarity },
+ { "rename from " , STATE_SIMILARITY, STATE_RENAME, parse_header_renamefrom },
+ { "rename old " , STATE_SIMILARITY, STATE_RENAME, parse_header_renamefrom },
+ { "copy from " , STATE_SIMILARITY, STATE_COPY, parse_header_copyfrom },
+ { "rename to " , STATE_RENAME, STATE_END, parse_header_renameto },
+ { "rename new " , STATE_RENAME, STATE_END, parse_header_renameto },
+ { "copy to " , STATE_COPY, STATE_END, parse_header_copyto },
+
+ /* Next patch */
+ { "diff --git " , STATE_END, 0, NULL },
+ { "@@ -" , STATE_END, 0, NULL },
+ { "-- " , STATE_END, 0, NULL },
};
static int parse_header_git(
{
size_t i;
int error = 0;
-
- /* Parse the diff --git line */
- if (parse_advance_expected_str(ctx, "diff --git ") < 0)
- return parse_err("corrupt git diff header at line %"PRIuZ, ctx->line_num);
-
- if (parse_header_path(&patch->header_old_path, ctx) < 0)
- return parse_err("corrupt old path in git diff header at line %"PRIuZ,
- ctx->line_num);
-
- if (parse_advance_ws(ctx) < 0 ||
- parse_header_path(&patch->header_new_path, ctx) < 0)
- return parse_err("corrupt new path in git diff header at line %"PRIuZ,
- ctx->line_num);
+ parse_header_state state = STATE_START;
/* Parse remaining header lines */
- for (parse_advance_line(ctx);
- ctx->remain_len > 0;
- parse_advance_line(ctx)) {
-
+ for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) {
bool found = false;
- if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n')
+ if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n')
break;
- for (i = 0; i < ARRAY_SIZE(header_git_ops); i++) {
- const header_git_op *op = &header_git_ops[i];
- size_t op_len = strlen(op->str);
+ for (i = 0; i < ARRAY_SIZE(transitions); i++) {
+ const parse_header_transition *transition = &transitions[i];
+ size_t op_len = strlen(transition->str);
- if (memcmp(ctx->line, op->str, min(op_len, ctx->line_len)) != 0)
+ if (transition->expected_state != state ||
+ git__prefixcmp(ctx->parse_ctx.line, transition->str) != 0)
continue;
+ state = transition->next_state;
+
/* Do not advance if this is the patch separator */
- if (op->fn == NULL)
+ if (transition->fn == NULL)
goto done;
- parse_advance_chars(ctx, op_len);
+ git_parse_advance_chars(&ctx->parse_ctx, op_len);
- if ((error = op->fn(patch, ctx)) < 0)
+ if ((error = transition->fn(patch, ctx)) < 0)
goto done;
- parse_advance_ws(ctx);
+ git_parse_advance_ws(&ctx->parse_ctx);
- if (parse_advance_expected_str(ctx, "\n") < 0 ||
- ctx->line_len > 0) {
- error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "\n") < 0 ||
+ ctx->parse_ctx.line_len > 0) {
+ error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
}
found = true;
break;
}
-
+
if (!found) {
- error = parse_err("invalid patch header at line %"PRIuZ,
- ctx->line_num);
+ error = git_parse_err("invalid patch header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
goto done;
}
}
+ if (state != STATE_END) {
+ error = git_parse_err("unexpected header line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
done:
return error;
}
const char *end;
int64_t num;
- if (!git__isdigit(ctx->line[0]))
+ if (!git__isdigit(ctx->parse_ctx.line[0]))
return -1;
- if (git__strntol64(&num, ctx->line, ctx->line_len, &end, 10) < 0)
+ if (git__strntol64(&num, ctx->parse_ctx.line, ctx->parse_ctx.line_len, &end, 10) < 0)
return -1;
if (num < 0)
return -1;
*out = num;
- parse_advance_chars(ctx, (end - ctx->line));
+ git_parse_advance_chars(&ctx->parse_ctx, (end - ctx->parse_ctx.line));
return 0;
}
{
git_off_t num;
- if (parse_number(&num, ctx) < 0 || !git__is_int(num))
+ if (git_parse_advance_digit(&num, &ctx->parse_ctx, 10) < 0 || !git__is_int(num))
return -1;
*out = (int)num;
git_patch_hunk *hunk,
git_patch_parse_ctx *ctx)
{
- const char *header_start = ctx->line;
+ const char *header_start = ctx->parse_ctx.line;
+ char c;
hunk->hunk.old_lines = 1;
hunk->hunk.new_lines = 1;
- if (parse_advance_expected_str(ctx, "@@ -") < 0 ||
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "@@ -") < 0 ||
parse_int(&hunk->hunk.old_start, ctx) < 0)
goto fail;
- if (ctx->line_len > 0 && ctx->line[0] == ',') {
- if (parse_advance_expected_str(ctx, ",") < 0 ||
+ if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') {
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 ||
parse_int(&hunk->hunk.old_lines, ctx) < 0)
goto fail;
}
- if (parse_advance_expected_str(ctx, " +") < 0 ||
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, " +") < 0 ||
parse_int(&hunk->hunk.new_start, ctx) < 0)
goto fail;
- if (ctx->line_len > 0 && ctx->line[0] == ',') {
- if (parse_advance_expected_str(ctx, ",") < 0 ||
+ if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') {
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 ||
parse_int(&hunk->hunk.new_lines, ctx) < 0)
goto fail;
}
- if (parse_advance_expected_str(ctx, " @@") < 0)
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, " @@") < 0)
goto fail;
- parse_advance_line(ctx);
+ git_parse_advance_line(&ctx->parse_ctx);
if (!hunk->hunk.old_lines && !hunk->hunk.new_lines)
goto fail;
- hunk->hunk.header_len = ctx->line - header_start;
+ hunk->hunk.header_len = ctx->parse_ctx.line - header_start;
if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))
- return parse_err("oversized patch hunk header at line %"PRIuZ,
- ctx->line_num);
+ return git_parse_err("oversized patch hunk header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len);
hunk->hunk.header[hunk->hunk.header_len] = '\0';
fail:
giterr_set(GITERR_PATCH, "invalid patch hunk header at line %"PRIuZ,
- ctx->line_num);
+ ctx->parse_ctx.line_num);
return -1;
}
int newlines = hunk->hunk.new_lines;
for (;
- ctx->remain_len > 1 &&
+ ctx->parse_ctx.remain_len > 1 &&
(oldlines || newlines) &&
- (ctx->remain_len <= 4 || memcmp(ctx->line, "@@ -", 4) != 0);
- parse_advance_line(ctx)) {
+ !git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -");
+ git_parse_advance_line(&ctx->parse_ctx)) {
+ char c;
int origin;
int prefix = 1;
- if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n') {
- error = parse_err("invalid patch instruction at line %"PRIuZ,
- ctx->line_num);
+ if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') {
+ error = git_parse_err("invalid patch instruction at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
goto done;
}
- switch (ctx->line[0]) {
+ git_parse_peek(&c, &ctx->parse_ctx, 0);
+
+ switch (c) {
case '\n':
prefix = 0;
+ /* fall through */
case ' ':
origin = GIT_DIFF_LINE_CONTEXT;
break;
default:
- error = parse_err("invalid patch hunk at line %"PRIuZ, ctx->line_num);
+ error = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
}
memset(line, 0x0, sizeof(git_diff_line));
- line->content = ctx->line + prefix;
- line->content_len = ctx->line_len - prefix;
- line->content_offset = ctx->content_len - ctx->remain_len;
+ line->content = ctx->parse_ctx.line + prefix;
+ line->content_len = ctx->parse_ctx.line_len - prefix;
+ line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
line->origin = origin;
hunk->line_count++;
}
if (oldlines || newlines) {
- error = parse_err(
+ error = git_parse_err(
"invalid patch hunk, expected %d old lines and %d new lines",
hunk->hunk.old_lines, hunk->hunk.new_lines);
goto done;
* localized. Because `diff` optimizes for the case where you
* want to apply the patch by hand.
*/
- if (parse_ctx_contains_s(ctx, "\\ ") &&
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "\\ ") &&
git_array_size(patch->base.lines) > 0) {
line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);
if (line->content_len < 1) {
- error = parse_err("cannot trim trailing newline of empty line");
+ error = git_parse_err("cannot trim trailing newline of empty line");
goto done;
}
line->content_len--;
- parse_advance_line(ctx);
+ git_parse_advance_line(&ctx->parse_ctx);
}
done:
{
int error = 0;
- for (ctx->line = ctx->remain;
- ctx->remain_len > 0;
- parse_advance_line(ctx)) {
-
+ for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) {
/* This line is too short to be a patch header. */
- if (ctx->line_len < 6)
+ if (ctx->parse_ctx.line_len < 6)
continue;
/* This might be a hunk header without a patch header, provide a
* sensible error message. */
- if (parse_ctx_contains_s(ctx, "@@ -")) {
- size_t line_num = ctx->line_num;
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) {
+ size_t line_num = ctx->parse_ctx.line_num;
git_patch_hunk hunk;
/* If this cannot be parsed as a hunk header, it's just leading
continue;
}
- error = parse_err("invalid hunk header outside patch at line %"PRIuZ,
+ error = git_parse_err("invalid hunk header outside patch at line %"PRIuZ,
line_num);
goto done;
}
/* This buffer is too short to contain a patch. */
- if (ctx->remain_len < ctx->line_len + 6)
+ if (ctx->parse_ctx.remain_len < ctx->parse_ctx.line_len + 6)
break;
/* A proper git patch */
- if (parse_ctx_contains_s(ctx, "diff --git ")) {
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "diff --git ")) {
error = parse_header_git(patch, ctx);
goto done;
}
git_off_t len;
int error = 0;
- if (parse_ctx_contains_s(ctx, "literal ")) {
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "literal ")) {
type = GIT_DIFF_BINARY_LITERAL;
- parse_advance_chars(ctx, 8);
- } else if (parse_ctx_contains_s(ctx, "delta ")) {
+ git_parse_advance_chars(&ctx->parse_ctx, 8);
+ } else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "delta ")) {
type = GIT_DIFF_BINARY_DELTA;
- parse_advance_chars(ctx, 6);
+ git_parse_advance_chars(&ctx->parse_ctx, 6);
} else {
- error = parse_err(
- "unknown binary delta type at line %"PRIuZ, ctx->line_num);
+ error = git_parse_err(
+ "unknown binary delta type at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
}
- if (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) {
- error = parse_err("invalid binary size at line %"PRIuZ, ctx->line_num);
+ if (git_parse_advance_digit(&len, &ctx->parse_ctx, 10) < 0 ||
+ git_parse_advance_nl(&ctx->parse_ctx) < 0 || len < 0) {
+ error = git_parse_err("invalid binary size at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
}
- while (ctx->line_len) {
- char c = ctx->line[0];
+ while (ctx->parse_ctx.line_len) {
+ char c;
size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size;
+ git_parse_peek(&c, &ctx->parse_ctx, 0);
+
if (c == '\n')
break;
else if (c >= 'A' && c <= 'Z')
decoded_len = c - 'a' + (('z' - 'a') + 1) + 1;
if (!decoded_len) {
- error = parse_err("invalid binary length at line %"PRIuZ, ctx->line_num);
+ error = git_parse_err("invalid binary length at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
}
- parse_advance_chars(ctx, 1);
+ git_parse_advance_chars(&ctx->parse_ctx, 1);
encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5;
- if (encoded_len > ctx->line_len - 1) {
- error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
+ if (encoded_len > ctx->parse_ctx.line_len - 1) {
+ error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
}
if ((error = git_buf_decode_base85(
- &decoded, ctx->line, encoded_len, decoded_len)) < 0)
+ &decoded, ctx->parse_ctx.line, encoded_len, decoded_len)) < 0)
goto done;
if (decoded.size - decoded_orig != decoded_len) {
- error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
+ error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
}
- parse_advance_chars(ctx, encoded_len);
+ git_parse_advance_chars(&ctx->parse_ctx, encoded_len);
- if (parse_advance_nl(ctx) < 0) {
- error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
+ if (git_parse_advance_nl(&ctx->parse_ctx) < 0) {
+ error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
}
}
{
int error;
- if (parse_advance_expected_str(ctx, "GIT binary patch") < 0 ||
- parse_advance_nl(ctx) < 0)
- return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "GIT binary patch") < 0 ||
+ git_parse_advance_nl(&ctx->parse_ctx) < 0)
+ return git_parse_err("corrupt git binary header at line %"PRIuZ, ctx->parse_ctx.line_num);
/* parse old->new binary diff */
if ((error = parse_patch_binary_side(
&patch->base.binary.new_file, ctx)) < 0)
return error;
- if (parse_advance_nl(ctx) < 0)
- return parse_err("corrupt git binary separator at line %"PRIuZ,
- ctx->line_num);
+ if (git_parse_advance_nl(&ctx->parse_ctx) < 0)
+ return git_parse_err("corrupt git binary separator at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
/* parse new->old binary diff */
if ((error = parse_patch_binary_side(
&patch->base.binary.old_file, ctx)) < 0)
return error;
- if (parse_advance_nl(ctx) < 0)
- return parse_err("corrupt git binary patch separator at line %"PRIuZ,
- ctx->line_num);
+ if (git_parse_advance_nl(&ctx->parse_ctx) < 0)
+ return git_parse_err("corrupt git binary patch separator at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
patch->base.binary.contains_data = 1;
patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
git_patch_parsed *patch,
git_patch_parse_ctx *ctx)
{
- if (parse_advance_expected_str(ctx, "Binary files ") < 0 ||
- parse_advance_expected_str(ctx, patch->header_old_path) < 0 ||
- parse_advance_expected_str(ctx, " and ") < 0 ||
- parse_advance_expected_str(ctx, patch->header_new_path) < 0 ||
- parse_advance_expected_str(ctx, " differ") < 0 ||
- parse_advance_nl(ctx) < 0)
- return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "Binary files ") < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, patch->header_old_path) < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, " and ") < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, patch->header_new_path) < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, " differ") < 0 ||
+ git_parse_advance_nl(&ctx->parse_ctx) < 0)
+ return git_parse_err("corrupt git binary header at line %"PRIuZ, ctx->parse_ctx.line_num);
patch->base.binary.contains_data = 0;
patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
git_patch_hunk *hunk;
int error = 0;
- while (parse_ctx_contains_s(ctx, "@@ -")) {
+ while (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) {
hunk = git_array_alloc(patch->base.hunks);
GITERR_CHECK_ALLOC(hunk);
static int parse_patch_body(
git_patch_parsed *patch, git_patch_parse_ctx *ctx)
{
- if (parse_ctx_contains_s(ctx, "GIT binary patch"))
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "GIT binary patch"))
return parse_patch_binary(patch, ctx);
- else if (parse_ctx_contains_s(ctx, "Binary files "))
+ else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "Binary files "))
return parse_patch_binary_nodata(patch, ctx);
else
return parse_patch_hunks(patch, ctx);
return 0;
if (two_null && strcmp(two, "/dev/null") != 0)
- return parse_err("expected %s path of '/dev/null'", old_or_new);
+ return git_parse_err("expected %s path of '/dev/null'", old_or_new);
else if (!two_null && strcmp(one, two) != 0)
- return parse_err("mismatched %s path names", old_or_new);
+ return git_parse_err("mismatched %s path names", old_or_new);
return 0;
}
}
if (remain_len || !*path)
- return parse_err(
+ return git_parse_err(
"header filename does not contain %"PRIuZ" path components",
prefix_len);
bool deleted = (patch->base.delta->status == GIT_DELTA_DELETED);
if (patch->old_path && !patch->new_path)
- return parse_err("missing new path");
+ return git_parse_err("missing new path");
if (!patch->old_path && patch->new_path)
- return parse_err("missing old path");
+ return git_parse_err("missing old path");
/* Ensure (non-renamed) paths match */
if (check_header_names(
if (!patch->base.delta->old_file.path &&
!patch->base.delta->new_file.path)
- return parse_err("git diff header lacks old / new paths");
+ return git_parse_err("git diff header lacks old / new paths");
return 0;
}
!(delta->flags & GIT_DIFF_FLAG_BINARY) &&
delta->new_file.mode == delta->old_file.mode &&
git_array_size(patch->base.hunks) == 0)
- return parse_err("patch with no hunks");
+ return git_parse_err("patch with no hunks");
if (delta->status == GIT_DELTA_ADDED) {
memset(&delta->old_file.id, 0x0, sizeof(git_oid));
if ((ctx = git__calloc(1, sizeof(git_patch_parse_ctx))) == NULL)
return NULL;
- if (content_len) {
- if ((ctx->content = git__malloc(content_len)) == NULL) {
- git__free(ctx);
- return NULL;
- }
-
- memcpy((char *)ctx->content, content, content_len);
+ if ((git_parse_ctx_init(&ctx->parse_ctx, content, content_len)) < 0) {
+ git__free(ctx);
+ return NULL;
}
- ctx->content_len = content_len;
- ctx->remain = ctx->content;
- ctx->remain_len = ctx->content_len;
-
if (opts)
memcpy(&ctx->opts, opts, sizeof(git_patch_options));
else
if (!ctx)
return;
- git__free((char *)ctx->content);
+ git_parse_ctx_clear(&ctx->parse_ctx);
git__free(ctx);
}
patch->base.delta->status = GIT_DELTA_MODIFIED;
patch->base.delta->nfiles = 2;
- start = ctx->remain_len;
+ start = ctx->parse_ctx.remain_len;
if ((error = parse_patch_header(patch, ctx)) < 0 ||
(error = parse_patch_body(patch, ctx)) < 0 ||
(error = check_patch(patch)) < 0)
goto done;
- used = start - ctx->remain_len;
- ctx->remain += used;
+ used = start - ctx->parse_ctx.remain_len;
+ ctx->parse_ctx.remain += used;
patch->base.diff_opts.old_prefix = patch->old_prefix;
patch->base.diff_opts.new_prefix = patch->new_prefix;
patch->base.diff_opts.flags |= GIT_DIFF_SHOW_BINARY;
- GIT_REFCOUNT_INC(patch);
+ GIT_REFCOUNT_INC(&patch->base);
*out = &patch->base;
done:
#ifndef INCLUDE_patch_parse_h__
#define INCLUDE_patch_parse_h__
+#include "common.h"
+
+#include "parse.h"
+#include "patch.h"
+
typedef struct {
git_refcount rc;
- /* Original content buffer */
- const char *content;
- size_t content_len;
-
git_patch_options opts;
- /* The remaining (unparsed) buffer */
- const char *remain;
- size_t remain_len;
-
- const char *line;
- size_t line_len;
- size_t line_num;
+ git_parse_ctx parse_ctx;
} git_patch_parse_ctx;
extern git_patch_parse_ctx *git_patch_parse_ctx_init(
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
#include "path.h"
+
#include "posix.h"
#include "repository.h"
#ifdef GIT_WIN32
#define INCLUDE_path_h__
#include "common.h"
+
#include "posix.h"
#include "buffer.h"
#include "vector.h"
(name[1] == L'.' && name[2] == L'\0')));
}
+#define git_path_is_absolute(p) \
+ (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
+
+#define git_path_is_dirsep(p) \
+ ((p) == '/' || (p) == '\\')
+
/**
* Convert backslashes in path to forward slashes.
*/
}
#else
# define git_path_mkposix(p) /* blank */
+
+#define git_path_is_absolute(p) \
+ ((p)[0] == '/')
+
+#define git_path_is_dirsep(p) \
+ ((p) == '/')
+
#endif
/**
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "pathspec.h"
+
#include "git2/pathspec.h"
#include "git2/diff.h"
-#include "pathspec.h"
#include "buf_text.h"
#include "attr_file.h"
#include "iterator.h"
#define INCLUDE_pathspec_h__
#include "common.h"
-#include <git2/pathspec.h>
+
+#include "git2/pathspec.h"
#include "buffer.h"
#include "vector.h"
#include "pool.h"
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
#include "pool.h"
+
#include "posix.h"
#ifndef GIT_WIN32
#include <unistd.h>
#define INCLUDE_pool_h__
#include "common.h"
+
#include "vector.h"
typedef struct git_pool_page git_pool_page;
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+
#include "posix.h"
+
#include "path.h"
#include <stdio.h>
#include <ctype.h>
if (ainfo->ai_servent)
ainfo->ai_port = ainfo->ai_servent->s_port;
else
- ainfo->ai_port = atol(port);
+ ainfo->ai_port = htons(atol(port));
memcpy(&ainfo->ai_addr_in.sin_addr,
ainfo->ai_hostent->h_addr_list[0],
#define INCLUDE_posix_h__
#include "common.h"
+
#include <fcntl.h>
#include <time.h>
#include "fnmatch.h"
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
/* access() mode parameter #defines */
#ifndef F_OK
*/
#include "pqueue.h"
+
#include "util.h"
#define PQUEUE_LCHILD_OF(I) (((I)<<1)+1)
#ifndef INCLUDE_pqueue_h__
#define INCLUDE_pqueue_h__
+#include "common.h"
+
#include "vector.h"
typedef git_vector git_pqueue;
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "proxy.h"
+
#include "git2/proxy.h"
int git_proxy_init_options(git_proxy_options *opts, unsigned int version)
return 0;
}
+
+void git_proxy_options_clear(git_proxy_options *opts)
+{
+ git__free((char *) opts->url);
+ opts->url = NULL;
+}
#ifndef INCLUDE_proxy_h__
#define INCLUDE_proxy_h__
+#include "common.h"
+
#include "git2/proxy.h"
extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src);
+extern void git_proxy_options_clear(git_proxy_options *opts);
-#endif
\ No newline at end of file
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "push.h"
+
#include "git2.h"
-#include "common.h"
#include "pack.h"
#include "pack-objects.h"
#include "remote.h"
#include "vector.h"
-#include "push.h"
#include "tree.h"
static int push_spec_rref_cmp(const void *a, const void *b)
if (!fetch_spec)
continue;
+ /* Clear the buffer which can be dirty from previous iteration */
+ git_buf_clear(&remote_ref_name);
+
if ((error = git_refspec_transform(&remote_ref_name, fetch_spec, status->ref)) < 0)
goto on_error;
return error;
}
-static int revwalk(git_vector *commits, git_push *push)
+static int queue_objects(git_push *push)
{
git_remote_head *head;
push_spec *spec;
git_revwalk *rw;
- git_oid oid;
unsigned int i;
int error = -1;
git_revwalk_hide(rw, &head->oid);
}
- while ((error = git_revwalk_next(&oid, rw)) == 0) {
- git_oid *o = git__malloc(GIT_OID_RAWSZ);
- if (!o) {
- error = -1;
- goto on_error;
- }
- git_oid_cpy(o, &oid);
- if ((error = git_vector_insert(commits, o)) < 0)
- goto on_error;
- }
+ error = git_packbuilder_insert_walk(push->pb, rw);
on_error:
git_revwalk_free(rw);
- return error == GIT_ITEROVER ? 0 : error;
-}
-
-static int enqueue_object(
- const git_tree_entry *entry,
- git_packbuilder *pb)
-{
- switch (git_tree_entry_type(entry)) {
- case GIT_OBJ_COMMIT:
- return 0;
- case GIT_OBJ_TREE:
- return git_packbuilder_insert_tree(pb, entry->oid);
- default:
- return git_packbuilder_insert(pb, entry->oid, entry->filename);
- }
-}
-
-static int queue_differences(
- git_tree *base,
- git_tree *delta,
- git_packbuilder *pb)
-{
- git_tree *b_child = NULL, *d_child = NULL;
- size_t b_length = git_tree_entrycount(base);
- size_t d_length = git_tree_entrycount(delta);
- size_t i = 0, j = 0;
- int error;
-
- while (i < b_length && j < d_length) {
- const git_tree_entry *b_entry = git_tree_entry_byindex(base, i);
- const git_tree_entry *d_entry = git_tree_entry_byindex(delta, j);
- int cmp = 0;
-
- if (!git_oid__cmp(b_entry->oid, d_entry->oid))
- goto loop;
-
- cmp = strcmp(b_entry->filename, d_entry->filename);
-
- /* If the entries are both trees and they have the same name but are
- * different, then we'll recurse after adding the right-hand entry */
- if (!cmp &&
- git_tree_entry__is_tree(b_entry) &&
- git_tree_entry__is_tree(d_entry)) {
- /* Add the right-hand entry */
- if ((error = git_packbuilder_insert(pb, d_entry->oid,
- d_entry->filename)) < 0)
- goto on_error;
-
- /* Acquire the subtrees and recurse */
- if ((error = git_tree_lookup(&b_child,
- git_tree_owner(base), b_entry->oid)) < 0 ||
- (error = git_tree_lookup(&d_child,
- git_tree_owner(delta), d_entry->oid)) < 0 ||
- (error = queue_differences(b_child, d_child, pb)) < 0)
- goto on_error;
-
- git_tree_free(b_child); b_child = NULL;
- git_tree_free(d_child); d_child = NULL;
- }
- /* If the object is new or different in the right-hand tree,
- * then enumerate it */
- else if (cmp >= 0 &&
- (error = enqueue_object(d_entry, pb)) < 0)
- goto on_error;
-
- loop:
- if (cmp <= 0) i++;
- if (cmp >= 0) j++;
- }
-
- /* Drain the right-hand tree of entries */
- for (; j < d_length; j++)
- if ((error = enqueue_object(git_tree_entry_byindex(delta, j), pb)) < 0)
- goto on_error;
-
- error = 0;
-
-on_error:
- if (b_child)
- git_tree_free(b_child);
-
- if (d_child)
- git_tree_free(d_child);
-
- return error;
-}
-
-static int queue_objects(git_push *push)
-{
- git_vector commits = GIT_VECTOR_INIT;
- git_oid *oid;
- size_t i;
- unsigned j;
- int error;
-
- if ((error = revwalk(&commits, push)) < 0)
- goto on_error;
-
- git_vector_foreach(&commits, i, oid) {
- git_commit *parent = NULL, *commit;
- git_tree *tree = NULL, *ptree = NULL;
- size_t parentcount;
-
- if ((error = git_commit_lookup(&commit, push->repo, oid)) < 0)
- goto on_error;
-
- /* Insert the commit */
- if ((error = git_packbuilder_insert(push->pb, oid, NULL)) < 0)
- goto loop_error;
-
- parentcount = git_commit_parentcount(commit);
-
- if (!parentcount) {
- if ((error = git_packbuilder_insert_tree(push->pb,
- git_commit_tree_id(commit))) < 0)
- goto loop_error;
- } else {
- if ((error = git_tree_lookup(&tree, push->repo,
- git_commit_tree_id(commit))) < 0 ||
- (error = git_packbuilder_insert(push->pb,
- git_commit_tree_id(commit), NULL)) < 0)
- goto loop_error;
-
- /* For each parent, add the items which are different */
- for (j = 0; j < parentcount; j++) {
- if ((error = git_commit_parent(&parent, commit, j)) < 0 ||
- (error = git_commit_tree(&ptree, parent)) < 0 ||
- (error = queue_differences(ptree, tree, push->pb)) < 0)
- goto loop_error;
-
- git_tree_free(ptree); ptree = NULL;
- git_commit_free(parent); parent = NULL;
- }
- }
-
- error = 0;
-
- loop_error:
- if (tree)
- git_tree_free(tree);
-
- if (ptree)
- git_tree_free(ptree);
-
- if (parent)
- git_commit_free(parent);
-
- git_commit_free(commit);
-
- if (error < 0)
- goto on_error;
- }
-
- error = 0;
-
-on_error:
- git_vector_free_deep(&commits);
return error;
}
#ifndef INCLUDE_push_h__
#define INCLUDE_push_h__
+#include "common.h"
+
#include "git2.h"
#include "refspec.h"
*/
#include "common.h"
+
#include "buffer.h"
#include "repository.h"
#include "posix.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "posix.h"
+#include "refdb.h"
#include "git2/object.h"
#include "git2/refs.h"
#include "git2/sys/refdb_backend.h"
#include "hash.h"
-#include "refdb.h"
#include "refs.h"
#include "reflog.h"
+#include "posix.h"
int git_refdb_new(git_refdb **out, git_repository *repo)
{
#ifndef INCLUDE_refdb_h__
#define INCLUDE_refdb_h__
+#include "common.h"
+
#include "git2/refdb.h"
#include "repository.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "refdb_fs.h"
+
#include "refs.h"
#include "hash.h"
#include "repository.h"
#include "pack.h"
#include "reflog.h"
#include "refdb.h"
-#include "refdb_fs.h"
#include "iterator.h"
#include "sortedcache.h"
#include "signature.h"
if ((!git_repository__cvar(&t, backend->repo, GIT_CVAR_FSYNCOBJECTFILES) && t) ||
git_repository__fsync_gitdir)
backend->fsync = 1;
+ backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS;
backend->parent.exists = &refdb_fs_backend__exists;
backend->parent.lookup = &refdb_fs_backend__lookup;
#ifndef INCLUDE_refdb_fs_h__
#define INCLUDE_refdb_fs_h__
+#include "common.h"
+
+#include "strmap.h"
+
typedef struct {
git_strmap *packfile;
time_t packfile_time;
*/
#include "reflog.h"
+
#include "repository.h"
#include "filebuf.h"
#include "signature.h"
#define INCLUDE_reflog_h__
#include "common.h"
+
#include "git2/reflog.h"
#include "vector.h"
return (total - 1) - idx;
}
-#endif /* INCLUDE_reflog_h__ */
+#endif
*/
#include "refs.h"
+
#include "hash.h"
#include "repository.h"
#include "fileops.h"
return peel_error(error, ref, "Cannot resolve reference");
}
- if (!git_oid_iszero(&resolved->peel)) {
+ /*
+ * If we try to peel an object to a tag, we cannot use
+ * the fully peeled object, as that will always resolve
+ * to a commit. So we only want to use the peeled value
+ * if it is not zero and the target is not a tag.
+ */
+ if (target_type != GIT_OBJ_TAG && !git_oid_iszero(&resolved->peel)) {
error = git_object_lookup(&target,
git_reference_owner(ref), &resolved->peel, GIT_OBJ_ANY);
} else {
#define INCLUDE_refs_h__
#include "common.h"
+
#include "git2/oid.h"
#include "git2/refs.h"
#include "git2/refdb.h"
#define GIT_SYMREF "ref: "
#define GIT_PACKEDREFS_FILE "packed-refs"
-#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled "
+#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled sorted "
#define GIT_PACKEDREFS_FILE_MODE 0666
#define GIT_HEAD_FILE "HEAD"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "refspec.h"
+
#include "git2/errors.h"
-#include "common.h"
-#include "refspec.h"
#include "util.h"
#include "posix.h"
#include "refs.h"
#ifndef INCLUDE_refspec_h__
#define INCLUDE_refspec_h__
+#include "common.h"
+
#include "git2/refspec.h"
#include "buffer.h"
#include "vector.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "remote.h"
+
#include "git2/config.h"
#include "git2/types.h"
#include "git2/oid.h"
#include "git2/net.h"
-#include "common.h"
#include "config.h"
#include "repository.h"
-#include "remote.h"
#include "fetch.h"
#include "refs.h"
#include "refspec.h"
git_buf var = GIT_BUF_INIT;
int error = -1;
- /* name is optional */
- assert(out && repo && url);
+ /* repo, name, and fetch are optional */
+ assert(out && url);
- if ((error = git_repository_config_snapshot(&config_ro, repo)) < 0)
+ if (repo && (error = git_repository_config_snapshot(&config_ro, repo)) < 0)
return error;
remote = git__calloc(1, sizeof(git_remote));
(error = canonicalize_url(&canonical_url, url)) < 0)
goto on_error;
- remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH);
+ if (repo) {
+ remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH);
+ } else {
+ remote->url = git__strdup(canonical_url.ptr);
+ }
GITERR_CHECK_ALLOC(remote->url);
if (name != NULL) {
if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
goto on_error;
- if ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 ||
- (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0)
+ if (repo &&
+ ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 ||
+ (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
goto on_error;
}
if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0)
goto on_error;
- if ((error = lookup_remote_prune_config(remote, config_ro, name)) < 0)
+ if (repo && (error = lookup_remote_prune_config(remote, config_ro, name)) < 0)
goto on_error;
/* Move the data over to where the matching functions can find them */
return create_internal(out, repo, NULL, url, NULL);
}
+int git_remote_create_detached(git_remote **out, const char *url)
+{
+ return create_internal(out, NULL, NULL, url, NULL);
+}
+
int git_remote_dup(git_remote **dest, git_remote *source)
{
size_t i;
url = git_remote__urlfordirection(remote, direction);
if (url == NULL) {
giterr_set(GITERR_INVALID,
- "Malformed remote '%s' - missing URL", remote->name);
+ "Malformed remote '%s' - missing %s URL",
+ remote->name ? remote->name : "(anonymous)",
+ direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
return -1;
}
assert(remote);
+ if (!remote->repo) {
+ giterr_set(GITERR_INVALID, "cannot download detached remote");
+ return -1;
+ }
+
if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
return error;
}
+static int truncate_fetch_head(const char *gitdir)
+{
+ git_buf path = GIT_BUF_INIT;
+ int error;
+
+ if ((error = git_buf_joinpath(&path, gitdir, GIT_FETCH_HEAD_FILE)) < 0)
+ return error;
+
+ error = git_futils_truncate(path.ptr, GIT_REFS_FILE_MODE);
+ git_buf_free(&path);
+
+ return error;
+}
+
int git_remote_update_tips(
git_remote *remote,
const git_remote_callbacks *callbacks,
else
tagopt = download_tags;
+ if ((error = truncate_fetch_head(git_repository_path(remote->repo))) < 0)
+ goto out;
+
if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0)
goto out;
assert(remote);
+ if (!remote->repo) {
+ giterr_set(GITERR_INVALID, "cannot download detached remote");
+ return -1;
+ }
+
if (opts) {
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
+ assert(remote);
+
+ if (!remote->repo) {
+ giterr_set(GITERR_INVALID, "cannot download detached remote");
+ return -1;
+ }
+
if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
#ifndef INCLUDE_remote_h__
#define INCLUDE_remote_h__
+#include "common.h"
+
#include "git2/remote.h"
#include "git2/transport.h"
#include "git2/sys/transport.h"
* 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 "repository.h"
+
#include <ctype.h>
#include "git2/object.h"
-#include "git2/refdb.h"
#include "git2/sys/repository.h"
#include "common.h"
-#include "repository.h"
#include "commit.h"
#include "tag.h"
#include "blob.h"
#include "refs.h"
#include "filter.h"
#include "odb.h"
+#include "refdb.h"
#include "remote.h"
#include "merge.h"
#include "diff_driver.h"
return error;
if ((error = git_repository_item_path(&config_path, repo, GIT_REPOSITORY_ITEM_CONFIG)) == 0)
- error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0);
+ error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, repo, 0);
if (error && error != GIT_ENOTFOUND)
goto on_error;
if (global_config_path != NULL &&
(error = git_config_add_file_ondisk(
- cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0)) < 0 &&
+ cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, repo, 0)) < 0 &&
error != GIT_ENOTFOUND)
goto on_error;
if (xdg_config_path != NULL &&
(error = git_config_add_file_ondisk(
- cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, 0)) < 0 &&
+ cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, repo, 0)) < 0 &&
error != GIT_ENOTFOUND)
goto on_error;
if (system_config_path != NULL &&
(error = git_config_add_file_ondisk(
- cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, 0)) < 0 &&
+ cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, repo, 0)) < 0 &&
error != GIT_ENOTFOUND)
goto on_error;
if (programdata_path != NULL &&
(error = git_config_add_file_ondisk(
- cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, 0)) < 0 &&
+ cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, repo, 0)) < 0 &&
error != GIT_ENOTFOUND)
goto on_error;
name_len = strlen(name);
- if ((name_len == def_len && memcmp(name, def, def_len) == 0) ||
+ if ((name_len == def_len && memcmp(name, def, def_len) == 0) ||
(name_len == def_dot_git_len && memcmp(name, def_dot_git, def_dot_git_len) == 0)) {
git__free(name);
return 0;
giterr_clear();
if (!(error = git_config_add_file_ondisk(
- parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
+ parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, repo, false)))
error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
}
default_template = true;
}
- if (tdir) {
+ /*
+ * If tdir was the empty string, treat it like tdir was a path to an
+ * empty directory (so, don't do any copying). This is the behavior
+ * that git(1) exhibits, although it doesn't seem to be officially
+ * documented.
+ */
+ if (tdir && git__strcmp(tdir, "") != 0) {
uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS |
GIT_CPDIR_SIMPLE_TO_MODE |
GIT_CPDIR_COPY_DOTFILES;
return is_empty;
}
-int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item)
+int git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item)
{
const char *parent;
return 0;
}
-const char *git_repository_path(git_repository *repo)
+const char *git_repository_path(const git_repository *repo)
{
assert(repo);
return repo->gitdir;
}
-const char *git_repository_workdir(git_repository *repo)
+const char *git_repository_workdir(const git_repository *repo)
{
assert(repo);
return repo->workdir;
}
-const char *git_repository_commondir(git_repository *repo)
+const char *git_repository_commondir(const git_repository *repo)
{
assert(repo);
return repo->commondir;
return error;
}
-int git_repository_is_bare(git_repository *repo)
+int git_repository_is_bare(const git_repository *repo)
{
assert(repo);
return repo->is_bare;
}
-int git_repository_is_worktree(git_repository *repo)
+int git_repository_is_worktree(const git_repository *repo)
{
assert(repo);
return repo->is_worktree;
error = git_futils_rmdir_r(path, NULL,
GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
}
-
+
git_buf_clear(&buf);
}
#ifndef INCLUDE_repository_h__
#define INCLUDE_repository_h__
+#include "common.h"
+
#include "git2/common.h"
#include "git2/oid.h"
#include "git2/odb.h"
*/
#include "common.h"
+
#include "commit.h"
#include "tag.h"
#include "merge.h"
int git_reset_default(
git_repository *repo,
- git_object *target,
- git_strarray* pathspecs)
+ const git_object *target,
+ const git_strarray* pathspecs)
{
git_object *commit = NULL;
git_tree *tree = NULL;
static int reset(
git_repository *repo,
- git_object *target,
+ const git_object *target,
const char *to,
git_reset_t reset_type,
const git_checkout_options *checkout_opts)
int git_reset(
git_repository *repo,
- git_object *target,
+ const git_object *target,
git_reset_t reset_type,
const git_checkout_options *checkout_opts)
{
int git_reset_from_annotated(
git_repository *repo,
- git_annotated_commit *commit,
+ const git_annotated_commit *commit,
git_reset_t reset_type,
const git_checkout_options *checkout_opts)
{
*/
#include "common.h"
+
#include "repository.h"
#include "filebuf.h"
#include "merge.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "common.h"
+
#include <assert.h>
-#include "common.h"
#include "buffer.h"
#include "tree.h"
#include "refdb.h"
}
case '@':
- {
if (spec[pos+1] == '{') {
git_object *temp_object = NULL;
if (temp_object != NULL)
base_rev = temp_object;
break;
- } else {
- /* Fall through */
}
- }
+ /* fall through */
default:
if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0)
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "revwalk.h"
+
#include "commit.h"
#include "odb.h"
#include "pool.h"
-#include "revwalk.h"
#include "git2/revparse.h"
#include "merge.h"
#include "vector.h"
#ifndef INCLUDE_revwalk_h__
#define INCLUDE_revwalk_h__
+#include "common.h"
+
#include "git2/revwalk.h"
#include "oidmap.h"
#include "commit_list.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "common.h"
+
#ifdef GIT_OPENSSL
# include <openssl/err.h>
#endif
#include <git2.h>
-#include "common.h"
#include "sysdir.h"
#include "cache.h"
#include "global.h"
#include "odb.h"
#include "refs.h"
#include "transports/smart.h"
+#include "streams/openssl.h"
void git_libgit2_version(int *major, int *minor, int *rev)
{
{
const char *file = va_arg(ap, const char *);
const char *path = va_arg(ap, const char *);
- if (!SSL_CTX_load_verify_locations(git__ssl_ctx, file, path)) {
- giterr_set(GITERR_NET, "SSL error: %s",
- ERR_error_string(ERR_get_error(), NULL));
- error = -1;
- }
+ error = git_openssl__set_cert_location(file, path);
}
#else
- giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled");
+ giterr_set(GITERR_SSL, "TLS backend doesn't support certificate locations");
error = -1;
#endif
break;
}
}
#else
- giterr_set(GITERR_NET, "cannot set custom ciphers: OpenSSL is not enabled");
+ giterr_set(GITERR_SSL, "TLS backend doesn't support custom ciphers");
error = -1;
#endif
break;
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include <stdio.h>
-
#include "sha1_lookup.h"
-#include "common.h"
-#include "oid.h"
-/*
- * Conventional binary search loop looks like this:
- *
- * unsigned lo, hi;
- * do {
- * unsigned mi = (lo + hi) / 2;
- * int cmp = "entry pointed at by mi" minus "target";
- * if (!cmp)
- * return (mi is the wanted one)
- * if (cmp > 0)
- * hi = mi; "mi is larger than target"
- * else
- * lo = mi+1; "mi is smaller than target"
- * } while (lo < hi);
- *
- * The invariants are:
- *
- * - When entering the loop, lo points at a slot that is never
- * above the target (it could be at the target), hi points at a
- * slot that is guaranteed to be above the target (it can never
- * be at the target).
- *
- * - We find a point 'mi' between lo and hi (mi could be the same
- * as lo, but never can be as same as hi), and check if it hits
- * the target. There are three cases:
- *
- * - if it is a hit, we are happy.
- *
- * - if it is strictly higher than the target, we set it to hi,
- * and repeat the search.
- *
- * - if it is strictly lower than the target, we update lo to
- * one slot after it, because we allow lo to be at the target.
- *
- * If the loop exits, there is no matching entry.
- *
- * When choosing 'mi', we do not have to take the "middle" but
- * anywhere in between lo and hi, as long as lo <= mi < hi is
- * satisfied. When we somehow know that the distance between the
- * target and lo is much shorter than the target and hi, we could
- * pick mi that is much closer to lo than the midway.
- *
- * Now, we can take advantage of the fact that SHA-1 is a good hash
- * function, and as long as there are enough entries in the table, we
- * can expect uniform distribution. An entry that begins with for
- * example "deadbeef..." is much likely to appear much later than in
- * the midway of the table. It can reasonably be expected to be near
- * 87% (222/256) from the top of the table.
- *
- * However, we do not want to pick "mi" too precisely. If the entry at
- * the 87% in the above example turns out to be higher than the target
- * we are looking for, we would end up narrowing the search space down
- * only by 13%, instead of 50% we would get if we did a simple binary
- * search. So we would want to hedge our bets by being less aggressive.
- *
- * The table at "table" holds at least "nr" entries of "elem_size"
- * bytes each. Each entry has the SHA-1 key at "key_offset". The
- * table is sorted by the SHA-1 key of the entries. The caller wants
- * to find the entry with "key", and knows that the entry at "lo" is
- * not higher than the entry it is looking for, and that the entry at
- * "hi" is higher than the entry it is looking for.
- */
-int sha1_entry_pos(const void *table,
- size_t elem_size,
- size_t key_offset,
- unsigned lo, unsigned hi, unsigned nr,
- const unsigned char *key)
-{
- const unsigned char *base = (const unsigned char*)table;
- const unsigned char *hi_key, *lo_key;
- unsigned ofs_0;
-
- if (!nr || lo >= hi)
- return -1;
-
- if (nr == hi)
- hi_key = NULL;
- else
- hi_key = base + elem_size * hi + key_offset;
- lo_key = base + elem_size * lo + key_offset;
-
- ofs_0 = 0;
- do {
- int cmp;
- unsigned ofs, mi, range;
- unsigned lov, hiv, kyv;
- const unsigned char *mi_key;
-
- range = hi - lo;
- if (hi_key) {
- for (ofs = ofs_0; ofs < 20; ofs++)
- if (lo_key[ofs] != hi_key[ofs])
- break;
- ofs_0 = ofs;
- /*
- * byte 0 thru (ofs-1) are the same between
- * lo and hi; ofs is the first byte that is
- * different.
- *
- * If ofs==20, then no bytes are different,
- * meaning we have entries with duplicate
- * keys. We know that we are in a solid run
- * of this entry (because the entries are
- * sorted, and our lo and hi are the same,
- * there can be nothing but this single key
- * in between). So we can stop the search.
- * Either one of these entries is it (and
- * we do not care which), or we do not have
- * it.
- *
- * Furthermore, we know that one of our
- * endpoints must be the edge of the run of
- * duplicates. For example, given this
- * sequence:
- *
- * idx 0 1 2 3 4 5
- * key A C C C C D
- *
- * If we are searching for "B", we might
- * hit the duplicate run at lo=1, hi=3
- * (e.g., by first mi=3, then mi=0). But we
- * can never have lo > 1, because B < C.
- * That is, if our key is less than the
- * run, we know that "lo" is the edge, but
- * we can say nothing of "hi". Similarly,
- * if our key is greater than the run, we
- * know that "hi" is the edge, but we can
- * say nothing of "lo".
- *
- * Therefore if we do not find it, we also
- * know where it would go if it did exist:
- * just on the far side of the edge that we
- * know about.
- */
- if (ofs == 20) {
- mi = lo;
- mi_key = base + elem_size * mi + key_offset;
- cmp = memcmp(mi_key, key, 20);
- if (!cmp)
- return mi;
- if (cmp < 0)
- return -1 - hi;
- else
- return -1 - lo;
- }
-
- hiv = hi_key[ofs_0];
- if (ofs_0 < 19)
- hiv = (hiv << 8) | hi_key[ofs_0+1];
- } else {
- hiv = 256;
- if (ofs_0 < 19)
- hiv <<= 8;
- }
- lov = lo_key[ofs_0];
- kyv = key[ofs_0];
- if (ofs_0 < 19) {
- lov = (lov << 8) | lo_key[ofs_0+1];
- kyv = (kyv << 8) | key[ofs_0+1];
- }
- assert(lov < hiv);
-
- if (kyv < lov)
- return -1 - lo;
- if (hiv < kyv)
- return -1 - hi;
-
- /*
- * Even if we know the target is much closer to 'hi'
- * than 'lo', if we pick too precisely and overshoot
- * (e.g. when we know 'mi' is closer to 'hi' than to
- * 'lo', pick 'mi' that is higher than the target), we
- * end up narrowing the search space by a smaller
- * amount (i.e. the distance between 'mi' and 'hi')
- * than what we would have (i.e. about half of 'lo'
- * and 'hi'). Hedge our bets to pick 'mi' less
- * aggressively, i.e. make 'mi' a bit closer to the
- * middle than we would otherwise pick.
- */
- kyv = (kyv * 6 + lov + hiv) / 8;
- if (lov < hiv - 1) {
- if (kyv == lov)
- kyv++;
- else if (kyv == hiv)
- kyv--;
- }
- mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
-
-#ifdef INDEX_DEBUG_LOOKUP
- printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
- printf("ofs %u lov %x, hiv %x, kyv %x\n",
- ofs_0, lov, hiv, kyv);
-#endif
-
- if (!(lo <= mi && mi < hi)) {
- giterr_set(GITERR_INVALID, "assertion failure: binary search invariant is false");
- return -1;
- }
+#include <stdio.h>
- mi_key = base + elem_size * mi + key_offset;
- cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
- if (!cmp)
- return mi;
- if (cmp > 0) {
- hi = mi;
- hi_key = mi_key;
- } else {
- lo = mi + 1;
- lo_key = mi_key + elem_size;
- }
- } while (lo < hi);
- return -((int)lo)-1;
-}
+#include "oid.h"
int sha1_position(const void *table,
size_t stride,
{
const unsigned char *base = table;
- do {
+ while (lo < hi) {
unsigned mi = (lo + hi) / 2;
int cmp = git_oid__hashcmp(base + mi * stride, key);
hi = mi;
else
lo = mi+1;
- } while (lo < hi);
+ }
return -((int)lo)-1;
}
#ifndef INCLUDE_sha1_lookup_h__
#define INCLUDE_sha1_lookup_h__
-#include <stdlib.h>
+#include "common.h"
-int sha1_entry_pos(const void *table,
- size_t elem_size,
- size_t key_offset,
- unsigned lo, unsigned hi, unsigned nr,
- const unsigned char *key);
+#include <stdlib.h>
int sha1_position(const void *table,
size_t stride,
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "signature.h"
+
#include "repository.h"
#include "git2/common.h"
#include "posix.h"
p->when.time = time;
p->when.offset = offset;
+ p->when.sign = (offset < 0) ? '-' : '+';
*sig_out = p;
return 0;
signature->when.time = source->when.time;
signature->when.offset = source->when.offset;
+ signature->when.sign = source->when.sign;
*dest = signature;
signature->when.time = source->when.time;
signature->when.offset = source->when.offset;
+ signature->when.sign = source->when.sign;
*dest = signature;
if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) {
git__free(sig->name);
git__free(sig->email);
+ sig->name = sig->email = NULL;
return signature_error("invalid Unix timestamp");
}
*/
if (hours <= 14 && mins <= 59) {
sig->when.offset = (hours * 60) + mins;
+ sig->when.sign = tz_start[0];
if (tz_start[0] == '-')
sig->when.offset = -sig->when.offset;
}
assert(buf && sig);
offset = sig->when.offset;
- sign = (sig->when.offset < 0) ? '-' : '+';
+ sign = (sig->when.offset < 0 || sig->when.sign == '-') ? '-' : '+';
if (offset < 0)
offset = -offset;
git__strcmp(one->name, two->name) == 0 &&
git__strcmp(one->email, two->email) == 0 &&
one->when.time == two->when.time &&
- one->when.offset == two->when.offset;
+ one->when.offset == two->when.offset &&
+ one->when.sign == two->when.sign;
}
#ifndef INCLUDE_signature_h__
#define INCLUDE_signature_h__
+#include "common.h"
+
#include "git2/common.h"
#include "git2/signature.h"
#include "repository.h"
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "posix.h"
-#include "netops.h"
-#include "stream.h"
-#include "socket_stream.h"
-
-#ifndef _WIN32
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <sys/select.h>
-# include <sys/time.h>
-# include <netdb.h>
-# include <netinet/in.h>
-# include <arpa/inet.h>
-#else
-# include <winsock2.h>
-# include <ws2tcpip.h>
-# ifdef _MSC_VER
-# pragma comment(lib, "ws2_32")
-# endif
-#endif
-
-#ifdef GIT_WIN32
-static void net_set_error(const char *str)
-{
- int error = WSAGetLastError();
- char * win32_error = git_win32_get_error_message(error);
-
- if (win32_error) {
- giterr_set(GITERR_NET, "%s: %s", str, win32_error);
- git__free(win32_error);
- } else {
- giterr_set(GITERR_NET, str);
- }
-}
-#else
-static void net_set_error(const char *str)
-{
- giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
-}
-#endif
-
-static int close_socket(GIT_SOCKET s)
-{
- if (s == INVALID_SOCKET)
- return 0;
-
-#ifdef GIT_WIN32
- if (SOCKET_ERROR == closesocket(s))
- return -1;
-
- if (0 != WSACleanup()) {
- giterr_set(GITERR_OS, "winsock cleanup failed");
- return -1;
- }
-
- return 0;
-#else
- return close(s);
-#endif
-
-}
-
-int socket_connect(git_stream *stream)
-{
- struct addrinfo *info = NULL, *p;
- struct addrinfo hints;
- git_socket_stream *st = (git_socket_stream *) stream;
- GIT_SOCKET s = INVALID_SOCKET;
- int ret;
-
-#ifdef GIT_WIN32
- /* on win32, the WSA context needs to be initialized
- * before any socket calls can be performed */
- WSADATA wsd;
-
- if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
- giterr_set(GITERR_OS, "winsock init failed");
- return -1;
- }
-
- if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
- WSACleanup();
- giterr_set(GITERR_OS, "winsock init failed");
- return -1;
- }
-#endif
-
- memset(&hints, 0x0, sizeof(struct addrinfo));
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_family = AF_UNSPEC;
-
- if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
- giterr_set(GITERR_NET,
- "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
- return -1;
- }
-
- for (p = info; p != NULL; p = p->ai_next) {
- s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
-
- if (s == INVALID_SOCKET)
- continue;
-
- if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
- break;
-
- /* If we can't connect, try the next one */
- close_socket(s);
- s = INVALID_SOCKET;
- }
-
- /* Oops, we couldn't connect to any address */
- if (s == INVALID_SOCKET && p == NULL) {
- giterr_set(GITERR_OS, "failed to connect to %s", st->host);
- p_freeaddrinfo(info);
- return -1;
- }
-
- st->s = s;
- p_freeaddrinfo(info);
- return 0;
-}
-
-ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
-{
- ssize_t ret;
- size_t off = 0;
- git_socket_stream *st = (git_socket_stream *) stream;
-
- while (off < len) {
- errno = 0;
- ret = p_send(st->s, data + off, len - off, flags);
- if (ret < 0) {
- net_set_error("Error sending data");
- return -1;
- }
-
- off += ret;
- }
-
- return off;
-}
-
-ssize_t socket_read(git_stream *stream, void *data, size_t len)
-{
- ssize_t ret;
- git_socket_stream *st = (git_socket_stream *) stream;
-
- if ((ret = p_recv(st->s, data, len, 0)) < 0)
- net_set_error("Error receiving socket data");
-
- return ret;
-}
-
-int socket_close(git_stream *stream)
-{
- git_socket_stream *st = (git_socket_stream *) stream;
- int error;
-
- error = close_socket(st->s);
- st->s = INVALID_SOCKET;
-
- return error;
-}
-
-void socket_free(git_stream *stream)
-{
- git_socket_stream *st = (git_socket_stream *) stream;
-
- git__free(st->host);
- git__free(st->port);
- git__free(st);
-}
-
-int git_socket_stream_new(git_stream **out, const char *host, const char *port)
-{
- git_socket_stream *st;
-
- assert(out && host);
-
- st = git__calloc(1, sizeof(git_socket_stream));
- GITERR_CHECK_ALLOC(st);
-
- st->host = git__strdup(host);
- GITERR_CHECK_ALLOC(st->host);
-
- if (port) {
- st->port = git__strdup(port);
- GITERR_CHECK_ALLOC(st->port);
- }
-
- st->parent.version = GIT_STREAM_VERSION;
- st->parent.connect = socket_connect;
- st->parent.write = socket_write;
- st->parent.read = socket_read;
- st->parent.close = socket_close;
- st->parent.free = socket_free;
- st->s = INVALID_SOCKET;
-
- *out = (git_stream *) st;
- return 0;
-}
+++ /dev/null
-/*
- * 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_socket_stream_h__
-#define INCLUDE_socket_stream_h__
-
-#include "netops.h"
-
-typedef struct {
- git_stream parent;
- char *host;
- char *port;
- GIT_SOCKET s;
-} git_socket_stream;
-
-extern int git_socket_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
#include "sortedcache.h"
int git_sortedcache_new(
#ifndef INCLUDE_sorted_cache_h__
#define INCLUDE_sorted_cache_h__
+#include "common.h"
+
#include "util.h"
#include "fileops.h"
#include "vector.h"
*/
#include "common.h"
+
#include "repository.h"
#include "commit.h"
#include "message.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "status.h"
+
#include "git2.h"
#include "fileops.h"
#include "hash.h"
#include "vector.h"
#include "tree.h"
-#include "status.h"
#include "git2/status.h"
#include "repository.h"
#include "ignore.h"
if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 ||
(error = git_repository_index(&index, repo)) < 0)
return error;
-
- /* if there is no HEAD, that's okay - we'll make an empty iterator */
- if ((error = git_repository_head_tree(&head, repo)) < 0) {
- if (error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH)
- goto done;
- giterr_clear();
+
+ if (opts != NULL && opts->baseline != NULL) {
+ head = opts->baseline;
+ } else {
+ /* if there is no HEAD, that's okay - we'll make an empty iterator */
+ if ((error = git_repository_head_tree(&head, repo)) < 0) {
+ if (error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH)
+ goto done;
+ giterr_clear();
+ }
}
/* refresh index from disk unless prevented */
*out = status;
- git_tree_free(head);
+ if (opts == NULL || opts->baseline != head)
+ git_tree_free(head);
git_index_free(index);
return error;
#ifndef INCLUDE_status_h__
#define INCLUDE_status_h__
+#include "common.h"
+
#include "diff.h"
#include "git2/status.h"
#include "git2/diff.h"
+++ /dev/null
-/*
- * 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.
- */
-
-#ifdef GIT_SECURE_TRANSPORT
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecureTransport.h>
-#include <Security/SecCertificate.h>
-
-#include "git2/transport.h"
-
-#include "socket_stream.h"
-#include "curl_stream.h"
-
-static int stransport_error(OSStatus ret)
-{
- CFStringRef message;
-
- if (ret == noErr || ret == errSSLClosedGraceful) {
- giterr_clear();
- return 0;
- }
-
-#if !TARGET_OS_IPHONE
- message = SecCopyErrorMessageString(ret, NULL);
- GITERR_CHECK_ALLOC(message);
-
- giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
- CFRelease(message);
-#else
- giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
- GIT_UNUSED(message);
-#endif
-
- return -1;
-}
-
-typedef struct {
- git_stream parent;
- git_stream *io;
- SSLContextRef ctx;
- CFDataRef der_data;
- git_cert_x509 cert_info;
-} stransport_stream;
-
-static int stransport_connect(git_stream *stream)
-{
- stransport_stream *st = (stransport_stream *) stream;
- int error;
- SecTrustRef trust = NULL;
- SecTrustResultType sec_res;
- OSStatus ret;
-
- if ((error = git_stream_connect(st->io)) < 0)
- return error;
-
- ret = SSLHandshake(st->ctx);
- if (ret != errSSLServerAuthCompleted) {
- giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret);
- return -1;
- }
-
- if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
- goto on_error;
-
- if (!trust)
- return GIT_ECERTIFICATE;
-
- if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
- goto on_error;
-
- CFRelease(trust);
-
- if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
- giterr_set(GITERR_SSL, "internal security trust error");
- return -1;
- }
-
- if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
- sec_res == kSecTrustResultFatalTrustFailure)
- return GIT_ECERTIFICATE;
-
- return 0;
-
-on_error:
- if (trust)
- CFRelease(trust);
-
- return stransport_error(ret);
-}
-
-static int stransport_certificate(git_cert **out, git_stream *stream)
-{
- stransport_stream *st = (stransport_stream *) stream;
- SecTrustRef trust = NULL;
- SecCertificateRef sec_cert;
- OSStatus ret;
-
- if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
- return stransport_error(ret);
-
- sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
- st->der_data = SecCertificateCopyData(sec_cert);
- CFRelease(trust);
-
- if (st->der_data == NULL) {
- giterr_set(GITERR_SSL, "retrieved invalid certificate data");
- return -1;
- }
-
- st->cert_info.parent.cert_type = GIT_CERT_X509;
- st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
- st->cert_info.len = CFDataGetLength(st->der_data);
-
- *out = (git_cert *)&st->cert_info;
- return 0;
-}
-
-static int stransport_set_proxy(
- git_stream *stream,
- const git_proxy_options *proxy_opts)
-{
- stransport_stream *st = (stransport_stream *) stream;
-
- return git_stream_set_proxy(st->io, proxy_opts);
-}
-
-/*
- * Contrary to typical network IO callbacks, Secure Transport write callback is
- * expected to write *all* passed data, not just as much as it can, and any
- * other case would be considered a failure.
- *
- * This behavior is actually not specified in the Apple documentation, but is
- * required for things to work correctly (and incidentally, that's also how
- * Apple implements it in its projects at opensource.apple.com).
- *
- * Libgit2 streams happen to already have this very behavior so this is just
- * passthrough.
- */
-static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
-{
- git_stream *io = (git_stream *) conn;
-
- if (git_stream_write(io, data, *len, 0) < 0) {
- return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
- }
-
- return noErr;
-}
-
-static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
-{
- stransport_stream *st = (stransport_stream *) stream;
- size_t data_len, processed;
- OSStatus ret;
-
- GIT_UNUSED(flags);
-
- data_len = len;
- if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
- return stransport_error(ret);
-
- return processed;
-}
-
-/*
- * Contrary to typical network IO callbacks, Secure Transport read callback is
- * expected to read *exactly* the requested number of bytes, not just as much
- * as it can, and any other case would be considered a failure.
- *
- * This behavior is actually not specified in the Apple documentation, but is
- * required for things to work correctly (and incidentally, that's also how
- * Apple implements it in its projects at opensource.apple.com).
- */
-static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
-{
- git_stream *io = (git_stream *) conn;
- OSStatus error = noErr;
- size_t off = 0;
- ssize_t ret;
-
- do {
- ret = git_stream_read(io, data + off, *len - off);
- if (ret < 0) {
- error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */
- break;
- }
- if (ret == 0) {
- error = errSSLClosedGraceful;
- break;
- }
-
- off += ret;
- } while (off < *len);
-
- *len = off;
- return error;
-}
-
-static ssize_t stransport_read(git_stream *stream, void *data, size_t len)
-{
- stransport_stream *st = (stransport_stream *) stream;
- size_t processed;
- OSStatus ret;
-
- if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr)
- return stransport_error(ret);
-
- return processed;
-}
-
-static int stransport_close(git_stream *stream)
-{
- stransport_stream *st = (stransport_stream *) stream;
- OSStatus ret;
-
- ret = SSLClose(st->ctx);
- if (ret != noErr && ret != errSSLClosedGraceful)
- return stransport_error(ret);
-
- return git_stream_close(st->io);
-}
-
-static void stransport_free(git_stream *stream)
-{
- stransport_stream *st = (stransport_stream *) stream;
-
- git_stream_free(st->io);
- CFRelease(st->ctx);
- if (st->der_data)
- CFRelease(st->der_data);
- git__free(st);
-}
-
-int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
-{
- stransport_stream *st;
- int error;
- OSStatus ret;
-
- assert(out && host);
-
- st = git__calloc(1, sizeof(stransport_stream));
- GITERR_CHECK_ALLOC(st);
-
-#ifdef GIT_CURL
- error = git_curl_stream_new(&st->io, host, port);
-#else
- error = git_socket_stream_new(&st->io, host, port);
-#endif
-
- if (error < 0){
- git__free(st);
- return error;
- }
-
- st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
- if (!st->ctx) {
- giterr_set(GITERR_NET, "failed to create SSL context");
- git__free(st);
- return -1;
- }
-
- if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
- (ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
- (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
- (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
- (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
- (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
- CFRelease(st->ctx);
- git__free(st);
- return stransport_error(ret);
- }
-
- st->parent.version = GIT_STREAM_VERSION;
- st->parent.encrypted = 1;
- st->parent.proxy_support = git_stream_supports_proxy(st->io);
- st->parent.connect = stransport_connect;
- st->parent.certificate = stransport_certificate;
- st->parent.set_proxy = stransport_set_proxy;
- st->parent.read = stransport_read;
- st->parent.write = stransport_write;
- st->parent.close = stransport_close;
- st->parent.free = stransport_free;
-
- *out = (git_stream *) st;
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * 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_stransport_stream_h__
-#define INCLUDE_stransport_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/curl.h"
+
+#ifdef GIT_CURL
+
+#include <curl/curl.h>
+
+#include "stream.h"
+#include "git2/transport.h"
+#include "buffer.h"
+#include "global.h"
+#include "vector.h"
+#include "proxy.h"
+
+/* This is for backwards compatibility with curl<7.45.0. */
+#ifndef CURLINFO_ACTIVESOCKET
+# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
+# define GIT_CURL_BADSOCKET -1
+# define git_activesocket_t long
+#else
+# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
+# define git_activesocket_t curl_socket_t
+#endif
+
+typedef struct {
+ git_stream parent;
+ CURL *handle;
+ curl_socket_t socket;
+ char curl_error[CURL_ERROR_SIZE + 1];
+ git_cert_x509 cert_info;
+ git_strarray cert_info_strings;
+ git_proxy_options proxy;
+ git_cred *proxy_cred;
+} curl_stream;
+
+int git_curl_stream_global_init(void)
+{
+ if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
+ giterr_set(GITERR_NET, "could not initialize curl");
+ return -1;
+ }
+
+ /* `curl_global_cleanup` is provided by libcurl */
+ git__on_shutdown(curl_global_cleanup);
+ return 0;
+}
+
+static int seterr_curl(curl_stream *s)
+{
+ giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
+ return -1;
+}
+
+GIT_INLINE(int) error_no_credentials(void)
+{
+ giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
+ return GIT_EAUTH;
+}
+
+static int apply_proxy_creds(curl_stream *s)
+{
+ CURLcode res;
+ git_cred_userpass_plaintext *userpass;
+
+ if (!s->proxy_cred)
+ return GIT_ENOTFOUND;
+
+ userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
+ return seterr_curl(s);
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
+ return seterr_curl(s);
+
+ return 0;
+}
+
+static int ask_and_apply_proxy_creds(curl_stream *s)
+{
+ int error;
+ git_proxy_options *opts = &s->proxy;
+
+ if (!opts->credentials)
+ return error_no_credentials();
+
+ /* TODO: see if PROXYAUTH_AVAIL helps us here */
+ git_cred_free(s->proxy_cred);
+ s->proxy_cred = NULL;
+ giterr_clear();
+ error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
+ if (error == GIT_PASSTHROUGH)
+ return error_no_credentials();
+ if (error < 0) {
+ if (!giterr_last())
+ giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
+ return error;
+ }
+
+ if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
+ giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
+ return -1;
+ }
+
+ return apply_proxy_creds(s);
+}
+
+static int curls_connect(git_stream *stream)
+{
+ curl_stream *s = (curl_stream *) stream;
+ git_activesocket_t sockextr;
+ long connect_last = 0;
+ int failed_cert = 0, error;
+ bool retry_connect;
+ CURLcode res;
+
+ /* Apply any credentials we've already established */
+ error = apply_proxy_creds(s);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return seterr_curl(s);
+
+ do {
+ retry_connect = 0;
+ res = curl_easy_perform(s->handle);
+
+ curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
+
+ /* HTTP 407 Proxy Authentication Required */
+ if (connect_last == 407) {
+ if ((error = ask_and_apply_proxy_creds(s)) < 0)
+ return error;
+
+ retry_connect = true;
+ }
+ } while (retry_connect);
+
+ if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
+ return seterr_curl(s);
+ if (res == CURLE_PEER_FAILED_VERIFICATION)
+ failed_cert = 1;
+
+ if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
+ return seterr_curl(s);
+ }
+
+ if (sockextr == GIT_CURL_BADSOCKET) {
+ giterr_set(GITERR_NET, "curl socket is no longer valid");
+ return -1;
+ }
+
+ s->socket = sockextr;
+
+ if (s->parent.encrypted && failed_cert)
+ return GIT_ECERTIFICATE;
+
+ return 0;
+}
+
+static int curls_certificate(git_cert **out, git_stream *stream)
+{
+ int error;
+ CURLcode res;
+ struct curl_slist *slist;
+ struct curl_certinfo *certinfo;
+ git_vector strings = GIT_VECTOR_INIT;
+ curl_stream *s = (curl_stream *) stream;
+
+ if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
+ return seterr_curl(s);
+
+ /* No information is available, can happen with SecureTransport */
+ if (certinfo->num_of_certs == 0) {
+ s->cert_info.parent.cert_type = GIT_CERT_NONE;
+ s->cert_info.data = NULL;
+ s->cert_info.len = 0;
+ return 0;
+ }
+
+ if ((error = git_vector_init(&strings, 8, NULL)) < 0)
+ return error;
+
+ 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.parent.cert_type = GIT_CERT_STRARRAY;
+ s->cert_info.data = &s->cert_info_strings;
+ s->cert_info.len = strings.length;
+
+ *out = &s->cert_info.parent;
+
+ return 0;
+}
+
+static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
+{
+ int error;
+ CURLcode res;
+ curl_stream *s = (curl_stream *) stream;
+
+ git_proxy_options_clear(&s->proxy);
+ if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
+ return error;
+
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
+ return seterr_curl(s);
+
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
+ return seterr_curl(s);
+
+ return 0;
+}
+
+static int wait_for(curl_socket_t fd, bool reading)
+{
+ int ret;
+ fd_set infd, outfd, errfd;
+
+ FD_ZERO(&infd);
+ FD_ZERO(&outfd);
+ FD_ZERO(&errfd);
+
+ assert(fd >= 0);
+ FD_SET(fd, &errfd);
+ if (reading)
+ FD_SET(fd, &infd);
+ else
+ FD_SET(fd, &outfd);
+
+ if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
+ giterr_set(GITERR_OS, "error in select");
+ return -1;
+ }
+
+ return 0;
+}
+
+static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+ int error;
+ size_t off = 0, sent;
+ CURLcode res;
+ curl_stream *s = (curl_stream *) stream;
+
+ GIT_UNUSED(flags);
+
+ do {
+ if ((error = wait_for(s->socket, false)) < 0)
+ return error;
+
+ res = curl_easy_send(s->handle, data + off, len - off, &sent);
+ if (res == CURLE_OK)
+ off += sent;
+ } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
+
+ if (res != CURLE_OK)
+ return seterr_curl(s);
+
+ return len;
+}
+
+static ssize_t curls_read(git_stream *stream, void *data, size_t len)
+{
+ int error;
+ size_t read;
+ CURLcode res;
+ curl_stream *s = (curl_stream *) stream;
+
+ do {
+ if ((error = wait_for(s->socket, true)) < 0)
+ return error;
+
+ res = curl_easy_recv(s->handle, data, len, &read);
+ } while (res == CURLE_AGAIN);
+
+ if (res != CURLE_OK)
+ return seterr_curl(s);
+
+ return read;
+}
+
+static int curls_close(git_stream *stream)
+{
+ curl_stream *s = (curl_stream *) stream;
+
+ if (!s->handle)
+ return 0;
+
+ curl_easy_cleanup(s->handle);
+ s->handle = NULL;
+ s->socket = 0;
+
+ return 0;
+}
+
+static void curls_free(git_stream *stream)
+{
+ curl_stream *s = (curl_stream *) stream;
+
+ curls_close(stream);
+ git_strarray_free(&s->cert_info_strings);
+ git_proxy_options_clear(&s->proxy);
+ git_cred_free(s->proxy_cred);
+ git__free(s);
+}
+
+int git_curl_stream_new(git_stream **out, const char *host, const char *port)
+{
+ curl_stream *st;
+ CURL *handle;
+ int iport = 0, error;
+
+ st = git__calloc(1, sizeof(curl_stream));
+ GITERR_CHECK_ALLOC(st);
+
+ 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) {
+ git__free(st);
+ return error;
+ }
+
+ curl_easy_setopt(handle, CURLOPT_URL, host);
+ curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
+ curl_easy_setopt(handle, CURLOPT_PORT, iport);
+ curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
+ 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); */
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 0; /* we don't encrypt ourselves */
+ st->parent.proxy_support = 1;
+ st->parent.connect = curls_connect;
+ st->parent.certificate = curls_certificate;
+ st->parent.set_proxy = curls_set_proxy;
+ st->parent.read = curls_read;
+ st->parent.write = curls_write;
+ st->parent.close = curls_close;
+ st->parent.free = curls_free;
+ st->handle = handle;
+
+ *out = (git_stream *) st;
+ return 0;
+}
+
+#else
+
+#include "stream.h"
+
+int git_curl_stream_global_init(void)
+{
+ return 0;
+}
+
+int git_curl_stream_new(git_stream **out, const char *host, const char *port)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
+ giterr_set(GITERR_NET, "curl is not supported in this version");
+ return -1;
+}
+
+
+#endif
--- /dev/null
+/*
+ * 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_streams_curl_h__
+#define INCLUDE_streams_curl_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+extern int git_curl_stream_global_init(void);
+extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/openssl.h"
+
+#ifdef GIT_OPENSSL
+
+#include <ctype.h>
+
+#include "global.h"
+#include "posix.h"
+#include "stream.h"
+#include "streams/socket.h"
+#include "netops.h"
+#include "git2/transport.h"
+#include "git2/sys/openssl.h"
+
+#ifdef GIT_CURL
+# include "streams/curl.h"
+#endif
+
+#ifndef GIT_WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+#include <openssl/bio.h>
+
+SSL_CTX *git__ssl_ctx;
+
+#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
+
+#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
+
+static git_mutex *openssl_locks;
+
+static void openssl_locking_function(
+ int mode, int n, const char *file, int line)
+{
+ int lock;
+
+ 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[i]);
+ git__free(openssl_locks);
+}
+
+#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+static BIO_METHOD *git_stream_bio_method;
+static int init_bio_method(void);
+
+/**
+ * This function aims to clean-up the SSL context which
+ * we allocated.
+ */
+static void shutdown_ssl(void)
+{
+ if (git_stream_bio_method) {
+ BIO_meth_free(git_stream_bio_method);
+ git_stream_bio_method = NULL;
+ }
+
+ if (git__ssl_ctx) {
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ }
+}
+
+int git_openssl_stream_global_init(void)
+{
+#ifdef GIT_OPENSSL
+ long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+ const char *ciphers = git_libgit2__ssl_ciphers();
+
+ /* Older OpenSSL and MacOS OpenSSL doesn't have this */
+#ifdef SSL_OP_NO_COMPRESSION
+ ssl_opts |= SSL_OP_NO_COMPRESSION;
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+#else
+ OPENSSL_init_ssl(0, NULL);
+#endif
+
+ /*
+ * 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;
+ return -1;
+ }
+
+ if (!ciphers) {
+ ciphers = GIT_SSL_DEFAULT_CIPHERS;
+ }
+
+ if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ return -1;
+ }
+
+ if (init_bio_method() < 0) {
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ return -1;
+ }
+
+#endif
+
+ git__on_shutdown(shutdown_ssl);
+
+ return 0;
+}
+
+#if defined(GIT_THREADS)
+static void threadid_cb(CRYPTO_THREADID *threadid)
+{
+ CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
+}
+#endif
+
+int git_openssl_set_locking(void)
+{
+#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
+ int num_locks, i;
+
+ CRYPTO_THREADID_set_callback(threadid_cb);
+
+ 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;
+#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
+ return 0;
+#else
+ giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
+ return -1;
+#endif
+}
+
+
+static int bio_create(BIO *b)
+{
+ BIO_set_init(b, 1);
+ BIO_set_data(b, NULL);
+
+ return 1;
+}
+
+static int bio_destroy(BIO *b)
+{
+ if (!b)
+ return 0;
+
+ BIO_set_data(b, NULL);
+
+ return 1;
+}
+
+static int bio_read(BIO *b, char *buf, int len)
+{
+ git_stream *io = (git_stream *) BIO_get_data(b);
+
+ return (int) git_stream_read(io, buf, len);
+}
+
+static int bio_write(BIO *b, const char *buf, int len)
+{
+ git_stream *io = (git_stream *) BIO_get_data(b);
+
+ return (int) git_stream_write(io, buf, len, 0);
+}
+
+static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+ GIT_UNUSED(b);
+ GIT_UNUSED(num);
+ GIT_UNUSED(ptr);
+
+ if (cmd == BIO_CTRL_FLUSH)
+ return 1;
+
+ return 0;
+}
+
+static int bio_gets(BIO *b, char *buf, int len)
+{
+ GIT_UNUSED(b);
+ GIT_UNUSED(buf);
+ GIT_UNUSED(len);
+ return -1;
+}
+
+static int bio_puts(BIO *b, const char *str)
+{
+ return bio_write(b, str, strlen(str));
+}
+
+static int init_bio_method(void)
+{
+ /* Set up the BIO_METHOD we use for wrapping our own stream implementations */
+ git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
+ GITERR_CHECK_ALLOC(git_stream_bio_method);
+
+ BIO_meth_set_write(git_stream_bio_method, bio_write);
+ BIO_meth_set_read(git_stream_bio_method, bio_read);
+ BIO_meth_set_puts(git_stream_bio_method, bio_puts);
+ BIO_meth_set_gets(git_stream_bio_method, bio_gets);
+ BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
+ BIO_meth_set_create(git_stream_bio_method, bio_create);
+ BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);
+
+ return 0;
+}
+
+static int ssl_set_error(SSL *ssl, int error)
+{
+ int err;
+ unsigned long e;
+
+ err = SSL_get_error(ssl, error);
+
+ assert(err != SSL_ERROR_WANT_READ);
+ assert(err != SSL_ERROR_WANT_WRITE);
+
+ switch (err) {
+ case SSL_ERROR_WANT_CONNECT:
+ case SSL_ERROR_WANT_ACCEPT:
+ giterr_set(GITERR_NET, "SSL error: connection failure");
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ giterr_set(GITERR_NET, "SSL error: x509 error");
+ break;
+ case SSL_ERROR_SYSCALL:
+ e = ERR_get_error();
+ if (e > 0) {
+ char errmsg[256];
+ ERR_error_string_n(e, errmsg, sizeof(errmsg));
+ giterr_set(GITERR_NET, "SSL error: %s", errmsg);
+ break;
+ } else if (error < 0) {
+ giterr_set(GITERR_OS, "SSL error: syscall failure");
+ break;
+ }
+ giterr_set(GITERR_NET, "SSL error: received early EOF");
+ return GIT_EEOF;
+ break;
+ case SSL_ERROR_SSL:
+ {
+ char errmsg[256];
+ e = ERR_get_error();
+ ERR_error_string_n(e, errmsg, sizeof(errmsg));
+ giterr_set(GITERR_NET, "SSL error: %s", errmsg);
+ break;
+ }
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ giterr_set(GITERR_NET, "SSL error: unknown error");
+ break;
+ }
+ return -1;
+}
+
+static int ssl_teardown(SSL *ssl)
+{
+ int ret;
+
+ ret = SSL_shutdown(ssl);
+ if (ret < 0)
+ ret = ssl_set_error(ssl, ret);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int check_host_name(const char *name, const char *host)
+{
+ if (!strcasecmp(name, host))
+ return 0;
+
+ if (gitno__match_host(name, host) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int verify_server_cert(SSL *ssl, const char *host)
+{
+ X509 *cert = NULL;
+ X509_NAME *peer_name;
+ ASN1_STRING *str;
+ unsigned char *peer_cn = NULL;
+ int matched = -1, type = GEN_DNS;
+ GENERAL_NAMES *alts;
+ struct in6_addr addr6;
+ struct in_addr addr4;
+ void *addr = NULL;
+ int i = -1, j, error = 0;
+
+ if (SSL_get_verify_result(ssl) != X509_V_OK) {
+ giterr_set(GITERR_SSL, "the SSL certificate is invalid");
+ return GIT_ECERTIFICATE;
+ }
+
+ /* Try to parse the host as an IP address to see if it is */
+ if (p_inet_pton(AF_INET, host, &addr4)) {
+ type = GEN_IPADD;
+ addr = &addr4;
+ } else {
+ if (p_inet_pton(AF_INET6, host, &addr6)) {
+ type = GEN_IPADD;
+ addr = &addr6;
+ }
+ }
+
+
+ cert = SSL_get_peer_certificate(ssl);
+ if (!cert) {
+ error = -1;
+ giterr_set(GITERR_SSL, "the server did not provide a certificate");
+ goto cleanup;
+ }
+
+ /* Check the alternative names */
+ alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+ if (alts) {
+ int num;
+
+ num = sk_GENERAL_NAME_num(alts);
+ for (i = 0; i < num && matched != 1; i++) {
+ const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
+ const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
+ size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
+
+ /* Skip any names of a type we're not looking for */
+ if (gn->type != type)
+ continue;
+
+ if (type == GEN_DNS) {
+ /* If it contains embedded NULs, don't even try */
+ if (memchr(name, '\0', namelen))
+ continue;
+
+ if (check_host_name(name, host) < 0)
+ matched = 0;
+ else
+ matched = 1;
+ } else if (type == GEN_IPADD) {
+ /* Here name isn't so much a name but a binary representation of the IP */
+ matched = addr && !!memcmp(name, addr, namelen);
+ }
+ }
+ }
+ GENERAL_NAMES_free(alts);
+
+ if (matched == 0)
+ goto cert_fail_name;
+
+ if (matched == 1) {
+ goto cleanup;
+ }
+
+ /* If no alternative names are available, check the common name */
+ peer_name = X509_get_subject_name(cert);
+ if (peer_name == NULL)
+ goto on_error;
+
+ if (peer_name) {
+ /* Get the index of the last CN entry */
+ while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
+ i = j;
+ }
+
+ if (i < 0)
+ goto on_error;
+
+ str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
+ if (str == NULL)
+ goto on_error;
+
+ /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
+ if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
+ int size = ASN1_STRING_length(str);
+
+ if (size > 0) {
+ peer_cn = OPENSSL_malloc(size + 1);
+ GITERR_CHECK_ALLOC(peer_cn);
+ memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
+ peer_cn[size] = '\0';
+ } else {
+ goto cert_fail_name;
+ }
+ } else {
+ int size = ASN1_STRING_to_UTF8(&peer_cn, str);
+ GITERR_CHECK_ALLOC(peer_cn);
+ if (memchr(peer_cn, '\0', size))
+ goto cert_fail_name;
+ }
+
+ if (check_host_name((char *)peer_cn, host) < 0)
+ goto cert_fail_name;
+
+ goto cleanup;
+
+cert_fail_name:
+ error = GIT_ECERTIFICATE;
+ giterr_set(GITERR_SSL, "hostname does not match certificate");
+ goto cleanup;
+
+on_error:
+ error = ssl_set_error(ssl, 0);
+ goto cleanup;
+
+cleanup:
+ X509_free(cert);
+ OPENSSL_free(peer_cn);
+ return error;
+}
+
+typedef struct {
+ git_stream parent;
+ git_stream *io;
+ bool connected;
+ char *host;
+ SSL *ssl;
+ git_cert_x509 cert_info;
+} openssl_stream;
+
+int openssl_close(git_stream *stream);
+
+int openssl_connect(git_stream *stream)
+{
+ int ret;
+ BIO *bio;
+ openssl_stream *st = (openssl_stream *) stream;
+
+ if ((ret = git_stream_connect(st->io)) < 0)
+ return ret;
+
+ st->connected = true;
+
+ bio = BIO_new(git_stream_bio_method);
+ GITERR_CHECK_ALLOC(bio);
+
+ BIO_set_data(bio, st->io);
+ SSL_set_bio(st->ssl, bio, bio);
+
+ /* specify the host in case SNI is needed */
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ SSL_set_tlsext_host_name(st->ssl, st->host);
+#endif
+
+ if ((ret = SSL_connect(st->ssl)) <= 0)
+ return ssl_set_error(st->ssl, ret);
+
+ return verify_server_cert(st->ssl, st->host);
+}
+
+int openssl_certificate(git_cert **out, git_stream *stream)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+ int len;
+ X509 *cert = SSL_get_peer_certificate(st->ssl);
+ unsigned char *guard, *encoded_cert;
+
+ /* Retrieve the length of the certificate first */
+ len = i2d_X509(cert, NULL);
+ if (len < 0) {
+ giterr_set(GITERR_NET, "failed to retrieve certificate information");
+ return -1;
+ }
+
+ encoded_cert = git__malloc(len);
+ GITERR_CHECK_ALLOC(encoded_cert);
+ /* i2d_X509 makes 'guard' point to just after the data */
+ guard = encoded_cert;
+
+ len = i2d_X509(cert, &guard);
+ if (len < 0) {
+ git__free(encoded_cert);
+ giterr_set(GITERR_NET, "failed to retrieve certificate information");
+ return -1;
+ }
+
+ st->cert_info.parent.cert_type = GIT_CERT_X509;
+ st->cert_info.data = encoded_cert;
+ st->cert_info.len = len;
+
+ *out = &st->cert_info.parent;
+
+ return 0;
+}
+
+static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+
+ return git_stream_set_proxy(st->io, proxy_opts);
+}
+
+ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+ int ret;
+
+ GIT_UNUSED(flags);
+
+ if ((ret = SSL_write(st->ssl, data, len)) <= 0) {
+ return ssl_set_error(st->ssl, ret);
+ }
+
+ return ret;
+}
+
+ssize_t openssl_read(git_stream *stream, void *data, size_t len)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+ int ret;
+
+ if ((ret = SSL_read(st->ssl, data, len)) <= 0)
+ return ssl_set_error(st->ssl, ret);
+
+ return ret;
+}
+
+int openssl_close(git_stream *stream)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+ int ret;
+
+ if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
+ return -1;
+
+ st->connected = false;
+
+ return git_stream_close(st->io);
+}
+
+void openssl_free(git_stream *stream)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+
+ SSL_free(st->ssl);
+ git__free(st->host);
+ git__free(st->cert_info.data);
+ git_stream_free(st->io);
+ git__free(st);
+}
+
+int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
+{
+ int error;
+ openssl_stream *st;
+
+ st = git__calloc(1, sizeof(openssl_stream));
+ GITERR_CHECK_ALLOC(st);
+
+ st->io = NULL;
+#ifdef GIT_CURL
+ error = git_curl_stream_new(&st->io, host, port);
+#else
+ error = git_socket_stream_new(&st->io, host, port);
+#endif
+
+ if (error < 0)
+ goto out_err;
+
+ st->ssl = SSL_new(git__ssl_ctx);
+ if (st->ssl == NULL) {
+ giterr_set(GITERR_SSL, "failed to create ssl object");
+ error = -1;
+ goto out_err;
+ }
+
+ st->host = git__strdup(host);
+ GITERR_CHECK_ALLOC(st->host);
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
+ st->parent.connect = openssl_connect;
+ st->parent.certificate = openssl_certificate;
+ st->parent.set_proxy = openssl_set_proxy;
+ st->parent.read = openssl_read;
+ st->parent.write = openssl_write;
+ st->parent.close = openssl_close;
+ st->parent.free = openssl_free;
+
+ *out = (git_stream *) st;
+ return 0;
+
+out_err:
+ git_stream_free(st->io);
+ git__free(st);
+
+ return error;
+}
+
+int git_openssl__set_cert_location(const char *file, const char *path)
+{
+ if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) {
+ char errmsg[256];
+
+ ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg));
+ giterr_set(GITERR_SSL, "OpenSSL error: failed to load certificates: %s",
+ errmsg);
+
+ return -1;
+ }
+ return 0;
+}
+
+#else
+
+#include "stream.h"
+#include "git2/sys/openssl.h"
+
+int git_openssl_stream_global_init(void)
+{
+ return 0;
+}
+
+int git_openssl_set_locking(void)
+{
+ giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
+ return -1;
+}
+
+int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
+ giterr_set(GITERR_SSL, "openssl is not supported in this version");
+ return -1;
+}
+
+int git_openssl__set_cert_location(const char *file, const char *path)
+{
+ GIT_UNUSED(file);
+ GIT_UNUSED(path);
+
+ giterr_set(GITERR_SSL, "openssl is not supported in this version");
+ return -1;
+}
+
+#endif
--- /dev/null
+/*
+ * 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_streams_openssl_h__
+#define INCLUDE_streams_openssl_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+extern int git_openssl_stream_global_init(void);
+
+extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
+
+extern int git_openssl__set_cert_location(const char *file, const char *path);
+
+/*
+ * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
+ * which do not exist in previous versions. We define these inline functions so
+ * we can program against the interface instead of littering the implementation
+ * with ifdefs.
+ */
+#ifdef GIT_OPENSSL
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+
+
+
+# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+
+GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name)
+{
+ BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
+ if (!meth) {
+ return NULL;
+ }
+
+ meth->type = type;
+ meth->name = name;
+
+ return meth;
+}
+
+GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom)
+{
+ git__free(biom);
+}
+
+GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
+{
+ biom->bwrite = write;
+ return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
+{
+ biom->bread = read;
+ return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
+{
+ biom->bputs = puts;
+ return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
+
+{
+ biom->bgets = gets;
+ return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
+{
+ biom->ctrl = ctrl;
+ return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
+{
+ biom->create = create;
+ return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
+{
+ biom->destroy = destroy;
+ return 1;
+}
+
+GIT_INLINE(int) BIO_get_new_index(void)
+{
+ /* This exists as of 1.1 so before we'd just have 0 */
+ return 0;
+}
+
+GIT_INLINE(void) BIO_set_init(BIO *b, int init)
+{
+ b->init = init;
+}
+
+GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr)
+{
+ a->ptr = ptr;
+}
+
+GIT_INLINE(void*) BIO_get_data(BIO *a)
+{
+ return a->ptr;
+}
+
+GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+ return ASN1_STRING_data((ASN1_STRING *)x);
+}
+
+# endif // OpenSSL < 1.1
+#endif // GIT_OPENSSL
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/socket.h"
+
+#include "posix.h"
+#include "netops.h"
+#include "stream.h"
+
+#ifndef _WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <sys/time.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#else
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# ifdef _MSC_VER
+# pragma comment(lib, "ws2_32")
+# endif
+#endif
+
+#ifdef GIT_WIN32
+static void net_set_error(const char *str)
+{
+ int error = WSAGetLastError();
+ char * win32_error = git_win32_get_error_message(error);
+
+ if (win32_error) {
+ giterr_set(GITERR_NET, "%s: %s", str, win32_error);
+ git__free(win32_error);
+ } else {
+ giterr_set(GITERR_NET, str);
+ }
+}
+#else
+static void net_set_error(const char *str)
+{
+ giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
+}
+#endif
+
+static int close_socket(GIT_SOCKET s)
+{
+ if (s == INVALID_SOCKET)
+ return 0;
+
+#ifdef GIT_WIN32
+ if (SOCKET_ERROR == closesocket(s))
+ return -1;
+
+ if (0 != WSACleanup()) {
+ giterr_set(GITERR_OS, "winsock cleanup failed");
+ return -1;
+ }
+
+ return 0;
+#else
+ return close(s);
+#endif
+
+}
+
+int socket_connect(git_stream *stream)
+{
+ struct addrinfo *info = NULL, *p;
+ struct addrinfo hints;
+ git_socket_stream *st = (git_socket_stream *) stream;
+ GIT_SOCKET s = INVALID_SOCKET;
+ int ret;
+
+#ifdef GIT_WIN32
+ /* on win32, the WSA context needs to be initialized
+ * before any socket calls can be performed */
+ WSADATA wsd;
+
+ if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+ giterr_set(GITERR_OS, "winsock init failed");
+ return -1;
+ }
+
+ if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
+ WSACleanup();
+ giterr_set(GITERR_OS, "winsock init failed");
+ return -1;
+ }
+#endif
+
+ memset(&hints, 0x0, sizeof(struct addrinfo));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC;
+
+ if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
+ giterr_set(GITERR_NET,
+ "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
+ return -1;
+ }
+
+ for (p = info; p != NULL; p = p->ai_next) {
+ s = socket(p->ai_family, p->ai_socktype | SOCK_CLOEXEC, p->ai_protocol);
+
+ if (s == INVALID_SOCKET)
+ continue;
+
+ if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
+ break;
+
+ /* If we can't connect, try the next one */
+ close_socket(s);
+ s = INVALID_SOCKET;
+ }
+
+ /* Oops, we couldn't connect to any address */
+ if (s == INVALID_SOCKET && p == NULL) {
+ giterr_set(GITERR_OS, "failed to connect to %s", st->host);
+ p_freeaddrinfo(info);
+ return -1;
+ }
+
+ st->s = s;
+ p_freeaddrinfo(info);
+ return 0;
+}
+
+ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+ ssize_t ret;
+ size_t off = 0;
+ git_socket_stream *st = (git_socket_stream *) stream;
+
+ while (off < len) {
+ errno = 0;
+ ret = p_send(st->s, data + off, len - off, flags);
+ if (ret < 0) {
+ net_set_error("Error sending data");
+ return -1;
+ }
+
+ off += ret;
+ }
+
+ return off;
+}
+
+ssize_t socket_read(git_stream *stream, void *data, size_t len)
+{
+ ssize_t ret;
+ git_socket_stream *st = (git_socket_stream *) stream;
+
+ if ((ret = p_recv(st->s, data, len, 0)) < 0)
+ net_set_error("Error receiving socket data");
+
+ return ret;
+}
+
+int socket_close(git_stream *stream)
+{
+ git_socket_stream *st = (git_socket_stream *) stream;
+ int error;
+
+ error = close_socket(st->s);
+ st->s = INVALID_SOCKET;
+
+ return error;
+}
+
+void socket_free(git_stream *stream)
+{
+ git_socket_stream *st = (git_socket_stream *) stream;
+
+ git__free(st->host);
+ git__free(st->port);
+ git__free(st);
+}
+
+int git_socket_stream_new(git_stream **out, const char *host, const char *port)
+{
+ git_socket_stream *st;
+
+ assert(out && host);
+
+ st = git__calloc(1, sizeof(git_socket_stream));
+ GITERR_CHECK_ALLOC(st);
+
+ st->host = git__strdup(host);
+ GITERR_CHECK_ALLOC(st->host);
+
+ if (port) {
+ st->port = git__strdup(port);
+ GITERR_CHECK_ALLOC(st->port);
+ }
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.connect = socket_connect;
+ st->parent.write = socket_write;
+ st->parent.read = socket_read;
+ st->parent.close = socket_close;
+ st->parent.free = socket_free;
+ st->s = INVALID_SOCKET;
+
+ *out = (git_stream *) st;
+ return 0;
+}
--- /dev/null
+/*
+ * 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_streams_socket_h__
+#define INCLUDE_streams_socket_h__
+
+#include "common.h"
+
+#include "netops.h"
+
+typedef struct {
+ git_stream parent;
+ char *host;
+ char *port;
+ GIT_SOCKET s;
+} git_socket_stream;
+
+extern int git_socket_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/stransport.h"
+
+#ifdef GIT_SECURE_TRANSPORT
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/SecureTransport.h>
+#include <Security/SecCertificate.h>
+
+#include "git2/transport.h"
+
+#include "streams/socket.h"
+#include "streams/curl.h"
+
+static int stransport_error(OSStatus ret)
+{
+ CFStringRef message;
+
+ if (ret == noErr || ret == errSSLClosedGraceful) {
+ giterr_clear();
+ return 0;
+ }
+
+#if !TARGET_OS_IPHONE
+ message = SecCopyErrorMessageString(ret, NULL);
+ GITERR_CHECK_ALLOC(message);
+
+ giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
+ CFRelease(message);
+#else
+ giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
+ GIT_UNUSED(message);
+#endif
+
+ return -1;
+}
+
+typedef struct {
+ git_stream parent;
+ git_stream *io;
+ SSLContextRef ctx;
+ CFDataRef der_data;
+ git_cert_x509 cert_info;
+} stransport_stream;
+
+static int stransport_connect(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ int error;
+ SecTrustRef trust = NULL;
+ SecTrustResultType sec_res;
+ OSStatus ret;
+
+ if ((error = git_stream_connect(st->io)) < 0)
+ return error;
+
+ ret = SSLHandshake(st->ctx);
+ if (ret != errSSLServerAuthCompleted) {
+ giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret);
+ return -1;
+ }
+
+ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+ goto on_error;
+
+ if (!trust)
+ return GIT_ECERTIFICATE;
+
+ if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
+ goto on_error;
+
+ CFRelease(trust);
+
+ if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
+ giterr_set(GITERR_SSL, "internal security trust error");
+ return -1;
+ }
+
+ if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
+ sec_res == kSecTrustResultFatalTrustFailure) {
+ giterr_set(GITERR_SSL, "untrusted connection error");
+ return GIT_ECERTIFICATE;
+ }
+
+ return 0;
+
+on_error:
+ if (trust)
+ CFRelease(trust);
+
+ return stransport_error(ret);
+}
+
+static int stransport_certificate(git_cert **out, git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ SecTrustRef trust = NULL;
+ SecCertificateRef sec_cert;
+ OSStatus ret;
+
+ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+ return stransport_error(ret);
+
+ sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
+ st->der_data = SecCertificateCopyData(sec_cert);
+ CFRelease(trust);
+
+ if (st->der_data == NULL) {
+ giterr_set(GITERR_SSL, "retrieved invalid certificate data");
+ return -1;
+ }
+
+ st->cert_info.parent.cert_type = GIT_CERT_X509;
+ st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
+ st->cert_info.len = CFDataGetLength(st->der_data);
+
+ *out = (git_cert *)&st->cert_info;
+ return 0;
+}
+
+static int stransport_set_proxy(
+ git_stream *stream,
+ const git_proxy_options *proxy_opts)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+
+ return git_stream_set_proxy(st->io, proxy_opts);
+}
+
+/*
+ * Contrary to typical network IO callbacks, Secure Transport write callback is
+ * expected to write *all* passed data, not just as much as it can, and any
+ * other case would be considered a failure.
+ *
+ * This behavior is actually not specified in the Apple documentation, but is
+ * required for things to work correctly (and incidentally, that's also how
+ * Apple implements it in its projects at opensource.apple.com).
+ *
+ * Libgit2 streams happen to already have this very behavior so this is just
+ * passthrough.
+ */
+static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
+{
+ git_stream *io = (git_stream *) conn;
+
+ if (git_stream_write(io, data, *len, 0) < 0) {
+ return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
+ }
+
+ return noErr;
+}
+
+static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ size_t data_len, processed;
+ OSStatus ret;
+
+ GIT_UNUSED(flags);
+
+ data_len = len;
+ if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
+ return stransport_error(ret);
+
+ return processed;
+}
+
+/*
+ * Contrary to typical network IO callbacks, Secure Transport read callback is
+ * expected to read *exactly* the requested number of bytes, not just as much
+ * as it can, and any other case would be considered a failure.
+ *
+ * This behavior is actually not specified in the Apple documentation, but is
+ * required for things to work correctly (and incidentally, that's also how
+ * Apple implements it in its projects at opensource.apple.com).
+ */
+static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
+{
+ git_stream *io = (git_stream *) conn;
+ OSStatus error = noErr;
+ size_t off = 0;
+ ssize_t ret;
+
+ do {
+ ret = git_stream_read(io, data + off, *len - off);
+ if (ret < 0) {
+ error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */
+ break;
+ }
+ if (ret == 0) {
+ error = errSSLClosedGraceful;
+ break;
+ }
+
+ off += ret;
+ } while (off < *len);
+
+ *len = off;
+ return error;
+}
+
+static ssize_t stransport_read(git_stream *stream, void *data, size_t len)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ size_t processed;
+ OSStatus ret;
+
+ if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr)
+ return stransport_error(ret);
+
+ return processed;
+}
+
+static int stransport_close(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ OSStatus ret;
+
+ ret = SSLClose(st->ctx);
+ if (ret != noErr && ret != errSSLClosedGraceful)
+ return stransport_error(ret);
+
+ return git_stream_close(st->io);
+}
+
+static void stransport_free(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+
+ git_stream_free(st->io);
+ CFRelease(st->ctx);
+ if (st->der_data)
+ CFRelease(st->der_data);
+ git__free(st);
+}
+
+int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
+{
+ stransport_stream *st;
+ int error;
+ OSStatus ret;
+
+ assert(out && host);
+
+ st = git__calloc(1, sizeof(stransport_stream));
+ GITERR_CHECK_ALLOC(st);
+
+#ifdef GIT_CURL
+ error = git_curl_stream_new(&st->io, host, port);
+#else
+ error = git_socket_stream_new(&st->io, host, port);
+#endif
+
+ if (error < 0){
+ git__free(st);
+ return error;
+ }
+
+ st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+ if (!st->ctx) {
+ giterr_set(GITERR_NET, "failed to create SSL context");
+ git__free(st);
+ return -1;
+ }
+
+ if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
+ (ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
+ (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
+ (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
+ (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
+ (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
+ CFRelease(st->ctx);
+ git__free(st);
+ return stransport_error(ret);
+ }
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
+ st->parent.connect = stransport_connect;
+ st->parent.certificate = stransport_certificate;
+ st->parent.set_proxy = stransport_set_proxy;
+ st->parent.read = stransport_read;
+ st->parent.write = stransport_write;
+ st->parent.close = stransport_close;
+ st->parent.free = stransport_free;
+
+ *out = (git_stream *) st;
+ return 0;
+}
+
+#endif
--- /dev/null
+/*
+ * 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_streams_stransport_h__
+#define INCLUDE_streams_stransport_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/tls.h"
+
+#include "git2/errors.h"
+
+#include "streams/openssl.h"
+#include "streams/stransport.h"
+
+static git_stream_cb tls_ctor;
+
+int git_stream_register_tls(git_stream_cb ctor)
+{
+ tls_ctor = ctor;
+
+ return 0;
+}
+
+int git_tls_stream_new(git_stream **out, const char *host, const char *port)
+{
+
+ if (tls_ctor)
+ return tls_ctor(out, host, port);
+
+#ifdef GIT_SECURE_TRANSPORT
+ return git_stransport_stream_new(out, host, port);
+#elif defined(GIT_OPENSSL)
+ return git_openssl_stream_new(out, host, port);
+#else
+ GIT_UNUSED(out);
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
+ giterr_set(GITERR_SSL, "there is no TLS stream available");
+ return -1;
+#endif
+}
--- /dev/null
+/*
+ * 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_streams_tls_h__
+#define INCLUDE_streams_tls_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+/**
+ * Create a TLS stream with the most appropriate backend available for
+ * the current platform.
+ *
+ * This allows us to ask for a SecureTransport or OpenSSL stream
+ * according to being on general Unix vs OS X.
+ */
+extern int git_tls_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "submodule.h"
+
#include "git2/config.h"
#include "git2/sys/config.h"
#include "git2/types.h"
#include "config_file.h"
#include "config.h"
#include "repository.h"
-#include "submodule.h"
#include "tree.h"
#include "iterator.h"
#include "path.h"
assert(repo && name);
+ if (repo->is_bare) {
+ giterr_set(GITERR_SUBMODULE, "cannot get submodules without a working tree");
+ return -1;
+ }
+
if (repo->submodule_cache != NULL) {
khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name);
if (git_strmap_valid_index(repo->submodule_cache, pos)) {
int error;
size_t i;
+ if (repo->is_bare) {
+ giterr_set(GITERR_SUBMODULE, "cannot get submodules without a working tree");
+ return -1;
+ }
+
if ((error = git_strmap_alloc(&submodules)) < 0)
return error;
if (git_config_file__ondisk(&mods, path.ptr) < 0)
mods = NULL;
/* open should only fail here if the file is malformed */
- else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
+ else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) {
git_config_file_free(mods);
mods = NULL;
}
#ifndef INCLUDE_submodule_h__
#define INCLUDE_submodule_h__
+#include "common.h"
+
#include "git2/submodule.h"
#include "git2/repository.h"
#include "fileops.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "sysdir.h"
+
#include "global.h"
#include "buffer.h"
#include "path.h"
#include <ctype.h>
#if GIT_WIN32
#include "win32/findfile.h"
+#else
+#include <unistd.h>
+#include <pwd.h>
#endif
static int git_sysdir_guess_programdata_dirs(git_buf *out)
#endif
}
+#ifndef GIT_WIN32
+static int get_passwd_home(git_buf *out, uid_t uid)
+{
+ struct passwd pwd, *pwdptr;
+ char *buf = NULL;
+ long buflen;
+ int error;
+
+ assert(out);
+
+ if ((buflen = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1)
+ buflen = 1024;
+
+ do {
+ buf = git__realloc(buf, buflen);
+ error = getpwuid_r(uid, &pwd, buf, buflen, &pwdptr);
+ buflen *= 2;
+ } while (error == ERANGE && buflen <= 8192);
+
+ if (error) {
+ giterr_set(GITERR_OS, "failed to get passwd entry");
+ goto out;
+ }
+
+ if (!pwdptr) {
+ giterr_set(GITERR_OS, "no passwd entry found for user");
+ goto out;
+ }
+
+ if ((error = git_buf_puts(out, pwdptr->pw_dir)) < 0)
+ goto out;
+
+out:
+ git__free(buf);
+ return error;
+}
+#endif
+
static int git_sysdir_guess_global_dirs(git_buf *out)
{
#ifdef GIT_WIN32
return git_win32__find_global_dirs(out);
#else
- int error = git__getenv(out, "HOME");
+ int error;
+ uid_t uid, euid;
+
+ uid = getuid();
+ euid = geteuid();
+
+ /*
+ * In case we are running setuid, use the configuration
+ * of the effective user.
+ */
+ if (uid == euid)
+ error = git__getenv(out, "HOME");
+ else
+ error = get_passwd_home(out, euid);
if (error == GIT_ENOTFOUND) {
giterr_clear();
#else
git_buf env = GIT_BUF_INIT;
int error;
-
- if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
- error = git_buf_joinpath(out, env.ptr, "git");
-
- if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
- error = git_buf_joinpath(out, env.ptr, ".config/git");
+ uid_t uid, euid;
+
+ uid = getuid();
+ euid = geteuid();
+
+ /*
+ * In case we are running setuid, only look up passwd
+ * directory of the effective user.
+ */
+ if (uid == euid) {
+ if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
+ error = git_buf_joinpath(out, env.ptr, "git");
+
+ if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
+ error = git_buf_joinpath(out, env.ptr, ".config/git");
+ } else {
+ if ((error = get_passwd_home(&env, euid)) == 0)
+ error = git_buf_joinpath(out, env.ptr, ".config/git");
+ }
if (error == GIT_ENOTFOUND) {
giterr_clear();
#define INCLUDE_sysdir_h__
#include "common.h"
+
#include "posix.h"
#include "buffer.h"
*/
extern int git_sysdir_set(git_sysdir_t which, const char *paths);
-#endif /* INCLUDE_sysdir_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "commit.h"
#include "tag.h"
+
+#include "commit.h"
#include "signature.h"
#include "message.h"
#include "git2/object.h"
#ifndef INCLUDE_tag_h__
#define INCLUDE_tag_h__
+#include "common.h"
+
#include "git2/tag.h"
#include "repository.h"
#include "odb.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
#include "common.h"
#include "thread-utils.h"
# define GIT_MEMORY_BARRIER /* noop */
#endif
-#endif /* INCLUDE_thread_utils_h__ */
+#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/errors.h"
-#include "common.h"
-
-#include "openssl_stream.h"
-#include "stransport_stream.h"
-
-static git_stream_cb tls_ctor;
-
-int git_stream_register_tls(git_stream_cb ctor)
-{
- tls_ctor = ctor;
-
- return 0;
-}
-
-int git_tls_stream_new(git_stream **out, const char *host, const char *port)
-{
-
- if (tls_ctor)
- return tls_ctor(out, host, port);
-
-#ifdef GIT_SECURE_TRANSPORT
- return git_stransport_stream_new(out, host, port);
-#elif defined(GIT_OPENSSL)
- return git_openssl_stream_new(out, host, port);
-#else
- GIT_UNUSED(out);
- GIT_UNUSED(host);
- GIT_UNUSED(port);
-
- giterr_set(GITERR_SSL, "there is no TLS stream available");
- return -1;
-#endif
-}
+++ /dev/null
-/*
- * 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_tls_stream_h__
-#define INCLUDE_tls_stream_h__
-
-#include "git2/sys/stream.h"
-
-/**
- * Create a TLS stream with the most appropriate backend available for
- * the current platform.
- *
- * This allows us to ask for a SecureTransport or OpenSSL stream
- * according to being on general Unix vs OS X.
- */
-extern int git_tls_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "trace.h"
+
#include "buffer.h"
-#include "common.h"
#include "global.h"
-#include "trace.h"
#include "git2/trace.h"
#ifdef GIT_TRACE
#ifndef INCLUDE_trace_h__
#define INCLUDE_trace_h__
+#include "common.h"
+
#include <git2/trace.h>
#include "buffer.h"
--- /dev/null
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "array.h"
+#include "common.h"
+#include "git2/message.h"
+
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+
+#define COMMENT_LINE_CHAR '#'
+#define TRAILER_SEPARATORS ":"
+
+static const char *const git_generated_prefixes[] = {
+ "Signed-off-by: ",
+ "(cherry picked from commit ",
+ NULL
+};
+
+static int is_blank_line(const char *str)
+{
+ const char *s = str;
+ while (*s && *s != '\n' && isspace(*s))
+ s++;
+ return !*s || *s == '\n';
+}
+
+static const char *next_line(const char *str)
+{
+ const char *nl = strchr(str, '\n');
+
+ if (nl) {
+ return nl + 1;
+ } else {
+ // return pointer to the NUL terminator:
+ return str + strlen(str);
+ }
+}
+
+/*
+ * Return the position of the start of the last line. If len is 0, return -1.
+ */
+static int last_line(const char *buf, size_t len)
+{
+ int i;
+ if (len == 0)
+ return -1;
+ if (len == 1)
+ return 0;
+ /*
+ * Skip the last character (in addition to the null terminator),
+ * because if the last character is a newline, it is considered as part
+ * of the last line anyway.
+ */
+ i = len - 2;
+
+ for (; i >= 0; i--) {
+ if (buf[i] == '\n')
+ return i + 1;
+ }
+ return 0;
+}
+
+/*
+ * If the given line is of the form
+ * "<token><optional whitespace><separator>..." or "<separator>...", return the
+ * location of the separator. Otherwise, return -1. The optional whitespace
+ * is allowed there primarily to allow things like "Bug #43" where <token> is
+ * "Bug" and <separator> is "#".
+ *
+ * The separator-starts-line case (in which this function returns 0) is
+ * distinguished from the non-well-formed-line case (in which this function
+ * returns -1) because some callers of this function need such a distinction.
+ */
+static int find_separator(const char *line, const char *separators)
+{
+ int whitespace_found = 0;
+ const char *c;
+ for (c = line; *c; c++) {
+ if (strchr(separators, *c))
+ return c - line;
+ if (!whitespace_found && (isalnum(*c) || *c == '-'))
+ continue;
+ if (c != line && (*c == ' ' || *c == '\t')) {
+ whitespace_found = 1;
+ continue;
+ }
+ break;
+ }
+ return -1;
+}
+
+/*
+ * Inspect the given string and determine the true "end" of the log message, in
+ * order to find where to put a new Signed-off-by: line. Ignored are
+ * trailing comment lines and blank lines. To support "git commit -s
+ * --amend" on an existing commit, we also ignore "Conflicts:". To
+ * support "git commit -v", we truncate at cut lines.
+ *
+ * Returns the number of bytes from the tail to ignore, to be fed as
+ * the second parameter to append_signoff().
+ */
+static int ignore_non_trailer(const char *buf, size_t len)
+{
+ int boc = 0;
+ size_t bol = 0;
+ int in_old_conflicts_block = 0;
+ size_t cutoff = len;
+
+ while (bol < cutoff) {
+ const char *next_line = memchr(buf + bol, '\n', len - bol);
+
+ if (!next_line)
+ next_line = buf + len;
+ else
+ next_line++;
+
+ if (buf[bol] == COMMENT_LINE_CHAR || buf[bol] == '\n') {
+ /* is this the first of the run of comments? */
+ if (!boc)
+ boc = bol;
+ /* otherwise, it is just continuing */
+ } else if (git__prefixcmp(buf + bol, "Conflicts:\n") == 0) {
+ in_old_conflicts_block = 1;
+ if (!boc)
+ boc = bol;
+ } else if (in_old_conflicts_block && buf[bol] == '\t') {
+ ; /* a pathname in the conflicts block */
+ } else if (boc) {
+ /* the previous was not trailing comment */
+ boc = 0;
+ in_old_conflicts_block = 0;
+ }
+ bol = next_line - buf;
+ }
+ return boc ? len - boc : len - cutoff;
+}
+
+/*
+ * Return the position of the start of the patch or the length of str if there
+ * is no patch in the message.
+ */
+static int find_patch_start(const char *str)
+{
+ const char *s;
+
+ for (s = str; *s; s = next_line(s)) {
+ if (git__prefixcmp(s, "---") == 0)
+ return s - str;
+ }
+
+ return s - str;
+}
+
+/*
+ * Return the position of the first trailer line or len if there are no
+ * trailers.
+ */
+static int find_trailer_start(const char *buf, size_t len)
+{
+ const char *s;
+ int end_of_title, l, only_spaces = 1;
+ int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
+ /*
+ * Number of possible continuation lines encountered. This will be
+ * reset to 0 if we encounter a trailer (since those lines are to be
+ * considered continuations of that trailer), and added to
+ * non_trailer_lines if we encounter a non-trailer (since those lines
+ * are to be considered non-trailers).
+ */
+ int possible_continuation_lines = 0;
+
+ /* The first paragraph is the title and cannot be trailers */
+ for (s = buf; s < buf + len; s = next_line(s)) {
+ if (s[0] == COMMENT_LINE_CHAR)
+ continue;
+ if (is_blank_line(s))
+ break;
+ }
+ end_of_title = s - buf;
+
+ /*
+ * Get the start of the trailers by looking starting from the end for a
+ * blank line before a set of non-blank lines that (i) are all
+ * trailers, or (ii) contains at least one Git-generated trailer and
+ * consists of at least 25% trailers.
+ */
+ for (l = last_line(buf, len);
+ l >= end_of_title;
+ l = last_line(buf, l)) {
+ const char *bol = buf + l;
+ const char *const *p;
+ int separator_pos;
+
+ if (bol[0] == COMMENT_LINE_CHAR) {
+ non_trailer_lines += possible_continuation_lines;
+ possible_continuation_lines = 0;
+ continue;
+ }
+ if (is_blank_line(bol)) {
+ if (only_spaces)
+ continue;
+ non_trailer_lines += possible_continuation_lines;
+ if (recognized_prefix &&
+ trailer_lines * 3 >= non_trailer_lines)
+ return next_line(bol) - buf;
+ else if (trailer_lines && !non_trailer_lines)
+ return next_line(bol) - buf;
+ return len;
+ }
+ only_spaces = 0;
+
+ for (p = git_generated_prefixes; *p; p++) {
+ if (git__prefixcmp(bol, *p) == 0) {
+ trailer_lines++;
+ possible_continuation_lines = 0;
+ recognized_prefix = 1;
+ goto continue_outer_loop;
+ }
+ }
+
+ separator_pos = find_separator(bol, TRAILER_SEPARATORS);
+ if (separator_pos >= 1 && !isspace(bol[0])) {
+ trailer_lines++;
+ possible_continuation_lines = 0;
+ if (recognized_prefix)
+ continue;
+ } else if (isspace(bol[0]))
+ possible_continuation_lines++;
+ else {
+ non_trailer_lines++;
+ non_trailer_lines += possible_continuation_lines;
+ possible_continuation_lines = 0;
+ }
+continue_outer_loop:
+ ;
+ }
+
+ return len;
+}
+
+/* Return the position of the end of the trailers. */
+static int find_trailer_end(const char *buf, size_t len)
+{
+ return len - ignore_non_trailer(buf, len);
+}
+
+static char *extract_trailer_block(const char *message, size_t* len)
+{
+ size_t patch_start = find_patch_start(message);
+ size_t trailer_end = find_trailer_end(message, patch_start);
+ size_t trailer_start = find_trailer_start(message, trailer_end);
+
+ size_t trailer_len = trailer_end - trailer_start;
+
+ char *buffer = git__malloc(trailer_len + 1);
+ memcpy(buffer, message + trailer_start, trailer_len);
+ buffer[trailer_len] = 0;
+
+ *len = trailer_len;
+
+ return buffer;
+}
+
+enum trailer_state {
+ S_START = 0,
+ S_KEY = 1,
+ S_KEY_WS = 2,
+ S_SEP_WS = 3,
+ S_VALUE = 4,
+ S_VALUE_NL = 5,
+ S_VALUE_END = 6,
+ S_IGNORE = 7,
+};
+
+#define NEXT(st) { state = (st); ptr++; continue; }
+#define GOTO(st) { state = (st); continue; }
+
+typedef git_array_t(git_message_trailer) git_array_trailer_t;
+
+int git_message_trailers(git_message_trailer_array *trailer_arr, const char *message)
+{
+ enum trailer_state state = S_START;
+ int rc = 0;
+ char *ptr;
+ char *key = NULL;
+ char *value = NULL;
+ git_array_trailer_t arr = GIT_ARRAY_INIT;
+
+ size_t trailer_len;
+ char *trailer = extract_trailer_block(message, &trailer_len);
+
+ for (ptr = trailer;;) {
+ switch (state) {
+ case S_START: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ key = ptr;
+ GOTO(S_KEY);
+ }
+ case S_KEY: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ if (isalnum(*ptr) || *ptr == '-') {
+ // legal key character
+ NEXT(S_KEY);
+ }
+
+ if (*ptr == ' ' || *ptr == '\t') {
+ // optional whitespace before separator
+ *ptr = 0;
+ NEXT(S_KEY_WS);
+ }
+
+ if (strchr(TRAILER_SEPARATORS, *ptr)) {
+ *ptr = 0;
+ NEXT(S_SEP_WS);
+ }
+
+ // illegal character
+ GOTO(S_IGNORE);
+ }
+ case S_KEY_WS: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ if (*ptr == ' ' || *ptr == '\t') {
+ NEXT(S_KEY_WS);
+ }
+
+ if (strchr(TRAILER_SEPARATORS, *ptr)) {
+ NEXT(S_SEP_WS);
+ }
+
+ // illegal character
+ GOTO(S_IGNORE);
+ }
+ case S_SEP_WS: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ if (*ptr == ' ' || *ptr == '\t') {
+ NEXT(S_SEP_WS);
+ }
+
+ value = ptr;
+ NEXT(S_VALUE);
+ }
+ case S_VALUE: {
+ if (*ptr == 0) {
+ GOTO(S_VALUE_END);
+ }
+
+ if (*ptr == '\n') {
+ NEXT(S_VALUE_NL);
+ }
+
+ NEXT(S_VALUE);
+ }
+ case S_VALUE_NL: {
+ if (*ptr == ' ') {
+ // continuation;
+ NEXT(S_VALUE);
+ }
+
+ ptr[-1] = 0;
+ GOTO(S_VALUE_END);
+ }
+ case S_VALUE_END: {
+ git_message_trailer *t = git_array_alloc(arr);
+
+ t->key = key;
+ t->value = value;
+
+ key = NULL;
+ value = NULL;
+
+ GOTO(S_START);
+ }
+ case S_IGNORE: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ if (*ptr == '\n') {
+ NEXT(S_START);
+ }
+
+ NEXT(S_IGNORE);
+ }
+ }
+ }
+
+ret:
+ trailer_arr->_trailer_block = trailer;
+ trailer_arr->trailers = arr.ptr;
+ trailer_arr->count = arr.size;
+
+ return rc;
+}
+
+void git_message_trailer_array_free(git_message_trailer_array *arr)
+{
+ git__free(arr->_trailer_block);
+ git__free(arr->trailers);
+}
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "transaction.h"
+
#include "repository.h"
#include "strmap.h"
#include "refdb.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
#include "common.h"
+
#include "git2/types.h"
#include "git2/remote.h"
#include "git2/net.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "auth.h"
+
#include "git2.h"
#include "buffer.h"
-#include "auth.h"
static int basic_next_token(
git_buf *out, git_http_auth_context *ctx, git_cred *c)
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_http_auth_h__
-#define INCLUDE_http_auth_h__
+#ifndef INCLUDE_transports_auth_h__
+#define INCLUDE_transports_auth_h__
+
+#include "common.h"
#include "git2.h"
#include "netops.h"
const gitno_connection_data *connection_data);
#endif
-
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "auth_negotiate.h"
+
#ifdef GIT_GSSAPI
#include "git2.h"
-#include "common.h"
#include "buffer.h"
#include "auth.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_auth_negotiate_h__
-#define INCLUDE_auth_negotiate_h__
+#ifndef INCLUDE_transports_auth_negotiate_h__
+#define INCLUDE_transports_auth_negotiate_h__
+#include "common.h"
#include "git2.h"
#include "auth.h"
#endif /* GIT_GSSAPI */
#endif
-
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "cred.h"
+
#include "git2.h"
#include "smart.h"
#include "git2/cred_helpers.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_git_cred_h__
-#define INCLUDE_git_cred_h__
+#ifndef INCLUDE_transports_cred_h__
+#define INCLUDE_transports_cred_h__
+
+#include "common.h"
#include "git2/transport.h"
*/
#include "common.h"
+
#include "git2/cred_helpers.h"
int git_cred_userpass(
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "common.h"
+
#include "git2.h"
#include "buffer.h"
#include "netops.h"
#include "git2/sys/transport.h"
#include "stream.h"
-#include "socket_stream.h"
+#include "streams/socket.h"
#define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
+#include "common.h"
+
#ifndef GIT_WINHTTP
#include "git2.h"
#include "remote.h"
#include "smart.h"
#include "auth.h"
+#include "http.h"
#include "auth_negotiate.h"
-#include "tls_stream.h"
-#include "socket_stream.h"
-#include "curl_stream.h"
+#include "streams/tls.h"
+#include "streams/socket.h"
+#include "streams/curl.h"
git_http_auth_scheme auth_schemes[] = {
{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
return context->next_token(buf, context, cred);
}
-static const char *user_agent(void)
-{
- const char *custom = git_libgit2__user_agent();
-
- if (custom)
- return custom;
-
- return "libgit2 " LIBGIT2_VERSION;
-}
-
static int gen_request(
git_buf *buf,
http_stream *s,
git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
- git_buf_printf(buf, "User-Agent: git/2.0 (%s)\r\n", user_agent());
+ git_buf_puts(buf, "User-Agent: ");
+ git_http__user_agent(buf);
+ git_buf_puts(buf, "\r\n");
git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
if (s->chunked || content_length > 0) {
--- /dev/null
+/*
+ * 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_transports_http_h__
+#define INCLUDE_transports_http_h__
+
+#include "buffer.h"
+
+GIT_INLINE(int) git_http__user_agent(git_buf *buf)
+{
+ const char *ua = git_libgit2__user_agent();
+
+ if (!ua)
+ ua = "libgit2 " LIBGIT2_VERSION;
+
+ return git_buf_printf(buf, "git/2.0 (%s)", ua);
+}
+
+#endif
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
#include "common.h"
+
#include "git2/types.h"
#include "git2/net.h"
#include "git2/repository.h"
#include "git2/pack.h"
#include "git2/commit.h"
#include "git2/revparse.h"
+
#include "pack-objects.h"
#include "refs.h"
#include "posix.h"
return error;
}
+static int foreach_reference_cb(git_reference *reference, void *payload)
+{
+ git_revwalk *walk = (git_revwalk *)payload;
+
+ int error = git_revwalk_hide(walk, git_reference_target(reference));
+ /* The reference is in the local repository, so the target may not
+ * exist on the remote. It also may not be a commit. */
+ if (error == GIT_ENOTFOUND || error == GITERR_INVALID) {
+ giterr_clear();
+ error = 0;
+ }
+
+ git_reference_free(reference);
+
+ return error;
+}
+
static int local_download_pack(
git_transport *transport,
git_repository *repo,
if (git_object_type(obj) == GIT_OBJ_COMMIT) {
/* Revwalker includes only wanted commits */
error = git_revwalk_push(walk, &rhead->oid);
- if (!error && !git_oid_iszero(&rhead->loid)) {
- error = git_revwalk_hide(walk, &rhead->loid);
- if (error == GIT_ENOTFOUND)
- error = 0;
- }
} else {
/* Tag or some other wanted object. Add it on its own */
error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name);
goto cleanup;
}
+ if ((error = git_reference_foreach(repo, foreach_reference_cb, walk)))
+ goto cleanup;
+
if ((error = git_packbuilder_insert_walk(pack, walk)))
goto cleanup;
* 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.h"
+
#include "smart.h"
+
+#include "git2.h"
#include "refs.h"
#include "refspec.h"
#include "proxy.h"
* 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_transports_smart_h__
+#define INCLUDE_transports_smart_h__
+
+#include "common.h"
+
#include "git2.h"
#include "vector.h"
#include "netops.h"
int git_pkt_buffer_wants(const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_buf *buf);
int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
void git_pkt_free(git_pkt *pkt);
+
+#endif
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
+#include "common.h"
+
#include "git2.h"
#include "git2/odb_backend.h"
git_revwalk *walk = NULL;
git_strarray refs;
unsigned int i;
- git_reference *ref;
+ git_reference *ref = NULL;
int error;
if ((error = git_reference_list(&refs, repo)) < 0)
git_revwalk_sorting(walk, GIT_SORT_TIME);
for (i = 0; i < refs.count; ++i) {
+ git_reference_free(ref);
+ ref = NULL;
+
/* No tags */
if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
continue;
if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
goto on_error;
-
- git_reference_free(ref);
}
- git_strarray_free(&refs);
*out = walk;
- return 0;
on_error:
- git_revwalk_free(walk);
+ if (error)
+ git_revwalk_free(walk);
git_reference_free(ref);
git_strarray_free(&refs);
return error;
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "ssh.h"
+
#ifdef GIT_SSH
#include <libssh2.h>
#endif
#include "netops.h"
#include "smart.h"
#include "cred.h"
-#include "socket_stream.h"
-#include "ssh.h"
+#include "streams/socket.h"
#ifdef GIT_SSH
*/
static int gen_proto(git_buf *request, const char *cmd, const char *url)
{
- char *repo;
+ const char *repo;
int len;
size_t i;
len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
git_buf_grow(request, len);
- git_buf_printf(request, "%s '%s'", cmd, repo);
- git_buf_putc(request, '\0');
+ git_buf_puts(request, cmd);
+ git_buf_puts(request, " '");
+ git_buf_decode_percent(request, repo, strlen(repo));
+ git_buf_puts(request, "'");
if (git_buf_oom(request))
return -1;
}
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
- if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED || rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
- return GIT_EAUTH;
+ if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED ||
+ rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED ||
+ rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
+ return GIT_EAUTH;
if (rc != LIBSSH2_ERROR_NONE) {
if (!giterr_last())
* 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_ssh_h__
-#define INCLUDE_ssh_h__
+#ifndef INCLUDE_transports_ssh_h__
+#define INCLUDE_transports_ssh_h__
+
+#include "common.h"
int git_transport_ssh_global_init(void);
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "common.h"
+
#ifdef GIT_WINHTTP
#include "git2.h"
#include "remote.h"
#include "repository.h"
#include "global.h"
+#include "http.h"
#include <wincrypt.h>
#include <winhttp.h>
#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0
#endif
+#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS_1_1
+# define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 0x00000200
+#endif
+
+#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS_1_2
+# define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 0x00000800
+#endif
+
static const char *prefix_https = "https://";
static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
* is "medium" which applies to the intranet and sounds like it would correspond
* to Internet Explorer security zones, but in fact does not. */
DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
+ DWORD native_scheme = 0;
- if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) == 0 &&
- (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) == 0) {
+ if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) != 0)
+ native_scheme |= WINHTTP_AUTH_SCHEME_NTLM;
+
+ if ((mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) != 0)
+ native_scheme |= WINHTTP_AUTH_SCHEME_NEGOTIATE;
+
+ if (!native_scheme) {
giterr_set(GITERR_NET, "invalid authentication scheme");
return -1;
}
if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD)))
return -1;
+ if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_SERVER, native_scheme, NULL, NULL, NULL))
+ return -1;
+
return 0;
}
cert.parent.cert_type = GIT_CERT_X509;
cert.data = cert_ctx->pbCertEncoded;
cert.len = cert_ctx->cbCertEncoded;
- error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->cred_acquire_payload);
+ error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->message_cb_payload);
CertFreeCertificateContext(cert_ctx);
if (error < 0 && !giterr_last())
if (WINHTTP_AUTH_SCHEME_NTLM & supported) {
*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
*allowed_types |= GIT_CREDTYPE_DEFAULT;
- *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE;
+ *allowed_mechanisms |= GIT_WINHTTP_AUTH_NTLM;
}
if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) {
*allowed_types |= GIT_CREDTYPE_DEFAULT;
- *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE;
+ *allowed_mechanisms |= GIT_WINHTTP_AUTH_NEGOTIATE;
}
if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
return ret;
}
-static int user_agent(git_buf *ua)
-{
- const char *custom = git_libgit2__user_agent();
-
- git_buf_clear(ua);
- git_buf_PUTS(ua, "git/1.0 (");
-
- if (custom)
- git_buf_puts(ua, custom);
- else
- git_buf_PUTS(ua, "libgit2 " LIBGIT2_VERSION);
-
- return git_buf_putc(ua, ')');
-}
-
static void CALLBACK winhttp_status(
HINTERNET connection,
DWORD_PTR ctx,
int error = -1;
int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+ DWORD protocols =
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
t->session = NULL;
t->connection = NULL;
return -1;
}
- if ((error = user_agent(&ua)) < 0) {
+
+ if ((error = git_http__user_agent(&ua)) < 0) {
git__free(wide_host);
return error;
}
goto on_error;
}
+ /*
+ * Do a best-effort attempt to enable TLS 1.2 but allow this to
+ * fail; if TLS 1.2 support is not available for some reason,
+ * ignore the failure (it will keep the default protocols).
+ */
+ WinHttpSetOption(t->session,
+ WINHTTP_OPTION_SECURE_PROTOCOLS,
+ &protocols,
+ sizeof(protocols));
+
if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
giterr_set(GITERR_OS, "failed to set timeouts for WinHTTP");
goto on_error;
*/
#include "tree-cache.h"
+
#include "pool.h"
#include "tree.h"
#define INCLUDE_tree_cache_h__
#include "common.h"
+
#include "pool.h"
#include "buffer.h"
#include "git2/oid.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "commit.h"
#include "tree.h"
+
+#include "commit.h"
#include "git2/repository.h"
#include "git2/object.h"
#include "fileops.h"
unsigned int attr;
if (parse_mode(&attr, buffer, &buffer) < 0 || !buffer)
- return tree_error("Failed to parse tree. Can't parse filemode", NULL);
+ return tree_error("failed to parse tree: can't parse filemode", NULL);
if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL)
- return tree_error("Failed to parse tree. Object is corrupted", NULL);
+ return tree_error("failed to parse tree: object is corrupted", NULL);
if ((filename_len = nul - buffer) == 0)
- return tree_error("Failed to parse tree. Can't parse filename", NULL);
+ return tree_error("failed to parse tree: can't parse filename", NULL);
if ((buffer_end - (nul + 1)) < GIT_OID_RAWSZ)
- return tree_error("Failed to parse tree. Can't parse OID", NULL);
+ return tree_error("failed to parse tree: can't parse OID", NULL);
/* Allocate the entry */
{
int error = 0;
if (!valid_entry_name(bld->repo, filename))
- return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
+ return tree_error("failed to insert entry: invalid name for a tree entry", filename);
+
+ if (git_oid_iszero(id))
+ return tree_error("failed to insert entry: invalid null OID for a tree entry", filename);
entry = alloc_entry(filename, strlen(filename), id);
GITERR_CHECK_ALLOC(entry);
assert(bld && id && filename);
if (!valid_filemode(filemode))
- return tree_error("Failed to insert entry. Invalid filemode for file", filename);
+ return tree_error("failed to insert entry: invalid filemode for file", filename);
if (!valid_entry_name(bld->repo, filename))
- return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
+ return tree_error("failed to insert entry: invalid name for a tree entry", filename);
+
+ if (git_oid_iszero(id))
+ return tree_error("failed to insert entry: invalid null OID", filename);
if (filemode != GIT_FILEMODE_COMMIT &&
!git_object__is_valid(bld->repo, id, otype_from_mode(filemode)))
- return tree_error("Failed to insert entry; invalid object specified", filename);
+ return tree_error("failed to insert entry: invalid object specified", filename);
pos = git_strmap_lookup_index(bld->map, filename);
if (git_strmap_valid_index(bld->map, pos)) {
git_tree_entry *entry = treebuilder_get(bld, filename);
if (entry == NULL)
- return tree_error("Failed to remove entry. File isn't in the tree", filename);
+ return tree_error("failed to remove entry: file isn't in the tree", filename);
git_strmap_delete(bld->map, filename);
git_tree_entry_free(entry);
return GIT_ENOTFOUND;
}
- /* If there's only a slash left in the path, we
+ /* If there's only a slash left in the path, we
* return the current entry; otherwise, we keep
* walking down the path */
if (path[filename_len + 1] != '\0')
break;
-
+ /* fall through */
case '\0':
/* If there are no more components in the path, return
* this entry */
#ifndef INCLUDE_tree_h__
#define INCLUDE_tree_h__
+#include "common.h"
+
#include "git2/tree.h"
#include "repository.h"
#include "odb.h"
#define PUSH_NEXT() do {\
len = count_run(dst, curr, size, store);\
run = minrun;\
- if (run < minrun) run = minrun;\
if (run > (ssize_t)size - curr) run = size - curr;\
if (run > len) {\
bisort(&dst[curr], len, run, cmp, payload);\
* 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/common.h>
+
+#include "common.h"
+
+#include "git2/common.h"
#if !defined(GIT_WIN32) && !defined(NO_MMAP)
* 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_posix__unix_h__
-#define INCLUDE_posix__unix_h__
+#ifndef INCLUDE_unix_posix_h__
+#define INCLUDE_unix_posix_h__
+
+#ifndef LIBGIT2_NO_FEATURES_H
+# include "git2/sys/features.h"
+#endif
#include <stdio.h>
#include <dirent.h>
#define p_timeval timeval
-#ifdef HAVE_FUTIMENS
+#ifdef GIT_USE_FUTIMENS
GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2])
{
struct timespec s[2];
# define p_futimes futimes
#endif
-#ifdef HAVE_REGCOMP_L
+#ifdef GIT_USE_REGCOMP_L
#include <xlocale.h>
GIT_INLINE(int) p_regcomp(regex_t *preg, const char *pattern, int cflags)
{
#define git_rwlock_free(a) pthread_rwlock_destroy(a)
#define GIT_RWLOCK_STATIC_INIT PTHREAD_RWLOCK_INITIALIZER
-#endif /* INCLUDE_unix_pthread_h__ */
+#endif
* 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/common.h>
+
+#include "common.h"
+
+#include "git2/common.h"
#ifndef GIT_WIN32
* 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.h>
+
+#include "util.h"
+
#include "common.h"
-#include <stdio.h>
-#include <ctype.h>
-#include "posix.h"
#ifdef GIT_WIN32
# include "win32/w32_buffer.h"
git__strntolower(str, strlen(str));
}
-int git__prefixcmp(const char *str, const char *prefix)
+GIT_INLINE(int) prefixcmp(const char *str, size_t str_n, const char *prefix, bool icase)
{
- for (;;) {
- unsigned char p = *(prefix++), s;
+ int s, p;
+
+ while (str_n--) {
+ s = (unsigned char)*str++;
+ p = (unsigned char)*prefix++;
+
+ if (icase) {
+ s = git__tolower(s);
+ p = git__tolower(p);
+ }
+
if (!p)
return 0;
- if ((s = *(str++)) != p)
+
+ if (s != p)
return s - p;
}
+
+ return (0 - *prefix);
}
-int git__prefixcmp_icase(const char *str, const char *prefix)
+int git__prefixcmp(const char *str, const char *prefix)
{
- return strncasecmp(str, prefix, strlen(prefix));
+ return prefixcmp(str, SIZE_MAX, prefix, false);
}
-int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
+int git__prefixncmp(const char *str, size_t str_n, const char *prefix)
{
- int s, p;
-
- while(str_n--) {
- s = (unsigned char)git__tolower(*str++);
- p = (unsigned char)git__tolower(*prefix++);
+ return prefixcmp(str, str_n, prefix, false);
+}
- if (s != p)
- return s - p;
- }
+int git__prefixcmp_icase(const char *str, const char *prefix)
+{
+ return prefixcmp(str, SIZE_MAX, prefix, true);
+}
- return (0 - *prefix);
+int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
+{
+ return prefixcmp(str, str_n, prefix, true);
}
int git__suffixcmp(const char *str, const char *suffix)
switch(len & 3) {
case 3: k1 ^= tail[2] << 16;
+ /* fall through */
case 2: k1 ^= tail[1] << 8;
+ /* fall through */
case 1: k1 ^= tail[0];
- MURMUR_BLOCK();
+ MURMUR_BLOCK();
}
h1 ^= len;
#ifndef INCLUDE_util_h__
#define INCLUDE_util_h__
-#include "git2/buffer.h"
-#include "buffer.h"
+#include "common.h"
-#if defined(GIT_MSVC_CRTDBG)
-/* Enable MSVC CRTDBG memory leak reporting.
- *
- * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC
- * documentation because all allocs/frees in libgit2 already go through
- * the "git__" routines defined in this file. Simply using the normal
- * reporting mechanism causes all leaks to be attributed to a routine
- * here in util.h (ie, the actual call to calloc()) rather than the
- * caller of git__calloc().
- *
- * Therefore, we declare a set of "git__crtdbg__" routines to replace
- * the corresponding "git__" routines and re-define the "git__" symbols
- * as macros. This allows us to get and report the file:line info of
- * the real caller.
- *
- * We DO NOT replace the "git__free" routine because it needs to remain
- * a function pointer because it is used as a function argument when
- * setting up various structure "destructors".
- *
- * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes
- * "free" to be remapped to "_free_dbg" and this causes problems for
- * structures which define a field named "free".
- *
- * Finally, CRTDBG must be explicitly enabled and configured at program
- * startup. See tests/main.c for an example.
- */
-#include <stdlib.h>
-#include <crtdbg.h>
-#include "win32/w32_crtdbg_stacktrace.h"
+#ifndef GIT_WIN32
+# include <ctype.h>
#endif
+#include "git2/buffer.h"
+
+#include "buffer.h"
#include "common.h"
#include "strnlen.h"
+#include "thread-utils.h"
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#define bitsizeof(x) (CHAR_BIT * sizeof(x))
#if defined(GIT_MSVC_CRTDBG)
-GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line)
-{
- void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
-{
- void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line)
-{
- char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__strndup(const char *str, size_t n, const char *file, int line)
-{
- size_t length = 0, alloclength;
- char *ptr;
-
- length = p_strnlen(str, n);
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
- !(ptr = git__crtdbg__malloc(alloclength, file, line)))
- return NULL;
-
- if (length)
- memcpy(ptr, str, length);
-
- ptr[length] = '\0';
-
- return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const char *file, int line)
-{
- char *ptr;
- size_t alloclen;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
- !(ptr = git__crtdbg__malloc(alloclen, file, line)))
- return NULL;
-
- memcpy(ptr, start, n);
- ptr[n] = '\0';
- return ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
-{
- void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!new_ptr) giterr_set_oom();
- return new_ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
-{
- size_t newsize;
-
- return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
- NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
-}
+/* Enable MSVC CRTDBG memory leak reporting.
+ *
+ * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC
+ * documentation because all allocs/frees in libgit2 already go through
+ * the "git__" routines defined in this file. Simply using the normal
+ * reporting mechanism causes all leaks to be attributed to a routine
+ * here in util.h (ie, the actual call to calloc()) rather than the
+ * caller of git__calloc().
+ *
+ * Therefore, we declare a set of "git__crtdbg__" routines to replace
+ * the corresponding "git__" routines and re-define the "git__" symbols
+ * as macros. This allows us to get and report the file:line info of
+ * the real caller.
+ *
+ * We DO NOT replace the "git__free" routine because it needs to remain
+ * a function pointer because it is used as a function argument when
+ * setting up various structure "destructors".
+ *
+ * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes
+ * "free" to be remapped to "_free_dbg" and this causes problems for
+ * structures which define a field named "free".
+ *
+ * Finally, CRTDBG must be explicitly enabled and configured at program
+ * startup. See tests/main.c for an example.
+ */
-GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
-{
- return git__crtdbg__reallocarray(NULL, nelem, elsize, file, line);
-}
+#include "win32/w32_crtdbg_stacktrace.h"
#define git__malloc(len) git__crtdbg__malloc(len, __FILE__, __LINE__)
#define git__calloc(nelem, elsize) git__crtdbg__calloc(nelem, elsize, __FILE__, __LINE__)
extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__prefixcmp_icase(const char *str, const char *prefix);
+extern int git__prefixncmp(const char *str, size_t str_n, const char *prefix);
extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix);
extern int git__strcasesort_cmp(const char *a, const char *b);
-#include "thread-utils.h"
-
typedef struct {
git_atomic refcount;
void *owner;
typedef void (*git_refcount_freeptr)(void *r);
#define GIT_REFCOUNT_INC(r) { \
- git_atomic_inc(&((git_refcount *)(r))->refcount); \
+ git_atomic_inc(&(r)->rc.refcount); \
}
#define GIT_REFCOUNT_DEC(_r, do_free) { \
- git_refcount *r = (git_refcount *)(_r); \
+ git_refcount *r = &(_r)->rc; \
int val = git_atomic_dec(&r->refcount); \
if (val <= 0 && r->owner == NULL) { do_free(_r); } \
}
#define GIT_REFCOUNT_OWN(r, o) { \
- ((git_refcount *)(r))->owner = o; \
+ (r)->rc.owner = o; \
}
-#define GIT_REFCOUNT_OWNER(r) (((git_refcount *)(r))->owner)
+#define GIT_REFCOUNT_OWNER(r) ((r)->rc.owner)
-#define GIT_REFCOUNT_VAL(r) git_atomic_get(&((git_refcount *)(r))->refcount)
+#define GIT_REFCOUNT_VAL(r) git_atomic_get((r)->rc.refcount)
static signed char from_hex[] = {
extern int git__getenv(git_buf *out, const char *name);
-#endif /* INCLUDE_util_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "varint.h"
uintmax_t git_decode_varint(const unsigned char *bufp, size_t *varint_len)
#ifndef INCLUDE_varint_h__
#define INCLUDE_varint_h__
+#include "common.h"
+
#include <stdint.h>
extern int git_encode_varint(unsigned char *, size_t, uintmax_t);
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "vector.h"
+
#include "integer.h"
/* In elements, not bytes */
* 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 "dir.h"
+
#define GIT__WIN32_NO_WRAP_DIR
#include "posix.h"
* 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_dir_h__
-#define INCLUDE_dir_h__
+#ifndef INCLUDE_win32_dir_h__
+#define INCLUDE_win32_dir_h__
#include "common.h"
+
#include "w32_util.h"
struct git__dirent {
# define closedir git__closedir
# endif
-#endif /* INCLUDE_dir_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "error.h"
+
#include "utf-conv.h"
#ifdef GIT_WINHTTP
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_git_win32_error_h__
-#define INCLUDE_git_win32_error_h__
+#ifndef INCLUDE_win32_error_h__
+#define INCLUDE_win32_error_h__
+
+#include "common.h"
extern char *git_win32_get_error_message(DWORD error_code);
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "findfile.h"
+
#include "path_w32.h"
#include "utf-conv.h"
#include "path.h"
-#include "findfile.h"
#define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_git_findfile_h__
-#define INCLUDE_git_findfile_h__
+#ifndef INCLUDE_win32_findfile_h__
+#define INCLUDE_win32_findfile_h__
+
+#include "common.h"
extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath);
extern int git_win32__find_global_dirs(git_buf *out);
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "common.h"
+
#include "map.h"
#include <errno.h>
* 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_mingw_compat__
-#define INCLUDE_mingw_compat__
+#ifndef INCLUDE_win32_mingw_compat_h__
+#define INCLUDE_win32_mingw_compat_h__
#if defined(__MINGW32__)
#endif
-#endif /* INCLUDE_mingw_compat__ */
+#endif
* 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_msvc_compat__
-#define INCLUDE_msvc_compat__
+#ifndef INCLUDE_win32_msvc_compat_h__
+#define INCLUDE_win32_msvc_compat_h__
#if defined(_MSC_VER)
#define GIT_STDLIB_CALL __cdecl
-#endif /* INCLUDE_msvc_compat__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "path.h"
#include "path_w32.h"
+
+#include "path.h"
#include "utf-conv.h"
#include "posix.h"
#include "reparse.h"
#define PATH__ABSOLUTE_LEN 3
-#define path__is_dirsep(p) ((p) == '/' || (p) == '\\')
-
-#define path__is_absolute(p) \
- (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
-
#define path__is_nt_namespace(p) \
(((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
wchar_t *c;
for (c = path; *c; c++) {
- if (path__is_dirsep(*c))
+ if (git_path_is_dirsep(*c))
return c + 1;
}
if (wcsncmp(path, L"UNC\\", 4) == 0)
path = path__skip_server(path + 4);
- else if (path__is_absolute(path))
+ else if (git_path_is_absolute(path))
path += PATH__ABSOLUTE_LEN;
- } else if (path__is_absolute(path)) {
+ } else if (git_path_is_absolute(path)) {
path += PATH__ABSOLUTE_LEN;
} else if (path__is_unc(path)) {
path = path__skip_server(path + 2);
dest += PATH__NT_NAMESPACE_LEN;
/* See if this is an absolute path (beginning with a drive letter) */
- if (path__is_absolute(src)) {
+ if (git_path_is_absolute(src)) {
if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
goto on_error;
}
if (path__cwd(dest, MAX_PATH) < 0)
goto on_error;
- if (!path__is_absolute(dest)) {
+ if (!git_path_is_absolute(dest)) {
errno = ENOENT;
goto on_error;
}
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_git_path_w32_h__
-#define INCLUDE_git_path_w32_h__
+#ifndef INCLUDE_win32_path_w32_h__
+#define INCLUDE_win32_path_w32_h__
#include "common.h"
+
#include "vector.h"
/*
* 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_posix__w32_h__
-#define INCLUDE_posix__w32_h__
+#ifndef INCLUDE_win32_posix_h__
+#define INCLUDE_win32_posix_h__
#include "common.h"
#include "../posix.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+
+#include "common.h"
+
#include "../posix.h"
#include "../fileops.h"
#include "path.h"
#define do_with_retries(fn, remediation) \
do { \
- int __tries, __ret; \
- for (__tries = 0; __tries < git_win32__retries; __tries++) { \
- if (__tries && (__ret = (remediation)) != 0) \
- return __ret; \
+ int __retry, __ret; \
+ for (__retry = git_win32__retries; __retry; __retry--) { \
if ((__ret = (fn)) != GIT_RETRY) \
return __ret; \
+ if (__retry > 1 && (__ret = (remediation)) != 0) { \
+ if (__ret == GIT_RETRY) \
+ continue; \
+ return __ret; \
+ } \
Sleep(5); \
} \
return -1; \
if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
goto on_error;
- return 0;
+ return GIT_RETRY;
on_error:
set_errno();
+#include "common.h"
+
#include <assert.h>
#include <errno.h>
#include <limits.h>
#endif
#include "git2.h"
-#include "common.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_git_win32_reparse_h__
-#define INCLUDE_git_win32_reparse_h__
+#ifndef INCLUDE_win32_reparse_h__
+#define INCLUDE_win32_reparse_h__
/* This structure is defined on MSDN at
* http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
*/
#include "thread.h"
+
#include "../global.h"
#define CLEAN_THREAD_EXIT 0x6F012842
#ifndef INCLUDE_win32_thread_h__
#define INCLUDE_win32_thread_h__
-#include "../common.h"
+#include "common.h"
#if defined (_MSC_VER)
# define GIT_RESTRICT __restrict
int git_rwlock_wrunlock(git_rwlock *);
int git_rwlock_free(git_rwlock *);
-#endif /* INCLUDE_win32_thread_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "utf-conv.h"
GIT_INLINE(void) git__set_errno(void)
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_git_utfconv_h__
-#define INCLUDE_git_utfconv_h__
+#ifndef INCLUDE_win32_utf_conv_h__
+#define INCLUDE_win32_utf_conv_h__
-#include <wchar.h>
#include "common.h"
+#include <wchar.h>
+
#ifndef WC_ERR_INVALID_CHARS
# define WC_ERR_INVALID_CHARS 0x80
#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
#include "w32_buffer.h"
+
#include "../buffer.h"
#include "utf-conv.h"
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_git_win32_buffer_h__
-#define INCLUDE_git_win32_buffer_h__
+#ifndef INCLUDE_win32_w32_buffer_h__
+#define INCLUDE_win32_w32_buffer_h__
+
+#include "common.h"
#include "../buffer.h"
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "w32_crtdbg_stacktrace.h"
+
#if defined(GIT_MSVC_CRTDBG)
#include "w32_stack.h"
-#include "w32_crtdbg_stacktrace.h"
#define CRTDBG_STACKTRACE__UID_LEN (15)
* 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_w32_crtdbg_stacktrace_h__
-#define INCLUDE_w32_crtdbg_stacktrace_h__
+#ifndef INCLUDE_win32_w32_crtdbg_stacktrace_h__
+#define INCLUDE_win32_w32_crtdbg_stacktrace_h__
+
+#include "common.h"
#if defined(GIT_MSVC_CRTDBG)
+#include <stdlib.h>
+#include <crtdbg.h>
+
+#include "git2/errors.h"
+#include "strnlen.h"
+
/**
* Initialize our memory leak tracking and de-dup data structures.
* This should ONLY be called by git_libgit2_init().
*/
const char *git_win32__crtdbg_stacktrace(int skip, const char *file);
+GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line)
+{
+ void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+ if (!ptr) giterr_set_oom();
+ return ptr;
+}
+
+GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
+{
+ void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+ if (!ptr) giterr_set_oom();
+ return ptr;
+}
+
+GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line)
+{
+ char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+ if (!ptr) giterr_set_oom();
+ return ptr;
+}
+
+GIT_INLINE(char *) git__crtdbg__strndup(const char *str, size_t n, const char *file, int line)
+{
+ size_t length = 0, alloclength;
+ char *ptr;
+
+ length = p_strnlen(str, n);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
+ !(ptr = git__crtdbg__malloc(alloclength, file, line)))
+ return NULL;
+
+ if (length)
+ memcpy(ptr, str, length);
+
+ ptr[length] = '\0';
+
+ return ptr;
+}
+
+GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const char *file, int line)
+{
+ char *ptr;
+ size_t alloclen;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
+ !(ptr = git__crtdbg__malloc(alloclen, file, line)))
+ return NULL;
+
+ memcpy(ptr, start, n);
+ ptr[n] = '\0';
+ return ptr;
+}
+
+GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
+{
+ void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+ if (!new_ptr) giterr_set_oom();
+ return new_ptr;
+}
+
+GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
+{
+ size_t newsize;
+
+ return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
+ NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+}
+
+GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
+{
+ return git__crtdbg__reallocarray(NULL, nelem, elsize, file, line);
+}
+
+
#endif
#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "w32_stack.h"
+
#if defined(GIT_MSVC_CRTDBG)
#include "Windows.h"
#include "Dbghelp.h"
#include "win32/posix.h"
-#include "w32_stack.h"
#include "hash.h"
/**
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_w32_stack_h__
-#define INCLUDE_w32_stack_h__
+#ifndef INCLUDE_win32_w32_stack_h__
+#define INCLUDE_win32_w32_stack_h__
+
+#include "common.h"
#if defined(GIT_MSVC_CRTDBG)
const char *prefix, const char *suffix);
#endif /* GIT_MSVC_CRTDBG */
-#endif /* INCLUDE_w32_stack_h__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_w32_util_h__
-#define INCLUDE_w32_util_h__
+#ifndef INCLUDE_win32_w32_util_h__
+#define INCLUDE_win32_w32_util_h__
+
+#include "common.h"
#include "utf-conv.h"
#include "posix.h"
* 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_win32_compat__
-#define INCLUDE_win32_compat__
+#ifndef INCLUDE_win32_win32_compat_h__
+#define INCLUDE_win32_win32_compat_h__
#include <stdint.h>
#include <time.h>
#define stat p_stat
-#endif /* INCLUDE_win32_compat__ */
+#endif
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "worktree.h"
#include "git2/branch.h"
#include "git2/commit.h"
#include "git2/worktree.h"
#include "repository.h"
-#include "worktree.h"
static bool is_worktree_dir(const char *dir)
{
return err;
}
-int git_worktree_lock(git_worktree *wt, char *creason)
+int git_worktree_lock(git_worktree *wt, const char *reason)
{
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
int err;
if ((err = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0)
goto out;
- if (creason)
- git_buf_attach_notowned(&buf, creason, strlen(creason));
+ if (reason)
+ git_buf_attach_notowned(&buf, reason, strlen(reason));
if ((err = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0)
goto out;
#ifndef INCLUDE_worktree_h__
#define INCLUDE_worktree_h__
+#include "common.h"
+
#include "git2/common.h"
#include "git2/worktree.h"
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
-#include "../util.h"
-
#if !defined(XDIFF_H)
#define XDIFF_H
extern "C" {
#endif /* #ifdef __cplusplus */
+/* xpparm_t.flags */
+#define XDF_NEED_MINIMAL (1 << 0)
+
+#define XDF_IGNORE_WHITESPACE (1 << 1)
+#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 2)
+#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 3)
+#define XDF_IGNORE_CR_AT_EOL (1 << 4)
+#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | \
+ XDF_IGNORE_WHITESPACE_CHANGE | \
+ XDF_IGNORE_WHITESPACE_AT_EOL | \
+ XDF_IGNORE_CR_AT_EOL)
-#define XDF_NEED_MINIMAL (1 << 1)
-#define XDF_IGNORE_WHITESPACE (1 << 2)
-#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
-#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
-#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
-#define XDF_PATIENCE_DIFF (1 << 5)
-#define XDF_HISTOGRAM_DIFF (1 << 6)
+#define XDF_PATIENCE_DIFF (1 << 14)
+#define XDF_HISTOGRAM_DIFF (1 << 15)
#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
-#define XDF_IGNORE_BLANK_LINES (1 << 7)
+#define XDF_INDENT_HEURISTIC (1 << 23)
+/* xdemitconf_t.flags */
#define XDL_EMIT_FUNCNAMES (1 << 0)
-#define XDL_EMIT_COMMON (1 << 1)
#define XDL_EMIT_FUNCCONTEXT (1 << 2)
#define XDL_MMB_READONLY (1 << 0)
typedef struct s_xpparam {
unsigned long flags;
+
+ /* See Documentation/diff-options.txt. */
+ char **anchors;
+ size_t anchors_nr;
} xpparam_t;
typedef struct s_xdemitcb {
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
-#include "common.h"
#include "integer.h"
#define XDL_SNAKE_CNT 20
#define XDL_K_HEUR 4
-
+/** Declare a function as always inlined. */
+#if defined(_MSC_VER)
+# define XDL_INLINE(type) static __inline type
+#else
+# define XDL_INLINE(type) static inline type
+#endif
typedef struct s_xdpsplit {
long i1, i2;
}
-int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
- long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
- char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
- xrecord_t **recs = xdf->recs;
+static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags)
+{
+ return (rec1->ha == rec2->ha &&
+ xdl_recmatch(rec1->ptr, rec1->size,
+ rec2->ptr, rec2->size,
+ flags));
+}
+
+/*
+ * If a line is indented more than this, get_indent() just returns this value.
+ * This avoids having to do absurd amounts of work for data that are not
+ * human-readable text, and also ensures that the output of get_indent fits within
+ * an int.
+ */
+#define MAX_INDENT 200
+
+/*
+ * Return the amount of indentation of the specified line, treating TAB as 8
+ * columns. Return -1 if line is empty or contains only whitespace. Clamp the
+ * output value at MAX_INDENT.
+ */
+static int get_indent(xrecord_t *rec)
+{
+ long i;
+ int ret = 0;
+
+ for (i = 0; i < rec->size; i++) {
+ char c = rec->ptr[i];
+
+ if (!XDL_ISSPACE(c))
+ return ret;
+ else if (c == ' ')
+ ret += 1;
+ else if (c == '\t')
+ ret += 8 - ret % 8;
+ /* ignore other whitespace characters */
+
+ if (ret >= MAX_INDENT)
+ return MAX_INDENT;
+ }
+ /* The line contains only whitespace. */
+ return -1;
+}
+
+/*
+ * If more than this number of consecutive blank rows are found, just return this
+ * value. This avoids requiring O(N^2) work for pathological cases, and also
+ * ensures that the output of score_split fits in an int.
+ */
+#define MAX_BLANKS 20
+
+/* Characteristics measured about a hypothetical split position. */
+struct split_measurement {
/*
- * This is the same of what GNU diff does. Move back and forward
- * change groups for a consistent and pretty diff output. This also
- * helps in finding joinable change groups and reduce the diff size.
+ * Is the split at the end of the file (aside from any blank lines)?
*/
- for (ix = ixo = 0;;) {
- /*
- * Find the first changed line in the to-be-compacted file.
- * We need to keep track of both indexes, so if we find a
- * changed lines group on the other file, while scanning the
- * to-be-compacted file, we need to skip it properly. Note
- * that loops that are testing for changed lines on rchg* do
- * not need index bounding since the array is prepared with
- * a zero at position -1 and N.
- */
- for (; ix < nrec && !rchg[ix]; ix++)
- while (rchgo[ixo++]);
- if (ix == nrec)
+ int end_of_file;
+
+ /*
+ * How much is the line immediately following the split indented (or -1 if
+ * the line is blank):
+ */
+ int indent;
+
+ /*
+ * How many consecutive lines above the split are blank?
+ */
+ int pre_blank;
+
+ /*
+ * How much is the nearest non-blank line above the split indented (or -1
+ * if there is no such line)?
+ */
+ int pre_indent;
+
+ /*
+ * How many lines after the line following the split are blank?
+ */
+ int post_blank;
+
+ /*
+ * How much is the nearest non-blank line after the line following the
+ * split indented (or -1 if there is no such line)?
+ */
+ int post_indent;
+};
+
+struct split_score {
+ /* The effective indent of this split (smaller is preferred). */
+ int effective_indent;
+
+ /* Penalty for this split (smaller is preferred). */
+ int penalty;
+};
+
+/*
+ * Fill m with information about a hypothetical split of xdf above line split.
+ */
+static void measure_split(const xdfile_t *xdf, long split,
+ struct split_measurement *m)
+{
+ long i;
+
+ if (split >= xdf->nrec) {
+ m->end_of_file = 1;
+ m->indent = -1;
+ } else {
+ m->end_of_file = 0;
+ m->indent = get_indent(xdf->recs[split]);
+ }
+
+ m->pre_blank = 0;
+ m->pre_indent = -1;
+ for (i = split - 1; i >= 0; i--) {
+ m->pre_indent = get_indent(xdf->recs[i]);
+ if (m->pre_indent != -1)
+ break;
+ m->pre_blank += 1;
+ if (m->pre_blank == MAX_BLANKS) {
+ m->pre_indent = 0;
break;
+ }
+ }
+ m->post_blank = 0;
+ m->post_indent = -1;
+ for (i = split + 1; i < xdf->nrec; i++) {
+ m->post_indent = get_indent(xdf->recs[i]);
+ if (m->post_indent != -1)
+ break;
+ m->post_blank += 1;
+ if (m->post_blank == MAX_BLANKS) {
+ m->post_indent = 0;
+ break;
+ }
+ }
+}
+
+/*
+ * The empirically-determined weight factors used by score_split() below.
+ * Larger values means that the position is a less favorable place to split.
+ *
+ * Note that scores are only ever compared against each other, so multiplying
+ * all of these weight/penalty values by the same factor wouldn't change the
+ * heuristic's behavior. Still, we need to set that arbitrary scale *somehow*.
+ * In practice, these numbers are chosen to be large enough that they can be
+ * adjusted relative to each other with sufficient precision despite using
+ * integer math.
+ */
+
+/* Penalty if there are no non-blank lines before the split */
+#define START_OF_FILE_PENALTY 1
+
+/* Penalty if there are no non-blank lines after the split */
+#define END_OF_FILE_PENALTY 21
+
+/* Multiplier for the number of blank lines around the split */
+#define TOTAL_BLANK_WEIGHT (-30)
+
+/* Multiplier for the number of blank lines after the split */
+#define POST_BLANK_WEIGHT 6
+
+/*
+ * Penalties applied if the line is indented more than its predecessor
+ */
+#define RELATIVE_INDENT_PENALTY (-4)
+#define RELATIVE_INDENT_WITH_BLANK_PENALTY 10
+
+/*
+ * Penalties applied if the line is indented less than both its predecessor and
+ * its successor
+ */
+#define RELATIVE_OUTDENT_PENALTY 24
+#define RELATIVE_OUTDENT_WITH_BLANK_PENALTY 17
+
+/*
+ * Penalties applied if the line is indented less than its predecessor but not
+ * less than its successor
+ */
+#define RELATIVE_DEDENT_PENALTY 23
+#define RELATIVE_DEDENT_WITH_BLANK_PENALTY 17
+
+/*
+ * We only consider whether the sum of the effective indents for splits are
+ * less than (-1), equal to (0), or greater than (+1) each other. The resulting
+ * value is multiplied by the following weight and combined with the penalty to
+ * determine the better of two scores.
+ */
+#define INDENT_WEIGHT 60
+
+/*
+ * Compute a badness score for the hypothetical split whose measurements are
+ * stored in m. The weight factors were determined empirically using the tools and
+ * corpus described in
+ *
+ * https://github.com/mhagger/diff-slider-tools
+ *
+ * Also see that project if you want to improve the weights based on, for example,
+ * a larger or more diverse corpus.
+ */
+static void score_add_split(const struct split_measurement *m, struct split_score *s)
+{
+ /*
+ * A place to accumulate penalty factors (positive makes this index more
+ * favored):
+ */
+ int post_blank, total_blank, indent, any_blanks;
+
+ if (m->pre_indent == -1 && m->pre_blank == 0)
+ s->penalty += START_OF_FILE_PENALTY;
+
+ if (m->end_of_file)
+ s->penalty += END_OF_FILE_PENALTY;
+
+ /*
+ * Set post_blank to the number of blank lines following the split,
+ * including the line immediately after the split:
+ */
+ post_blank = (m->indent == -1) ? 1 + m->post_blank : 0;
+ total_blank = m->pre_blank + post_blank;
+
+ /* Penalties based on nearby blank lines: */
+ s->penalty += TOTAL_BLANK_WEIGHT * total_blank;
+ s->penalty += POST_BLANK_WEIGHT * post_blank;
+
+ if (m->indent != -1)
+ indent = m->indent;
+ else
+ indent = m->post_indent;
+
+ any_blanks = (total_blank != 0);
+
+ /* Note that the effective indent is -1 at the end of the file: */
+ s->effective_indent += indent;
+
+ if (indent == -1) {
+ /* No additional adjustments needed. */
+ } else if (m->pre_indent == -1) {
+ /* No additional adjustments needed. */
+ } else if (indent > m->pre_indent) {
+ /*
+ * The line is indented more than its predecessor.
+ */
+ s->penalty += any_blanks ?
+ RELATIVE_INDENT_WITH_BLANK_PENALTY :
+ RELATIVE_INDENT_PENALTY;
+ } else if (indent == m->pre_indent) {
+ /*
+ * The line has the same indentation level as its predecessor.
+ * No additional adjustments needed.
+ */
+ } else {
/*
- * Record the start of a changed-group in the to-be-compacted file
- * and find the end of it, on both to-be-compacted and other file
- * indexes (ix and ixo).
+ * The line is indented less than its predecessor. It could be
+ * the block terminator of the previous block, but it could
+ * also be the start of a new block (e.g., an "else" block, or
+ * maybe the previous block didn't have a block terminator).
+ * Try to distinguish those cases based on what comes next:
*/
- ixs = ix;
- for (ix++; rchg[ix]; ix++);
- for (; rchgo[ixo]; ixo++);
+ if (m->post_indent != -1 && m->post_indent > indent) {
+ /*
+ * The following line is indented more. So it is likely
+ * that this line is the start of a block.
+ */
+ s->penalty += any_blanks ?
+ RELATIVE_OUTDENT_WITH_BLANK_PENALTY :
+ RELATIVE_OUTDENT_PENALTY;
+ } else {
+ /*
+ * That was probably the end of a block.
+ */
+ s->penalty += any_blanks ?
+ RELATIVE_DEDENT_WITH_BLANK_PENALTY :
+ RELATIVE_DEDENT_PENALTY;
+ }
+ }
+}
+
+static int score_cmp(struct split_score *s1, struct split_score *s2)
+{
+ /* -1 if s1.effective_indent < s2->effective_indent, etc. */
+ int cmp_indents = ((s1->effective_indent > s2->effective_indent) -
+ (s1->effective_indent < s2->effective_indent));
+
+ return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty);
+}
+
+/*
+ * Represent a group of changed lines in an xdfile_t (i.e., a contiguous group
+ * of lines that was inserted or deleted from the corresponding version of the
+ * file). We consider there to be such a group at the beginning of the file, at
+ * the end of the file, and between any two unchanged lines, though most such
+ * groups will usually be empty.
+ *
+ * If the first line in a group is equal to the line following the group, then
+ * the group can be slid down. Similarly, if the last line in a group is equal
+ * to the line preceding the group, then the group can be slid up. See
+ * group_slide_down() and group_slide_up().
+ *
+ * Note that loops that are testing for changed lines in xdf->rchg do not need
+ * index bounding since the array is prepared with a zero at position -1 and N.
+ */
+struct xdlgroup {
+ /*
+ * The index of the first changed line in the group, or the index of
+ * the unchanged line above which the (empty) group is located.
+ */
+ long start;
+
+ /*
+ * The index of the first unchanged line after the group. For an empty
+ * group, end is equal to start.
+ */
+ long end;
+};
+
+/*
+ * Initialize g to point at the first group in xdf.
+ */
+static void group_init(xdfile_t *xdf, struct xdlgroup *g)
+{
+ g->start = g->end = 0;
+ while (xdf->rchg[g->end])
+ g->end++;
+}
+
+/*
+ * Move g to describe the next (possibly empty) group in xdf and return 0. If g
+ * is already at the end of the file, do nothing and return -1.
+ */
+XDL_INLINE(int) group_next(xdfile_t *xdf, struct xdlgroup *g)
+{
+ if (g->end == xdf->nrec)
+ return -1;
+
+ g->start = g->end + 1;
+ for (g->end = g->start; xdf->rchg[g->end]; g->end++)
+ ;
+
+ return 0;
+}
+
+/*
+ * Move g to describe the previous (possibly empty) group in xdf and return 0.
+ * If g is already at the beginning of the file, do nothing and return -1.
+ */
+XDL_INLINE(int) group_previous(xdfile_t *xdf, struct xdlgroup *g)
+{
+ if (g->start == 0)
+ return -1;
+
+ g->end = g->start - 1;
+ for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)
+ ;
+
+ return 0;
+}
+
+/*
+ * If g can be slid toward the end of the file, do so, and if it bumps into a
+ * following group, expand this group to include it. Return 0 on success or -1
+ * if g cannot be slid down.
+ */
+static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags)
+{
+ if (g->end < xdf->nrec &&
+ recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) {
+ xdf->rchg[g->start++] = 0;
+ xdf->rchg[g->end++] = 1;
+
+ while (xdf->rchg[g->end])
+ g->end++;
+
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * If g can be slid toward the beginning of the file, do so, and if it bumps
+ * into a previous group, expand this group to include it. Return 0 on success
+ * or -1 if g cannot be slid up.
+ */
+static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags)
+{
+ if (g->start > 0 &&
+ recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) {
+ xdf->rchg[--g->start] = 1;
+ xdf->rchg[--g->end] = 0;
+
+ while (xdf->rchg[g->start - 1])
+ g->start--;
+
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static void xdl_bug(const char *msg)
+{
+ fprintf(stderr, "BUG: %s\n", msg);
+ exit(1);
+}
+
+/*
+ * Move back and forward change groups for a consistent and pretty diff output.
+ * This also helps in finding joinable change groups and reducing the diff
+ * size.
+ */
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
+ struct xdlgroup g, go;
+ long earliest_end, end_matching_other;
+ long groupsize;
+ group_init(xdf, &g);
+ group_init(xdfo, &go);
+
+ while (1) {
+ /* If the group is empty in the to-be-compacted file, skip it: */
+ if (g.end == g.start)
+ goto next;
+
+ /*
+ * Now shift the change up and then down as far as possible in
+ * each direction. If it bumps into any other changes, merge them.
+ */
do {
- grpsiz = ix - ixs;
+ groupsize = g.end - g.start;
/*
- * If the line before the current change group, is equal to
- * the last line of the current change group, shift backward
- * the group.
+ * Keep track of the last "end" index that causes this
+ * group to align with a group of changed lines in the
+ * other file. -1 indicates that we haven't found such
+ * a match yet:
*/
- while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
- xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
- rchg[--ixs] = 1;
- rchg[--ix] = 0;
-
- /*
- * This change might have joined two change groups,
- * so we try to take this scenario in account by moving
- * the start index accordingly (and so the other-file
- * end-of-group index).
- */
- for (; rchg[ixs - 1]; ixs--);
- while (rchgo[--ixo]);
- }
+ end_matching_other = -1;
- /*
- * Record the end-of-group position in case we are matched
- * with a group of changes in the other file (that is, the
- * change record before the end-of-group index in the other
- * file is set).
- */
- ixref = rchgo[ixo - 1] ? ix: nrec;
+ /* Shift the group backward as much as possible: */
+ while (!group_slide_up(xdf, &g, flags))
+ if (group_previous(xdfo, &go))
+ xdl_bug("group sync broken sliding up");
/*
- * If the first line of the current change group, is equal to
- * the line next of the current change group, shift forward
- * the group.
+ * This is this highest that this group can be shifted.
+ * Record its end index:
*/
- while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
- xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
- rchg[ixs++] = 0;
- rchg[ix++] = 1;
-
- /*
- * This change might have joined two change groups,
- * so we try to take this scenario in account by moving
- * the start index accordingly (and so the other-file
- * end-of-group index). Keep tracking the reference
- * index in case we are shifting together with a
- * corresponding group of changes in the other file.
- */
- for (; rchg[ix]; ix++);
- while (rchgo[++ixo])
- ixref = ix;
+ earliest_end = g.end;
+
+ if (go.end > go.start)
+ end_matching_other = g.end;
+
+ /* Now shift the group forward as far as possible: */
+ while (1) {
+ if (group_slide_down(xdf, &g, flags))
+ break;
+ if (group_next(xdfo, &go))
+ xdl_bug("group sync broken sliding down");
+
+ if (go.end > go.start)
+ end_matching_other = g.end;
}
- } while (grpsiz != ix - ixs);
+ } while (groupsize != g.end - g.start);
/*
- * Try to move back the possibly merged group of changes, to match
- * the recorded position in the other file.
+ * If the group can be shifted, then we can possibly use this
+ * freedom to produce a more intuitive diff.
+ *
+ * The group is currently shifted as far down as possible, so the
+ * heuristics below only have to handle upwards shifts.
*/
- while (ixref < ix) {
- rchg[--ixs] = 1;
- rchg[--ix] = 0;
- while (rchgo[--ixo]);
+
+ if (g.end == earliest_end) {
+ /* no shifting was possible */
+ } else if (end_matching_other != -1) {
+ /*
+ * Move the possibly merged group of changes back to line
+ * up with the last group of changes from the other file
+ * that it can align with.
+ */
+ while (go.end == go.start) {
+ if (group_slide_up(xdf, &g, flags))
+ xdl_bug("match disappeared");
+ if (group_previous(xdfo, &go))
+ xdl_bug("group sync broken sliding to match");
+ }
+ } else if (flags & XDF_INDENT_HEURISTIC) {
+ /*
+ * Indent heuristic: a group of pure add/delete lines
+ * implies two splits, one between the end of the "before"
+ * context and the start of the group, and another between
+ * the end of the group and the beginning of the "after"
+ * context. Some splits are aesthetically better and some
+ * are worse. We compute a badness "score" for each split,
+ * and add the scores for the two splits to define a
+ * "score" for each position that the group can be shifted
+ * to. Then we pick the shift with the lowest score.
+ */
+ long shift, best_shift = -1;
+ struct split_score best_score;
+
+ for (shift = earliest_end; shift <= g.end; shift++) {
+ struct split_measurement m;
+ struct split_score score = {0, 0};
+
+ measure_split(xdf, shift, &m);
+ score_add_split(&m, &score);
+ measure_split(xdf, shift - groupsize, &m);
+ score_add_split(&m, &score);
+ if (best_shift == -1 ||
+ score_cmp(&score, &best_score) <= 0) {
+ best_score.effective_indent = score.effective_indent;
+ best_score.penalty = score.penalty;
+ best_shift = shift;
+ }
+ }
+
+ while (g.end > best_shift) {
+ if (group_slide_up(xdf, &g, flags))
+ xdl_bug("best shift unreached");
+ if (group_previous(xdfo, &go))
+ xdl_bug("group sync broken sliding to blank line");
+ }
}
+
+ next:
+ /* Move past the just-processed group: */
+ if (group_next(xdf, &g))
+ break;
+ if (group_next(xdfo, &go))
+ xdl_bug("group sync broken moving to next group");
}
+ if (!group_next(xdfo, &go))
+ xdl_bug("group sync broken at end of file");
+
return 0;
}
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
#include "xinclude.h"
-
-
-
-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);
-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
-
-
-
-
static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
*rec = xdf->recs[ri]->ptr;
if (len > 0 &&
(isalpha((unsigned char)*rec) || /* identifier? */
- *rec == '_' || /* also identifier? */
+ *rec == '_' || /* also identifier? */
*rec == '$')) { /* identifiers from VMS and other esoterico */
if (len > sz)
len = sz;
return -1;
}
-static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
- xdemitconf_t const *xecfg) {
- xdfile_t *xdf = &xe->xdf2;
- const char *rchg = xdf->rchg;
- long ix;
-
- (void)xscr;
- (void)xecfg;
+static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,
+ char *buf, long sz)
+{
+ const char *rec;
+ long len = xdl_get_rec(xdf, ri, &rec);
+ if (!xecfg->find_func)
+ return def_ff(rec, len, buf, sz, xecfg->find_func_priv);
+ return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
+}
- for (ix = 0; ix < xdf->nrec; ix++) {
- if (rchg[ix])
- continue;
- if (xdl_emit_record(xdf, ix, "", ecb))
- return -1;
- }
- return 0;
+static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)
+{
+ char dummy[1];
+ return match_func_rec(xdf, xecfg, ri, dummy, sizeof(dummy)) >= 0;
}
struct func_line {
static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
struct func_line *func_line, long start, long limit)
{
- find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
long l, size, step = (start > limit) ? -1 : 1;
char *buf, dummy[1];
size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
- const char *rec;
- long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
- long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
+ long len = match_func_rec(&xe->xdf1, xecfg, l, buf, size);
if (len >= 0) {
if (func_line)
func_line->len = len;
return -1;
}
+static int is_empty_rec(xdfile_t *xdf, long ri)
+{
+ const char *rec;
+ long len = xdl_get_rec(xdf, ri, &rec);
+
+ while (len > 0 && XDL_ISSPACE(*rec)) {
+ rec++;
+ len--;
+ }
+ return !len;
+}
+
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg) {
long s1, s2, e1, e2, lctx;
long funclineprev = -1;
struct func_line func_line = { 0 };
- if (xecfg->flags & XDL_EMIT_COMMON)
- return xdl_emit_common(xe, xscr, ecb, xecfg);
-
for (xch = xscr; xch; xch = xche->next) {
xche = xdl_get_hunk(&xch, xecfg);
if (!xch)
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
- long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
+ long fs1, i1 = xch->i1;
+
+ /* Appended chunk? */
+ if (i1 >= xe->xdf1.nrec) {
+ long i2 = xch->i2;
+
+ /*
+ * We don't need additional context if
+ * a whole function was added.
+ */
+ while (i2 < xe->xdf2.nrec) {
+ if (is_func_rec(&xe->xdf2, xecfg, i2))
+ goto post_context_calculation;
+ i2++;
+ }
+
+ /*
+ * Otherwise get more context from the
+ * pre-image.
+ */
+ i1 = xe->xdf1.nrec - 1;
+ }
+
+ fs1 = get_func_line(xe, xecfg, NULL, i1, -1);
+ while (fs1 > 0 && !is_empty_rec(&xe->xdf1, fs1 - 1) &&
+ !is_func_rec(&xe->xdf1, xecfg, fs1 - 1))
+ fs1--;
if (fs1 < 0)
fs1 = 0;
if (fs1 < s1) {
}
}
- again:
+ post_context_calculation:
lctx = xecfg->ctxlen;
lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
long fe1 = get_func_line(xe, xecfg, NULL,
xche->i1 + xche->chg1,
xe->xdf1.nrec);
+ while (fe1 > 0 && is_empty_rec(&xe->xdf1, fe1 - 1))
+ fe1--;
if (fe1 < 0)
fe1 = xe->xdf1.nrec;
if (fe1 > e1) {
* its new end.
*/
if (xche->next) {
- long l = xche->next->i1;
- if (l <= e1 ||
+ long l = XDL_MIN(xche->next->i1,
+ xe->xdf1.nrec - 1);
+ if (l - xecfg->ctxlen <= e1 ||
get_func_line(xe, xecfg, NULL, l, e1) < 0) {
xche = xche->next;
- goto again;
+ goto post_context_calculation;
}
}
}
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
#include "xinclude.h"
#include "xtypes.h"
#include "xdiff.h"
-#include "common.h"
#define MAX_PTR UINT_MAX
#define MAX_CNT UINT_MAX
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
#include "xdiffi.h"
#include "xemit.h"
+#include "common.h"
#endif /* #if !defined(XINCLUDE_H) */
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
-#include "common.h"
typedef struct s_xdmerge {
struct s_xdmerge *next;
return 0;
}
-static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
{
xrecord_t **recs;
size_t size = 0;
if (add_nl) {
i = recs[count - 1]->size;
if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
+ if (needs_cr) {
+ if (dest)
+ dest[size] = '\r';
+ GITERR_CHECK_ALLOC_ADD(&size, size, 1);
+ }
+
if (dest)
dest[size] = '\n';
return 0;
}
-static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
{
- return xdl_recs_copy_0(out, 0, xe, i, count, add_nl, dest);
+ return xdl_recs_copy_0(out, 0, xe, i, count, needs_cr, add_nl, dest);
}
-static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
{
- return xdl_recs_copy_0(out, 1, xe, i, count, add_nl, dest);
+ return xdl_recs_copy_0(out, 1, xe, i, count, needs_cr, add_nl, dest);
+}
+
+/*
+ * Returns 1 if the i'th line ends in CR/LF (if it is the last line and
+ * has no eol, the preceding line, if any), 0 if it ends in LF-only, and
+ * -1 if the line ending cannot be determined.
+ */
+static int is_eol_crlf(xdfile_t *file, int i)
+{
+ long size;
+
+ if (i < file->nrec - 1)
+ /* All lines before the last *must* end in LF */
+ return (size = file->recs[i]->size) > 1 &&
+ file->recs[i]->ptr[size - 2] == '\r';
+ if (!file->nrec)
+ /* Cannot determine eol style from empty file */
+ return -1;
+ if ((size = file->recs[i]->size) &&
+ file->recs[i]->ptr[size - 1] == '\n')
+ /* Last line; ends in LF; Is it CR/LF? */
+ return size > 1 &&
+ file->recs[i]->ptr[size - 2] == '\r';
+ if (!i)
+ /* The only line has no eol */
+ return -1;
+ /* Determine eol from second-to-last line */
+ return (size = file->recs[i - 1]->size) > 1 &&
+ file->recs[i - 1]->ptr[size - 2] == '\r';
+}
+
+static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)
+{
+ int needs_cr;
+
+ /* Match post-images' preceding, or first, lines' end-of-line style */
+ needs_cr = is_eol_crlf(&xe1->xdf2, m->i1 ? m->i1 - 1 : 0);
+ if (needs_cr)
+ needs_cr = is_eol_crlf(&xe2->xdf2, m->i2 ? m->i2 - 1 : 0);
+ /* Look at pre-image's first line, unless we already settled on LF */
+ if (needs_cr)
+ needs_cr = is_eol_crlf(&xe1->xdf1, 0);
+ /* If still undecided, use LF-only */
+ return needs_cr < 0 ? 0 : needs_cr;
}
static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0);
int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0);
int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0);
+ int needs_cr = is_cr_needed(xe1, xe2, m);
size_t copied;
*out = 0;
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
/* Before conflicting part */
- if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0,
+ if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, 0,
dest ? dest + size : NULL) < 0)
return -1;
GITERR_CHECK_ALLOC_ADD(&size, size, copied);
if (!dest) {
- GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker1_size);
+ GITERR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker1_size);
} else {
memset(dest + size, '<', marker_size);
size += marker_size;
memcpy(dest + size + 1, name1, marker1_size - 1);
size += marker1_size;
}
+ if (needs_cr)
+ dest[size++] = '\r';
dest[size++] = '\n';
}
/* Postimage from side #1 */
- if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, 1,
+ if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, needs_cr, 1,
dest ? dest + size : NULL) < 0)
return -1;
if (style == XDL_MERGE_DIFF3) {
/* Shared preimage */
if (!dest) {
- GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker3_size);
+ GITERR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker3_size);
} else {
memset(dest + size, '|', marker_size);
size += marker_size;
memcpy(dest + size + 1, name3, marker3_size - 1);
size += marker3_size;
}
+ if (needs_cr)
+ dest[size++] = '\r';
dest[size++] = '\n';
}
- if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, 1,
+ if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, needs_cr, 1,
dest ? dest + size : NULL) < 0)
return -1;
GITERR_CHECK_ALLOC_ADD(&size, size, copied);
}
if (!dest) {
- GITERR_CHECK_ALLOC_ADD3(&size, size, marker_size, 1);
+ GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, needs_cr);
} else {
memset(dest + size, '=', marker_size);
size += marker_size;
+ if (needs_cr)
+ dest[size++] = '\r';
dest[size++] = '\n';
}
/* Postimage from side #2 */
- if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 1,
+ if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, needs_cr, 1,
dest ? dest + size : NULL) < 0)
return -1;
GITERR_CHECK_ALLOC_ADD(&size, size, copied);
if (!dest) {
- GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker2_size);
+ GITERR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker2_size);
} else {
memset(dest + size, '>', marker_size);
size += marker_size;
memcpy(dest + size + 1, name2, marker2_size - 1);
size += marker2_size;
}
+ if (needs_cr)
+ dest[size++] = '\r';
dest[size++] = '\n';
}
}
else if (m->mode & 3) {
/* Before conflicting part */
- if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0,
+ if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, 0,
dest ? dest + size : NULL) < 0)
return -1;
GITERR_CHECK_ALLOC_ADD(&size, size, copied);
/* Postimage from side #1 */
if (m->mode & 1) {
- if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, (m->mode & 2),
+ int needs_cr = is_cr_needed(xe1, xe2, m);
+
+ if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, needs_cr, (m->mode & 2),
dest ? dest + size : NULL) < 0)
return -1;
GITERR_CHECK_ALLOC_ADD(&size, size, copied);
/* Postimage from side #2 */
if (m->mode & 2) {
- if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0,
+ if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0, 0,
dest ? dest + size : NULL) < 0)
return -1;
GITERR_CHECK_ALLOC_ADD(&size, size, copied);
i = m->i1 + m->chg1;
}
- if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0,
+ if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0, 0,
dest ? dest + size : NULL) < 0)
return -1;
GITERR_CHECK_ALLOC_ADD(&size, size, copied);
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin
+ * Copyright (C) 2003-2016 Davide Libenzi, Johannes E. Schindelin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
* initially, "next" reflects only the order in file1.
*/
struct entry *next, *previous;
+
+ /*
+ * If 1, this entry can serve as an anchor. See
+ * Documentation/diff-options.txt for more information.
+ */
+ unsigned anchor : 1;
} *entries, *first, *last;
/* were common records found? */
unsigned long has_matches;
xpparam_t const *xpp;
};
+static int is_anchor(xpparam_t const *xpp, const char *line)
+{
+ unsigned long i;
+ for (i = 0; i < xpp->anchors_nr; i++) {
+ if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i])))
+ return 1;
+ }
+ return 0;
+}
+
/* The argument "pass" is 1 for the first file, 2 for the second. */
-static void insert_record(int line, struct hashmap *map, int pass)
+static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,
+ int pass)
{
xrecord_t **records = pass == 1 ?
map->env->xdf1.recs : map->env->xdf2.recs;
return;
map->entries[index].line1 = line;
map->entries[index].hash = record->ha;
+ map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);
if (!map->first)
map->first = map->entries + index;
if (map->last) {
/* First, fill with entries from the first file */
while (count1--)
- insert_record(line1++, result, 1);
+ insert_record(xpp, line1++, result, 1);
/* Then search for matches in the second file */
while (count2--)
- insert_record(line2++, result, 2);
+ insert_record(xpp, line2++, result, 2);
return 0;
}
int left = -1, right = longest;
while (left + 1 < right) {
- int middle = (left + right) / 2;
+ int middle = left + (right - left) / 2;
/* by construction, no two entries can be equal */
if (sequence[middle]->line2 > entry->line2)
right = middle;
int longest = 0, i;
struct entry *entry;
+ /*
+ * If not -1, this entry in sequence must never be overridden.
+ * Therefore, overriding entries before this has no effect, so
+ * do not do that either.
+ */
+ int anchor_i = -1;
+
for (entry = map->first; entry; entry = entry->next) {
if (!entry->line2 || entry->line2 == NON_UNIQUE)
continue;
i = binary_search(sequence, longest, entry);
entry->previous = i < 0 ? NULL : sequence[i];
- sequence[++i] = entry;
- if (i == longest)
+ ++i;
+ if (i <= anchor_i)
+ continue;
+ sequence[i] = entry;
+ if (entry->anchor) {
+ anchor_i = i;
+ longest = anchor_i + 1;
+ } else if (i == longest) {
longest++;
+ }
}
/* No common unique lines were found */
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
void *xdl_mmfile_first(mmfile_t *mmf, long *size)
{
- *size = (long)mmf->size;
+ *size = mmf->size;
return mmf->ptr;
}
long xdl_mmfile_size(mmfile_t *mmf)
{
- return (long)mmf->size;
+ return mmf->size;
}
return (i == size);
}
+/*
+ * Have we eaten everything on the line, except for an optional
+ * CR at the very end?
+ */
+static int ends_with_optional_cr(const char *l, long s, long i)
+{
+ int complete = s && l[s-1] == '\n';
+
+ if (complete)
+ s--;
+ if (s == i)
+ return 1;
+ /* do not ignore CR at the end of an incomplete line */
+ if (complete && s == i + 1 && l[i] == '\r')
+ return 1;
+ return 0;
+}
+
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
{
int i1, i2;
/*
* -w matches everything that matches with -b, and -b in turn
- * matches everything that matches with --ignore-space-at-eol.
+ * matches everything that matches with --ignore-space-at-eol,
+ * which in turn matches everything that matches with --ignore-cr-at-eol.
*
* Each flavor of ignoring needs different logic to skip whitespaces
* while we have both sides to compare.
return 0;
}
} else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
- while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
- ; /* keep going */
+ while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
+ i1++;
+ i2++;
+ }
+ } else if (flags & XDF_IGNORE_CR_AT_EOL) {
+ /* Find the first difference and see how the line ends */
+ while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
+ i1++;
+ i2++;
+ }
+ return (ends_with_optional_cr(l1, s1, i1) &&
+ ends_with_optional_cr(l2, s2, i2));
}
/*
char const *top, long flags) {
unsigned long ha = 5381;
char const *ptr = *data;
+ int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL;
for (; ptr < top && *ptr != '\n'; ptr++) {
- if (XDL_ISSPACE(*ptr)) {
+ if (cr_at_eol_only) {
+ /* do not ignore CR at the end of an incomplete line */
+ if (*ptr == '\r' &&
+ (ptr + 1 < top && ptr[1] == '\n'))
+ continue;
+ }
+ else if (XDL_ISSPACE(*ptr)) {
const char *ptr2 = ptr;
int at_eol;
while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
return ha;
}
-
unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
unsigned long ha = 5381;
char const *ptr = *data;
return ha;
}
-
unsigned int xdl_hashbits(unsigned int size) {
unsigned int val = 1, bits = 0;
*str++ = '0';
*str = '\0';
- return (int)(str - out);
-}
-
-
-long xdl_atol(char const *str, char const **next) {
- long val, base;
- char const *top;
-
- for (top = str; XDL_ISDIGIT(*top); top++);
- if (next)
- *next = top;
- for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
- val += base * (long)(*top - '0');
- return val;
+ return str - out;
}
-
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
const char *func, long funclen, xdemitcb_t *ecb) {
int nb = 0;
nb += 3;
if (func && funclen) {
buf[nb++] = ' ';
- if (funclen > (long)sizeof(buf) - nb - 1)
- funclen = (long)sizeof(buf) - nb - 1;
+ if (funclen > (long)(sizeof(buf) - nb - 1))
+ funclen = sizeof(buf) - nb - 1;
memcpy(buf + nb, func, funclen);
nb += funclen;
}
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
int xdl_cha_init(chastore_t *cha, long isize, long icount);
void xdl_cha_free(chastore_t *cha);
void *xdl_cha_alloc(chastore_t *cha);
-void *xdl_cha_first(chastore_t *cha);
-void *xdl_cha_next(chastore_t *cha);
long xdl_guess_lines(mmfile_t *mf, long sample);
int xdl_blankline(const char *line, long size, long flags);
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
unsigned long xdl_hash_record(char const **data, char const *top, long flags);
unsigned int xdl_hashbits(unsigned int size);
int xdl_num_out(char *out, long val);
-long xdl_atol(char const *str, char const **next);
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
const char *func, long funclen, xdemitcb_t *ecb);
int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "zstream.h"
+
#include <zlib.h>
-#include "zstream.h"
#include "buffer.h"
#define ZSTREAM_BUFFER_SIZE (1024 * 1024)
#define ZSTREAM_BUFFER_MIN_EXTRA 8
-static int zstream_seterr(git_zstream *zs)
+GIT_INLINE(int) zstream_seterr(git_zstream *zs)
{
- if (zs->zerr == Z_OK || zs->zerr == Z_STREAM_END)
+ switch (zs->zerr) {
+ case Z_OK:
+ case Z_STREAM_END:
+ case Z_BUF_ERROR: /* not fatal; we retry with a larger buffer */
return 0;
-
- if (zs->zerr == Z_MEM_ERROR)
+ case Z_MEM_ERROR:
giterr_set_oom();
- else if (zs->z.msg)
- giterr_set_str(GITERR_ZLIB, zs->z.msg);
- else
- giterr_set(GITERR_ZLIB, "unknown compression error");
+ break;
+ default:
+ if (zs->z.msg)
+ giterr_set_str(GITERR_ZLIB, zs->z.msg);
+ else
+ giterr_set(GITERR_ZLIB, "unknown compression error");
+ }
return -1;
}
return ZSTREAM_BUFFER_MIN_EXTRA;
}
+int git_zstream_get_output_chunk(
+ void *out, size_t *out_len, git_zstream *zstream)
+{
+ size_t in_queued, in_used, out_queued;
+
+ /* set up input data */
+ zstream->z.next_in = (Bytef *)zstream->in;
+
+ /* feed as much data to zlib as it can consume, at most UINT_MAX */
+ if (zstream->in_len > UINT_MAX) {
+ zstream->z.avail_in = UINT_MAX;
+ zstream->flush = Z_NO_FLUSH;
+ } else {
+ zstream->z.avail_in = (uInt)zstream->in_len;
+ zstream->flush = Z_FINISH;
+ }
+ in_queued = (size_t)zstream->z.avail_in;
+
+ /* set up output data */
+ zstream->z.next_out = out;
+ zstream->z.avail_out = (uInt)*out_len;
+
+ if ((size_t)zstream->z.avail_out != *out_len)
+ zstream->z.avail_out = UINT_MAX;
+ out_queued = (size_t)zstream->z.avail_out;
+
+ /* compress next chunk */
+ if (zstream->type == GIT_ZSTREAM_INFLATE)
+ zstream->zerr = inflate(&zstream->z, zstream->flush);
+ else
+ zstream->zerr = deflate(&zstream->z, zstream->flush);
+
+ if (zstream_seterr(zstream))
+ return -1;
+
+ in_used = (in_queued - zstream->z.avail_in);
+ zstream->in_len -= in_used;
+ zstream->in += in_used;
+
+ *out_len = (out_queued - zstream->z.avail_out);
+
+ return 0;
+}
+
int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
{
- int zflush = Z_FINISH;
size_t out_remain = *out_len;
if (zstream->in_len && zstream->zerr == Z_STREAM_END) {
}
while (out_remain > 0 && zstream->zerr != Z_STREAM_END) {
- size_t out_queued, in_queued, out_used, in_used;
-
- /* set up in data */
- zstream->z.next_in = (Bytef *)zstream->in;
- zstream->z.avail_in = (uInt)zstream->in_len;
- if ((size_t)zstream->z.avail_in != zstream->in_len) {
- zstream->z.avail_in = INT_MAX;
- zflush = Z_NO_FLUSH;
- } else {
- zflush = Z_FINISH;
- }
- in_queued = (size_t)zstream->z.avail_in;
-
- /* set up out data */
- zstream->z.next_out = out;
- zstream->z.avail_out = (uInt)out_remain;
- if ((size_t)zstream->z.avail_out != out_remain)
- zstream->z.avail_out = INT_MAX;
- out_queued = (size_t)zstream->z.avail_out;
-
- /* compress next chunk */
- if (zstream->type == GIT_ZSTREAM_INFLATE)
- zstream->zerr = inflate(&zstream->z, zflush);
- else
- zstream->zerr = deflate(&zstream->z, zflush);
-
- if (zstream->zerr == Z_STREAM_ERROR)
- return zstream_seterr(zstream);
+ size_t out_written = out_remain;
- out_used = (out_queued - zstream->z.avail_out);
- out_remain -= out_used;
- out = ((char *)out) + out_used;
+ if (git_zstream_get_output_chunk(out, &out_written, zstream) < 0)
+ return -1;
- in_used = (in_queued - zstream->z.avail_in);
- zstream->in_len -= in_used;
- zstream->in += in_used;
+ out_remain -= out_written;
+ out = ((char *)out) + out_written;
}
/* either we finished the input or we did not flush the data */
- assert(zstream->in_len > 0 || zflush == Z_FINISH);
+ assert(zstream->in_len > 0 || zstream->flush == Z_FINISH);
/* set out_size to number of bytes actually written to output */
*out_len = *out_len - out_remain;
#ifndef INCLUDE_zstream_h__
#define INCLUDE_zstream_h__
+#include "common.h"
+
#include <zlib.h>
-#include "common.h"
#include "buffer.h"
typedef enum {
git_zstream_t type;
const char *in;
size_t in_len;
+ int flush;
int zerr;
} git_zstream;
size_t git_zstream_suggest_output_len(git_zstream *zstream);
+/* get as much output as is available in the input buffer */
+int git_zstream_get_output_chunk(
+ void *out, size_t *out_len, git_zstream *zstream);
+
+/* get all the output from the entire input buffer */
int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream);
bool git_zstream_done(git_zstream *zstream);
int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len);
int git_zstream_inflatebuf(git_buf *out, const void *in, size_t in_len);
-#endif /* INCLUDE_zstream_h__ */
+#endif
--- /dev/null
+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}/resources/")
+SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
+ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
+ADD_DEFINITIONS(-DCLAR_TMPDIR=\"libgit2_tests\")
+
+INCLUDE_DIRECTORIES(${CLAR_PATH} ${libgit2_BINARY_DIR}/src)
+FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
+SET(SRC_CLAR "main.c" "clar_libgit2.c" "clar_libgit2_trace.c" "clar_libgit2_timer.c" "clar.c")
+
+IF(MSVC_IDE)
+ LIST(APPEND SRC_CLAR "precompiled.c")
+ENDIF()
+
+ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clar.suite
+ COMMAND ${PYTHON_EXECUTABLE} generate.py -o "${CMAKE_CURRENT_BINARY_DIR}" -f -xonline -xstress -xperf .
+ DEPENDS ${SRC_TEST}
+ WORKING_DIRECTORY ${CLAR_PATH}
+)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
+SET_SOURCE_FILES_PROPERTIES(
+ ${CLAR_PATH}/clar.c
+ PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clar.suite)
+
+LINK_DIRECTORIES(${LIBGIT2_LIBDIRS})
+INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES})
+
+ADD_EXECUTABLE(libgit2_clar ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS})
+
+SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+
+IF (${CMAKE_VERSION} VERSION_LESS 2.8.12)
+ # Already handled by a global INCLUDE_DIRECTORY()
+ELSE()
+ TARGET_INCLUDE_DIRECTORIES(libgit2_clar PRIVATE ../src PUBLIC ../include)
+ENDIF()
+
+TARGET_LINK_LIBRARIES(libgit2_clar ${LIBGIT2_LIBS})
+IDE_SPLIT_SOURCES(libgit2_clar)
+
+IF (MSVC_IDE)
+ # Precompiled headers
+ SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+ SET_SOURCE_FILES_PROPERTIES("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h")
+ENDIF ()
+
+IF (USE_HTTPS)
+ ADD_TEST(libgit2_clar "${libgit2_BINARY_DIR}/libgit2_clar" -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
+ELSE ()
+ ADD_TEST(libgit2_clar "${libgit2_BINARY_DIR}/libgit2_clar" -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
+ENDIF ()
+
+# Add additional test targets that require special setup
+ADD_TEST(libgit2_clar-proxy_credentials "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::clone::proxy_credentials_in_url -sonline::clone::proxy_credentials_request)
+ADD_TEST(libgit2_clar-ssh "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths)
assert_is_ignored(false, "NewFolder/NewFolder/File.txt");
}
+void test_attr_ignore__ignore_space(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder \n/NewFolder/NewFolder");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(true, "NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
+}
+
void test_attr_ignore__ignore_root(void)
{
cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder\n/NewFolder/NewFolder");
assert_is_ignored(true, "dist/foo.o");
assert_is_ignored(true, "bin/foo");
}
+
+void test_attr_ignore__unignore_dir_succeeds(void)
+{
+ cl_git_rewritefile("attr/.gitignore",
+ "*.c\n"
+ "!src/*.c\n");
+ assert_is_ignored(false, "src/foo.c");
+ assert_is_ignored(true, "src/foo/foo.c");
+}
+
+void test_attr_ignore__case_insensitive_unignores_previous_rule(void)
+{
+ git_config *cfg;
+
+ cl_git_rewritefile("attr/.gitignore",
+ "/case\n"
+ "!/Case/\n");
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "core.ignorecase", true));
+
+ cl_must_pass(p_mkdir("attr/case", 0755));
+ cl_git_mkfile("attr/case/file", "content");
+
+ assert_is_ignored(false, "case/file");
+}
+
+void test_attr_ignore__case_sensitive_unignore_does_nothing(void)
+{
+ git_config *cfg;
+
+ cl_git_rewritefile("attr/.gitignore",
+ "/case\n"
+ "!/Case/\n");
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "core.ignorecase", false));
+
+ cl_must_pass(p_mkdir("attr/case", 0755));
+ cl_git_mkfile("attr/case/file", "content");
+
+ assert_is_ignored(true, "case/file");
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "buffer.h"
+
+static void expect_decode_pass(const char *expected, const char *encoded)
+{
+ git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT;
+
+ /*
+ * ensure that we only read the given length of the input buffer
+ * by putting garbage at the end. this will ensure that we do
+ * not, eg, rely on nul-termination or walk off the end of the buf.
+ */
+ cl_git_pass(git_buf_puts(&in, encoded));
+ cl_git_pass(git_buf_PUTS(&in, "TRAILER"));
+
+ cl_git_pass(git_buf_decode_percent(&out, in.ptr, strlen(encoded)));
+
+ cl_assert_equal_s(expected, git_buf_cstr(&out));
+ cl_assert_equal_i(strlen(expected), git_buf_len(&out));
+
+ git_buf_free(&in);
+ git_buf_free(&out);
+}
+
+void test_buf_percent__decode_succeeds(void)
+{
+ expect_decode_pass("", "");
+ expect_decode_pass(" ", "%20");
+ expect_decode_pass("a", "a");
+ expect_decode_pass(" a", "%20a");
+ expect_decode_pass("a ", "a%20");
+ expect_decode_pass("github.com", "github.com");
+ expect_decode_pass("github.com", "githu%62.com");
+ expect_decode_pass("github.com", "github%2ecom");
+ expect_decode_pass("foo bar baz", "foo%20bar%20baz");
+ expect_decode_pass("foo bar baz", "foo%20bar%20baz");
+ expect_decode_pass("foo bar ", "foo%20bar%20");
+}
+
+void test_buf_percent__ignores_invalid(void)
+{
+ expect_decode_pass("githu%%.com", "githu%%.com");
+ expect_decode_pass("github.co%2", "github.co%2");
+ expect_decode_pass("github%2.com", "github%2.com");
+ expect_decode_pass("githu%2z.com", "githu%2z.com");
+ expect_decode_pass("github.co%9z", "github.co%9z");
+ expect_decode_pass("github.co%2", "github.co%2");
+ expect_decode_pass("github.co%", "github.co%");
+}
cl_assert(!git_path_isfile("testrepo/subdir/tracked-file"));
cl_assert(git_path_isfile("testrepo/subdir/untracked-file"));
}
+
+void test_checkout_head__typechange_workdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target;
+ struct stat st;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_revparse_single(&target, g_repo, "HEAD"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_chmod("testrepo/new.txt", 0755));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_git_pass(p_stat("testrepo/new.txt", &st));
+ cl_assert(!GIT_PERMS_IS_EXEC(st.st_mode));
+
+ git_object_free(target);
+}
+
+void test_checkout_head__typechange_index_and_workdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target;
+ git_index *index;
+ struct stat st;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_revparse_single(&target, g_repo, "HEAD"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_chmod("testrepo/new.txt", 0755));
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "new.txt"));
+ cl_git_pass(git_index_write(index));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_git_pass(p_stat("testrepo/new.txt", &st));
+ cl_assert(!GIT_PERMS_IS_EXEC(st.st_mode));
+
+ git_object_free(target);
+ git_index_free(index);
+}
+
+void test_checkout_head__workdir_filemode_is_simplified(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target, *branch;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_revparse_single(&target, g_repo, "a38d028f71eaa590febb7d716b1ca32350cf70da"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_chmod("testrepo/branch_file.txt", 0666));
+
+ /*
+ * Checkout should not fail with a conflict; though the file mode
+ * on disk is literally different to the base (0666 vs 0644), Git
+ * ignores the actual mode and simply treats both as non-executable.
+ */
+ cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
+
+ opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ cl_git_pass(git_checkout_tree(g_repo, branch, NULL));
+
+ git_object_free(branch);
+ git_object_free(target);
+}
+
+void test_checkout_head__obeys_filemode_true(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target, *branch;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ /* In this commit, `README` is executable */
+ cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_repo_set_bool(g_repo, "core.filemode", true);
+ cl_must_pass(p_chmod("testrepo/README", 0644));
+
+ /*
+ * Checkout will fail with a conflict; the file mode is updated in
+ * the checkout target, but the contents have changed in our branch.
+ */
+ cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
+
+ opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, branch, NULL));
+
+ git_object_free(branch);
+ git_object_free(target);
+}
+
+void test_checkout_head__obeys_filemode_false(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target, *branch;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ /* In this commit, `README` is executable */
+ cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_repo_set_bool(g_repo, "core.filemode", false);
+ cl_must_pass(p_chmod("testrepo/README", 0644));
+
+ /*
+ * Checkout will fail with a conflict; the file contents are updated
+ * in the checkout target, but the filemode has changed in our branch.
+ */
+ cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
+
+ opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ cl_git_pass(git_checkout_tree(g_repo, branch, NULL));
+
+ git_object_free(branch);
+ git_object_free(target);
+}
static git_checkout_options g_opts;
static git_object *g_object;
+static void assert_status_entrycount(git_repository *repo, size_t count)
+{
+ git_status_list *status;
+
+ cl_git_pass(git_status_list_new(&status, repo, NULL));
+ cl_assert_equal_i(count, git_status_list_entrycount(status));
+
+ git_status_list_free(status);
+}
+
void test_checkout_tree__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo");
cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
git_commit_free(commit);
+#else
+ cl_skip();
#endif
}
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_reference *head;
git_object *obj;
- git_status_list *status;
size_t conflicts = 0;
assert_on_branch(g_repo, "master");
opts.checkout_strategy |= GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
- cl_git_pass(git_status_list_new(&status, g_repo, NULL));
- cl_assert_equal_i(0, git_status_list_entrycount(status));
- git_status_list_free(status);
+ assert_status_entrycount(g_repo, 0);
git_object_free(obj);
git_reference_free(head);
}
+void test_checkout_tree__mode_change_is_force_updated(void)
+{
+ git_index *index;
+ git_reference *head;
+ git_object *obj;
+
+ if (!cl_is_chmod_supported())
+ clar__skip();
+
+ assert_on_branch(g_repo, "master");
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel(&obj, head, GIT_OBJ_COMMIT));
+
+ cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
+ assert_status_entrycount(g_repo, 0);
+
+ /* update the mode on-disk */
+ cl_must_pass(p_chmod("testrepo/README", 0755));
+
+ assert_status_entrycount(g_repo, 1);
+ cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
+ assert_status_entrycount(g_repo, 0);
+
+ /* update the mode on-disk and in the index */
+ cl_must_pass(p_chmod("testrepo/README", 0755));
+ cl_must_pass(git_index_add_bypath(index, "README"));
+
+ assert_status_entrycount(g_repo, 1);
+ cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
+ assert_status_entrycount(g_repo, 0);
+
+ git_object_free(obj);
+ git_reference_free(head);
+ git_index_free(index);
+}
+
void test_checkout_tree__nullopts(void)
{
cl_git_pass(git_checkout_tree(g_repo, NULL, NULL));
{
int i;
+ /* Verify options before execute */
for (i = 1; i < argc; ++i) {
char *argument = argv[i];
- if (argument[0] != '-')
+ if (argument[0] != '-' || argument[1] == '\0'
+ || strchr("sixvqQl", argument[1]) == NULL) {
clar_usage(argv[0]);
+ }
+ }
+
+ for (i = 1; i < argc; ++i) {
+ char *argument = argv[i];
switch (argument[1]) {
case 's':
break;
default:
- clar_usage(argv[0]);
+ assert(!"Unexpected commandline argument!");
}
}
}
{
(void)test_count;
printf("Loaded %d suites: %s\n", (int)suite_count, suite_names);
- printf("Started\n");
+ printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
}
static void clar_print_shutdown(int test_count, int suite_count, int error_count)
#include "clar_libgit2.h"
+#include "signature.h"
static int try_build_signature(const char *name, const char *email, git_time_t time, int offset)
{
git_signature_free(sign);
}
+void test_commit_signature__from_buf_with_neg_zero_offset(void)
+{
+ git_signature *sign;
+
+ cl_git_pass(git_signature_from_buffer(&sign, "Test User <test@test.tt> 1461698487 -0000"));
+ cl_assert_equal_s("Test User", sign->name);
+ cl_assert_equal_s("test@test.tt", sign->email);
+ cl_assert_equal_i(1461698487, sign->when.time);
+ cl_assert_equal_i(0, sign->when.offset);
+ cl_assert_equal_i('-', sign->when.sign);
+ git_signature_free(sign);
+}
+
+void test_commit_signature__pos_and_neg_zero_offsets_dont_match(void)
+{
+ git_signature *with_neg_zero;
+ git_signature *with_pos_zero;
+
+ cl_git_pass(git_signature_from_buffer(&with_neg_zero, "Test User <test@test.tt> 1461698487 -0000"));
+ cl_git_pass(git_signature_from_buffer(&with_pos_zero, "Test User <test@test.tt> 1461698487 +0000"));
+
+ cl_assert(!git_signature__equal(with_neg_zero, with_pos_zero));
+
+ git_signature_free((git_signature *)with_neg_zero);
+ git_signature_free((git_signature *)with_pos_zero);
+}
backend.version = 1024;
cl_git_pass(git_config_new(&cfg));
- cl_git_fail(git_config_add_backend(cfg, &backend, 0, false));
+ cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
err = giterr_last();
cl_assert_equal_i(GITERR_INVALID, err->klass);
giterr_clear();
backend.version = 1024;
- cl_git_fail(git_config_add_backend(cfg, &backend, 0, false));
+ cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
err = giterr_last();
cl_assert_equal_i(GITERR_INVALID, err->klass);
--- /dev/null
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "fileops.h"
+
+#ifdef GIT_WIN32
+# define ROOT_PREFIX "C:"
+#else
+# define ROOT_PREFIX
+#endif
+
+static git_repository *_repo;
+
+void test_config_conditionals__initialize(void)
+{
+ _repo = cl_git_sandbox_init("empty_standard_repo");
+}
+
+void test_config_conditionals__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_condition_includes(const char *keyword, const char *path, bool expected)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path);
+ git_buf_puts(&buf, "path = other\n");
+
+ cl_git_mkfile("empty_standard_repo/.git/config", buf.ptr);
+ cl_git_mkfile("empty_standard_repo/.git/other", "[foo]\nbar=baz\n");
+ _repo = cl_git_sandbox_reopen();
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+
+ if (expected) {
+ git_buf_clear(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_assert_equal_s("baz", git_buf_cstr(&buf));
+ } else {
+ cl_git_fail_with(GIT_ENOTFOUND,
+ git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ }
+
+ git_buf_free(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_conditionals__gitdir(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ char *sandbox_path;
+
+ assert_condition_includes("gitdir", ROOT_PREFIX "/", true);
+ assert_condition_includes("gitdir", "empty_standard_repo", true);
+ assert_condition_includes("gitdir", "empty_standard_repo/", true);
+ assert_condition_includes("gitdir", "./", true);
+
+ assert_condition_includes("gitdir", ROOT_PREFIX "/nonexistent", false);
+ assert_condition_includes("gitdir", ROOT_PREFIX "/empty_standard_repo", false);
+ assert_condition_includes("gitdir", "empty_stand", false);
+ assert_condition_includes("gitdir", "~/empty_standard_repo", false);
+
+ sandbox_path = p_realpath(clar_sandbox_path(), NULL);
+
+ git_buf_joinpath(&path, sandbox_path, "/");
+ assert_condition_includes("gitdir", path.ptr, true);
+
+ git_buf_joinpath(&path, sandbox_path, "/*");
+ assert_condition_includes("gitdir", path.ptr, true);
+
+ git_buf_joinpath(&path, sandbox_path, "empty_standard_repo");
+ assert_condition_includes("gitdir", path.ptr, true);
+
+ git_buf_joinpath(&path, sandbox_path, "Empty_Standard_Repo");
+ assert_condition_includes("gitdir", path.ptr, false);
+
+ git__free(sandbox_path);
+ git_buf_free(&path);
+}
+
+void test_config_conditionals__gitdir_i(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ char *sandbox_path;
+
+ sandbox_path = p_realpath(clar_sandbox_path(), NULL);
+
+ git_buf_joinpath(&path, sandbox_path, "empty_standard_repo");
+ assert_condition_includes("gitdir/i", path.ptr, true);
+
+ git_buf_joinpath(&path, sandbox_path, "EMPTY_STANDARD_REPO");
+ assert_condition_includes("gitdir/i", path.ptr, true);
+
+ git__free(sandbox_path);
+ git_buf_free(&path);
+}
+
+void test_config_conditionals__invalid_conditional_fails(void)
+{
+ assert_condition_includes("foobar", ".git", false);
+}
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
error = git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
- GIT_CONFIG_LEVEL_GLOBAL, 0);
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
cl_git_fail(error);
cl_assert_equal_i(GIT_EEXISTS, error);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
- GIT_CONFIG_LEVEL_LOCAL, 1));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
- GIT_CONFIG_LEVEL_LOCAL, 1));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
cl_assert_equal_s("don't find me!", buf.ptr);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
#include "buffer.h"
#include "fileops.h"
-void test_config_include__relative(void)
+static git_config *cfg;
+static git_buf buf;
+
+void test_config_include__initialize(void)
+{
+ cfg = NULL;
+ git_buf_init(&buf, 0);
+}
+
+void test_config_include__cleanup(void)
{
- git_config *cfg;
- git_buf buf = GIT_BUF_INIT;
+ git_config_free(cfg);
+ git_buf_free(&buf);
+}
+void test_config_include__relative(void)
+{
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-include")));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
-
- git_buf_free(&buf);
- git_config_free(cfg);
}
void test_config_include__absolute(void)
{
- git_config *cfg;
- git_buf buf = GIT_BUF_INIT;
-
cl_git_pass(git_buf_printf(&buf, "[include]\npath = %s/config-included", cl_fixture("config")));
cl_git_mkfile("config-include-absolute", git_buf_cstr(&buf));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
-
- git_buf_free(&buf);
- git_config_free(cfg);
}
void test_config_include__homedir(void)
{
- git_config *cfg;
- git_buf buf = GIT_BUF_INIT;
-
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
cl_git_mkfile("config-include-homedir", "[include]\npath = ~/config-included");
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
- git_buf_free(&buf);
- git_config_free(cfg);
-
cl_sandbox_set_search_path_defaults();
}
/* We need to pretend that the variables were defined where the file was included */
void test_config_include__ordering(void)
{
- git_config *cfg;
- git_buf buf = GIT_BUF_INIT;
-
cl_git_mkfile("included", "[foo \"bar\"]\nbaz = hurrah\nfrotz = hiya");
cl_git_mkfile("including",
"[foo \"bar\"]\nfrotz = hello\n"
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
-
- git_buf_free(&buf);
- git_config_free(cfg);
}
/* We need to pretend that the variables were defined where the file was included */
void test_config_include__depth(void)
{
- git_config *cfg;
-
cl_git_mkfile("a", "[include]\npath = b");
cl_git_mkfile("b", "[include]\npath = a");
void test_config_include__missing(void)
{
- git_config *cfg;
- git_buf buf = GIT_BUF_INIT;
-
cl_git_mkfile("including", "[include]\npath = nonexistentfile\n[foo]\nbar = baz");
giterr_clear();
cl_assert(giterr_last() == NULL);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf));
-
- git_buf_free(&buf);
- git_config_free(cfg);
}
void test_config_include__missing_homedir(void)
{
- git_config *cfg;
- git_buf buf = GIT_BUF_INIT;
-
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz");
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf));
- git_buf_free(&buf);
- git_config_free(cfg);
-
cl_sandbox_set_search_path_defaults();
}
#define replicate10(s) s s s s s s s s s s
void test_config_include__depth2(void)
{
- git_config *cfg;
- git_buf buf = GIT_BUF_INIT;
const char *content = "[include]\n" replicate10(replicate10("path=bottom\n"));
cl_git_mkfile("top-level", "[include]\npath = middle\n[foo]\nbar = baz");
git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2"));
cl_assert_equal_s("baz2", git_buf_cstr(&buf));
+}
- git_buf_free(&buf);
- git_config_free(cfg);
+void test_config_include__removing_include_removes_values(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = included");
+ cl_git_mkfile("included", "[foo]\nbar = value");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_mkfile("top-level", "");
+ cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+}
+
+void test_config_include__rewriting_include_refreshes_values(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = first\n[include]\npath = second");
+ cl_git_mkfile("first", "[first]\nfoo = bar");
+ cl_git_mkfile("second", "[second]\nfoo = bar");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_mkfile("first", "[first]\nother = value");
+ cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "first.other"));
+ cl_assert_equal_s(buf.ptr, "value");
+}
+
+void test_config_include__included_variables_cannot_be_deleted(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = included\n");
+ cl_git_mkfile("included", "[foo]\nbar = value");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_fail(git_config_delete_entry(cfg, "foo.bar"));
+}
+
+void test_config_include__included_variables_cannot_be_modified(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = included\n");
+ cl_git_mkfile("included", "[foo]\nbar = value");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_fail(git_config_set_string(cfg, "foo.bar", "other-value"));
}
check_get_multivar_foreach(cfg, 2, 1);
/* add another that has the _name entry */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* add another that does not have the _name entry */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* add another that does not have the _name entry at the end */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, NULL, 1));
check_get_multivar_foreach(cfg, 3, 2);
/* drop original file */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
check_get_multivar_foreach(cfg, 1, 1);
/* drop other file with match */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 0, 0);
/* reload original file (add different place in order) */
- cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
check_get_multivar_foreach(cfg, 2, 1);
check_get_multivar(cfg, 2);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
count = 0;
cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
count = 0;
cl_git_pass(git_config_iterator_new(&iter, cfg));
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_get_entry(&entry, cfg, "core.dummy2"));
cl_assert_equal_s("core.dummy2", entry->name);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
cl_assert_equal_i(28, i);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
cl_assert_equal_i(7, i);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_get_int32(&i, cfg, "core.global"));
cl_assert_equal_i(17, i);
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
git_buf_free(&expected_path);
git_config_free(cfg);
}
+
+void test_config_read__crlf_style_line_endings(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some]\r\n var = value\r\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ git_buf_free(&buf);
+}
+
+void test_config_read__trailing_crlf(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some]\r\n var = value\r\n\r\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ git_buf_free(&buf);
+}
+
+void test_config_read__bom(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some]\n var = value\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ git_buf_free(&buf);
+}
#include "clar_libgit2.h"
#include "config_file.h"
#include "config.h"
+#include "path.h"
static git_config *cfg;
cl_git_pass(git_config_file__ondisk(&backend, "global"));
backend->readonly = 1;
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
cl_assert(!git_path_exists("global"));
cl_git_pass(git_config_file__ondisk(&backend, "global"));
backend->readonly = 1;
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_file__ondisk(&backend, "local"));
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
cl_git_pass(git_config_file__ondisk(&backend, "local"));
backend->readonly = 1;
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_file__ondisk(&backend, "global"));
- cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
// open config15 as global level config file
cl_git_pass(git_config_new(&cfg));
cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
- GIT_CONFIG_LEVEL_LOCAL, 0));
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
- GIT_CONFIG_LEVEL_GLOBAL, 0));
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
git_config_free(cfg);
}
+
+void test_config_write__preserve_case(void)
+{
+ const char *filename = "config-preserve-case";
+ git_config *cfg;
+ git_buf result = GIT_BUF_INIT;
+ const char *expected = "[sOMe]\n" \
+ "\tThInG = foo\n" \
+ "\tOtheR = thing\n";
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
+ cl_git_pass(git_config_set_string(cfg, "SomE.OtheR", "thing"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+
+ cl_git_pass(git_futils_readbuffer(&result, filename));
+ cl_assert_equal_s(expected, result.ptr);
+ git_buf_free(&result);
+
+ git_config_free(cfg);
+}
#include "clar_libgit2.h"
#include "git2/sys/stream.h"
-#include "tls_stream.h"
+#include "streams/tls.h"
#include "stream.h"
static git_stream test_stream;
* or when openssl support is disabled (except on OSX
* with Security framework).
*/
-#if defined(GIT_WIN32) || \
- (!defined(GIT_SECURE_TRANSPORT) && !defined(GIT_OPENSSL))
+#if defined(GIT_WIN32) || !defined(GIT_HTTPS)
cl_git_fail_with(-1, error);
#else
cl_git_pass(error);
cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0);
}
+/* compare prefixes with len */
+void test_core_string__prefixncmp(void)
+{
+ cl_assert(git__prefixncmp("", 0, "") == 0);
+ cl_assert(git__prefixncmp("a", 1, "") == 0);
+ cl_assert(git__prefixncmp("", 0, "a") < 0);
+ cl_assert(git__prefixncmp("a", 1, "b") < 0);
+ cl_assert(git__prefixncmp("b", 1, "a") > 0);
+ cl_assert(git__prefixncmp("ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp("ab", 1, "a") == 0);
+ cl_assert(git__prefixncmp("ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp("a", 1, "ac") < 0);
+ cl_assert(git__prefixncmp("ab", 1, "ac") < 0);
+ cl_assert(git__prefixncmp("ab", 2, "aa") > 0);
+ cl_assert(git__prefixncmp("ab", 1, "aa") < 0);
+}
+
+/* compare prefixes with len */
+void test_core_string__prefixncmp_icase(void)
+{
+ cl_assert(git__prefixncmp_icase("", 0, "") == 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "") == 0);
+ cl_assert(git__prefixncmp_icase("", 0, "a") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "b") < 0);
+ cl_assert(git__prefixncmp_icase("A", 1, "b") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "B") < 0);
+ cl_assert(git__prefixncmp_icase("b", 1, "a") > 0);
+ cl_assert(git__prefixncmp_icase("B", 1, "a") > 0);
+ cl_assert(git__prefixncmp_icase("b", 1, "A") > 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp_icase("Ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "A") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "a") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("Ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "Ac") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "aa") > 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "aa") < 0);
+}
+
void test_core_string__strcmp(void)
{
cl_assert(git__strcmp("", "") == 0);
CHECK_MACRO_FUNC_INIT_EQUAL( \
git_proxy_options, GIT_PROXY_OPTIONS_VERSION, \
GIT_PROXY_OPTIONS_INIT, git_proxy_init_options);
+
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_VERSION, \
+ GIT_DIFF_PATCHID_OPTIONS_INIT, git_diff_patchid_init_options);
}
#include "clar_libgit2.h"
#include "diff_helpers.h"
+#define BLOB_DIFF \
+ "diff --git a/file b/file\n" \
+ "index 45141a7..4d713dc 100644\n" \
+ "--- a/file\n" \
+ "+++ b/file\n" \
+ "@@ -1 +1,6 @@\n" \
+ " Hello from the root\n" \
+ "+\n" \
+ "+Some additional lines\n" \
+ "+\n" \
+ "+Down here below\n" \
+ "+\n"
+
static git_repository *g_repo = NULL;
static diff_expects expected;
static git_diff_options opts;
cl_assert_equal_i(dels, exp->line_dels);
}
+void test_diff_blob__patch_with_freed_blobs(void)
+{
+ git_oid a_oid, b_oid;
+ git_blob *a, *b;
+ git_patch *p;
+ git_buf buf = GIT_BUF_INIT;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
+ /* tests/resources/attr/root_test2 */
+ cl_git_pass(git_oid_fromstrn(&b_oid, "4d713dc4", 8));
+ cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));
+
+ cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, NULL));
+
+ git_blob_free(a);
+ git_blob_free(b);
+
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(buf.ptr, BLOB_DIFF);
+
+ git_patch_free(p);
+ git_buf_free(&buf);
+}
+
void test_diff_blob__can_compare_text_blobs(void)
{
git_blob *a, *b, *c;
git_buf_free(&buf);
}
+void test_diff_parse__exact_rename(void)
+{
+ const char *content =
+ "---\n"
+ " old_name.c => new_name.c | 0\n"
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n"
+ " rename old_name.c => new_name.c (100%)\n"
+ "\n"
+ "diff --git a/old_name.c b/new_name.c\n"
+ "similarity index 100%\n"
+ "rename from old_name.c\n"
+ "rename to new_name.c\n"
+ "-- \n"
+ "2.9.3\n";
+ git_diff *diff;
+
+ cl_git_pass(git_diff_from_buffer(
+ &diff, content, strlen(content)));
+ git_diff_free(diff);
+}
+
void test_diff_parse__invalid_patches_fails(void)
{
test_parse_invalid_diff(PATCH_CORRUPT_MISSING_NEW_FILE);
--- /dev/null
+#include "clar_libgit2.h"
+#include "patch/patch_common.h"
+
+static void verify_patch_id(const char *diff_content, const char *expected_id)
+{
+ git_oid expected_oid, actual_oid;
+ git_diff *diff;
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected_id));
+ cl_git_pass(git_diff_from_buffer(&diff, diff_content, strlen(diff_content)));
+ cl_git_pass(git_diff_patchid(&actual_oid, diff, NULL));
+
+ cl_assert_equal_oid(&expected_oid, &actual_oid);
+
+ git_diff_free(diff);
+}
+
+void test_diff_patchid__simple_commit(void)
+{
+ verify_patch_id(PATCH_SIMPLE_COMMIT, "06094b1948b878b7d9ff7560b4eae672a014b0ec");
+}
+
+void test_diff_patchid__filename_with_spaces(void)
+{
+ verify_patch_id(PATCH_APPEND_NO_NL, "f0ba05413beaef743b630e796153839462ee477a");
+}
+
+void test_diff_patchid__multiple_hunks(void)
+{
+ verify_patch_id(PATCH_MULTIPLE_HUNKS, "81e26c34643d17f521e57c483a6a637e18ba1f57");
+}
+
+void test_diff_patchid__multiple_files(void)
+{
+ verify_patch_id(PATCH_MULTIPLE_FILES, "192d1f49d23f2004517963aecd3f8a6c467f50ff");
+}
+
+void test_diff_patchid__same_diff_with_differing_whitespace_has_same_id(void)
+{
+ const char *tabs =
+ "diff --git a/file.txt b/file.txt\n"
+ "index 8fecc09..1d43a92 100644\n"
+ "--- a/file.txt\n"
+ "+++ b/file.txt\n"
+ "@@ -1 +1 @@\n"
+ "-old text\n"
+ "+ new text\n";
+ const char *spaces =
+ "diff --git a/file.txt b/file.txt\n"
+ "index 8fecc09..1d43a92 100644\n"
+ "--- a/file.txt\n"
+ "+++ b/file.txt\n"
+ "@@ -1 +1 @@\n"
+ "-old text\n"
+ "+ new text\n";
+ const char *id = "11efdd13c30f7a1056eac2ae2fb952da475e2c23";
+
+ verify_patch_id(tabs, id);
+ verify_patch_id(spaces, id);
+}
cl_git_sandbox_cleanup();
}
+#define INITIAL_COMMIT "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2"
+#define COPY_RENAME_COMMIT "2bc7f351d20b53f1c72c16c4b036e491c478c49a"
+#define REWRITE_COPY_COMMIT "1c068dee5790ef1580cfc4cd670915b48d790084"
+#define RENAME_MODIFICATION_COMMIT "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"
+#define REWRITE_DELETE_COMMIT "84d8efa38af7ace2b302de0adbda16b1f1cd2e1b"
+#define DELETE_RENAME_COMMIT "be053a189b0bbde545e0a3f59ce00b46ad29ce0d"
+
/*
* Renames repo has:
*
* ikeepsix.txt -> ikeepsix.txt (reorder sections in file)
* sixserving.txt -> sixserving.txt (whitespace change - not just indent)
* sevencities.txt -> songof7cities.txt (rename, small text changes)
+ * commit 84d8efa38af7ace2b302de0adbda16b1f1cd2e1b
+ * songof7cities.txt -> songof7citie.txt (major rewrite, <20% match)
+ * ikeepsix.txt -> (deleted)
+ * untimely.txt (no change)
+ * sixserving.txt (no change)
+ * commit be053a189b0bbde545e0a3f59ce00b46ad29ce0d
+ * ikeepsix.txt -> (deleted)
+ * songof7cities.txt -> ikeepsix.txt (rename, 100% match)
+ * untimely.txt (no change)
+ * sixserving.txt (no change)
*/
void test_diff_rename__match_oid(void)
{
- const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
- const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ const char *old_sha = INITIAL_COMMIT;
+ const char *new_sha = COPY_RENAME_COMMIT;
git_tree *old_tree, *new_tree;
git_diff *diff;
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
void test_diff_rename__checks_options_version(void)
{
- const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
- const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ const char *old_sha = INITIAL_COMMIT;
+ const char *new_sha = COPY_RENAME_COMMIT;
git_tree *old_tree, *new_tree;
git_diff *diff;
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
void test_diff_rename__not_exact_match(void)
{
- const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
- const char *sha2 = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
+ const char *sha0 = COPY_RENAME_COMMIT;
+ const char *sha1 = REWRITE_COPY_COMMIT;
+ const char *sha2 = RENAME_MODIFICATION_COMMIT;
git_tree *old_tree, *new_tree;
git_diff *diff;
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
void test_diff_rename__working_directory_changes(void)
{
- const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ const char *sha0 = COPY_RENAME_COMMIT;
const char *blobsha = "66311f5cfbe7836c27510a3ba2f43e282e2c8bba";
git_oid id;
git_tree *tree;
void test_diff_rename__patch(void)
{
- const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
+ const char *sha0 = COPY_RENAME_COMMIT;
+ const char *sha1 = REWRITE_COPY_COMMIT;
git_tree *old_tree, *new_tree;
git_diff *diff;
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
void test_diff_rename__matches_config_behavior(void)
{
- const char *sha0 = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
- const char *sha1 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- const char *sha2 = "1c068dee5790ef1580cfc4cd670915b48d790084";
+ const char *sha0 = INITIAL_COMMIT;
+ const char *sha1 = COPY_RENAME_COMMIT;
+ const char *sha2 = REWRITE_COPY_COMMIT;
git_tree *tree0, *tree1, *tree2;
git_config *cfg;
void test_diff_rename__can_override_thresholds_when_obeying_config(void)
{
- const char *sha1 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- const char *sha2 = "1c068dee5790ef1580cfc4cd670915b48d790084";
+ const char *sha1 = COPY_RENAME_COMMIT;
+ const char *sha2 = REWRITE_COPY_COMMIT;
git_tree *tree1, *tree2;
git_config *cfg;
void test_diff_rename__by_config_doesnt_mess_with_whitespace_settings(void)
{
- const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
- const char *sha2 = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
+ const char *sha1 = REWRITE_COPY_COMMIT;
+ const char *sha2 = RENAME_MODIFICATION_COMMIT;
git_tree *tree1, *tree2;
git_config *cfg;
*/
void test_diff_rename__identical(void)
{
- const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
- const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ const char *old_sha = INITIAL_COMMIT;
+ const char *new_sha = COPY_RENAME_COMMIT;
git_tree *old_tree, *new_tree;
git_diff *diff;
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
git_tree_free(new_tree);
}
+void test_diff_rename__rewrite_and_delete(void)
+{
+ const char *old_sha = RENAME_MODIFICATION_COMMIT;
+ const char *new_sha = REWRITE_DELETE_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_buf diff_buf = GIT_BUF_INIT;
+ const char *expected =
+ "diff --git a/ikeepsix.txt b/ikeepsix.txt\n"
+ "deleted file mode 100644\n"
+ "index eaf4a3e..0000000\n"
+ "--- a/ikeepsix.txt\n"
+ "+++ /dev/null\n"
+ "@@ -1,27 +0,0 @@\n"
+ "-I Keep Six Honest Serving-Men\n"
+ "-=============================\n"
+ "-\n"
+ "-She sends'em abroad on her own affairs,\n"
+ "- From the second she opens her eyes—\n"
+ "-One million Hows, two million Wheres,\n"
+ "-And seven million Whys!\n"
+ "-\n"
+ "-I let them rest from nine till five,\n"
+ "- For I am busy then,\n"
+ "-As well as breakfast, lunch, and tea,\n"
+ "- For they are hungry men.\n"
+ "-But different folk have different views;\n"
+ "-I know a person small—\n"
+ "-She keeps ten million serving-men,\n"
+ "-Who get no rest at all!\n"
+ "-\n"
+ "- -- Rudyard Kipling\n"
+ "-\n"
+ "-I KEEP six honest serving-men\n"
+ "- (They taught me all I knew);\n"
+ "-Their names are What and Why and When\n"
+ "- And How and Where and Who.\n"
+ "-I send them over land and sea,\n"
+ "- I send them east and west;\n"
+ "-But after they have worked for me,\n"
+ "- I give them all a rest.\n"
+ "diff --git a/songof7cities.txt b/songof7cities.txt\n"
+ "index 4210ffd..95ceb12 100644\n"
+ "--- a/songof7cities.txt\n"
+ "+++ b/songof7cities.txt\n"
+ "@@ -1,45 +1,45 @@\n"
+ "-The Song of Seven Cities\n"
+ "+THE SONG OF SEVEN CITIES\n"
+ " ------------------------\n"
+ " \n"
+ "-I WAS Lord of Cities very sumptuously builded.\n"
+ "-Seven roaring Cities paid me tribute from afar.\n"
+ "-Ivory their outposts were--the guardrooms of them gilded,\n"
+ "-And garrisoned with Amazons invincible in war.\n"
+ "-\n"
+ "-All the world went softly when it walked before my Cities--\n"
+ "-Neither King nor Army vexed my peoples at their toil,\n"
+ "-Never horse nor chariot irked or overbore my Cities,\n"
+ "-Never Mob nor Ruler questioned whence they drew their spoil.\n"
+ "-\n"
+ "-Banded, mailed and arrogant from sunrise unto sunset;\n"
+ "-Singing while they sacked it, they possessed the land at large.\n"
+ "-Yet when men would rob them, they resisted, they made onset\n"
+ "-And pierced the smoke of battle with a thousand-sabred charge.\n"
+ "-\n"
+ "-So they warred and trafficked only yesterday, my Cities.\n"
+ "-To-day there is no mark or mound of where my Cities stood.\n"
+ "-For the River rose at midnight and it washed away my Cities.\n"
+ "-They are evened with Atlantis and the towns before the Flood.\n"
+ "-\n"
+ "-Rain on rain-gorged channels raised the water-levels round them,\n"
+ "-Freshet backed on freshet swelled and swept their world from sight,\n"
+ "-Till the emboldened floods linked arms and, flashing forward, drowned them--\n"
+ "-Drowned my Seven Cities and their peoples in one night!\n"
+ "-\n"
+ "-Low among the alders lie their derelict foundations,\n"
+ "-The beams wherein they trusted and the plinths whereon they built--\n"
+ "-My rulers and their treasure and their unborn populations,\n"
+ "-Dead, destroyed, aborted, and defiled with mud and silt!\n"
+ "-\n"
+ "-The Daughters of the Palace whom they cherished in my Cities,\n"
+ "-My silver-tongued Princesses, and the promise of their May--\n"
+ "-Their bridegrooms of the June-tide--all have perished in my Cities,\n"
+ "-With the harsh envenomed virgins that can neither love nor play.\n"
+ "-\n"
+ "-I was Lord of Cities--I will build anew my Cities,\n"
+ "-Seven, set on rocks, above the wrath of any flood.\n"
+ "-Nor will I rest from search till I have filled anew my Cities\n"
+ "-With peoples undefeated of the dark, enduring blood.\n"
+ "+I WAS LORD OF CITIES VERY SUMPTUOUSLY BUILDED.\n"
+ "+SEVEN ROARING CITIES PAID ME TRIBUTE FROM AFAR.\n"
+ "+IVORY THEIR OUTPOSTS WERE--THE GUARDROOMS OF THEM GILDED,\n"
+ "+AND GARRISONED WITH AMAZONS INVINCIBLE IN WAR.\n"
+ "+\n"
+ "+ALL THE WORLD WENT SOFTLY WHEN IT WALKED BEFORE MY CITIES--\n"
+ "+NEITHER KING NOR ARMY VEXED MY PEOPLES AT THEIR TOIL,\n"
+ "+NEVER HORSE NOR CHARIOT IRKED OR OVERBORE MY CITIES,\n"
+ "+NEVER MOB NOR RULER QUESTIONED WHENCE THEY DREW THEIR SPOIL.\n"
+ "+\n"
+ "+BANDED, MAILED AND ARROGANT FROM SUNRISE UNTO SUNSET;\n"
+ "+SINGING WHILE THEY SACKED IT, THEY POSSESSED THE LAND AT LARGE.\n"
+ "+YET WHEN MEN WOULD ROB THEM, THEY RESISTED, THEY MADE ONSET\n"
+ "+AND PIERCED THE SMOKE OF BATTLE WITH A THOUSAND-SABRED CHARGE.\n"
+ "+\n"
+ "+SO THEY WARRED AND TRAFFICKED ONLY YESTERDAY, MY CITIES.\n"
+ "+TO-DAY THERE IS NO MARK OR MOUND OF WHERE MY CITIES STOOD.\n"
+ "+FOR THE RIVER ROSE AT MIDNIGHT AND IT WASHED AWAY MY CITIES.\n"
+ "+THEY ARE EVENED WITH ATLANTIS AND THE TOWNS BEFORE THE FLOOD.\n"
+ "+\n"
+ "+RAIN ON RAIN-GORGED CHANNELS RAISED THE WATER-LEVELS ROUND THEM,\n"
+ "+FRESHET BACKED ON FRESHET SWELLED AND SWEPT THEIR WORLD FROM SIGHT,\n"
+ "+TILL THE EMBOLDENED FLOODS LINKED ARMS AND, FLASHING FORWARD, DROWNED THEM--\n"
+ "+DROWNED MY SEVEN CITIES AND THEIR PEOPLES IN ONE NIGHT!\n"
+ "+\n"
+ "+LOW AMONG THE ALDERS LIE THEIR DERELICT FOUNDATIONS,\n"
+ "+THE BEAMS WHEREIN THEY TRUSTED AND THE PLINTHS WHEREON THEY BUILT--\n"
+ "+MY RULERS AND THEIR TREASURE AND THEIR UNBORN POPULATIONS,\n"
+ "+DEAD, DESTROYED, ABORTED, AND DEFILED WITH MUD AND SILT!\n"
+ "+\n"
+ "+THE DAUGHTERS OF THE PALACE WHOM THEY CHERISHED IN MY CITIES,\n"
+ "+MY SILVER-TONGUED PRINCESSES, AND THE PROMISE OF THEIR MAY--\n"
+ "+THEIR BRIDEGROOMS OF THE JUNE-TIDE--ALL HAVE PERISHED IN MY CITIES,\n"
+ "+WITH THE HARSH ENVENOMED VIRGINS THAT CAN NEITHER LOVE NOR PLAY.\n"
+ "+\n"
+ "+I WAS LORD OF CITIES--I WILL BUILD ANEW MY CITIES,\n"
+ "+SEVEN, SET ON ROCKS, ABOVE THE WRATH OF ANY FLOOD.\n"
+ "+NOR WILL I REST FROM SEARCH TILL I HAVE FILLED ANEW MY CITIES\n"
+ "+WITH PEOPLES UNDEFEATED OF THE DARK, ENDURING BLOOD.\n"
+ " \n"
+ " To the sound of trumpets shall their seed restore my Cities\n"
+ " Wealthy and well-weaponed, that once more may I behold\n";
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ find_opts.flags = GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, old_tree, new_tree, NULL));
+ cl_git_pass(git_diff_find_similar(diff, &find_opts));
+
+ cl_git_pass(git_diff_to_buf(&diff_buf, diff, GIT_DIFF_FORMAT_PATCH));
+
+ cl_assert_equal_s(expected, diff_buf.ptr);
+
+ git_buf_free(&diff_buf);
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__delete_and_rename(void)
+{
+ const char *old_sha = RENAME_MODIFICATION_COMMIT;
+ const char *new_sha = DELETE_RENAME_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_buf diff_buf = GIT_BUF_INIT;
+ const char *expected =
+ "diff --git a/sixserving.txt b/sixserving.txt\n"
+ "deleted file mode 100644\n"
+ "index f90d4fc..0000000\n"
+ "--- a/sixserving.txt\n"
+ "+++ /dev/null\n"
+ "@@ -1,25 +0,0 @@\n"
+ "-I KEEP six honest serving-men\n"
+ "- (They taught me all I knew);\n"
+ "-Their names are What and Why and When\n"
+ "- And How and Where and Who.\n"
+ "-I send them over land and sea,\n"
+ "- I send them east and west;\n"
+ "-But after they have worked for me,\n"
+ "- I give them all a rest.\n"
+ "-\n"
+ "-I let them rest from nine till five,\n"
+ "- For I am busy then,\n"
+ "-As well as breakfast, lunch, and tea,\n"
+ "- For they are hungry men.\n"
+ "-But different folk have different views;\n"
+ "-I know a person small—\n"
+ "-She keeps ten million serving-men,\n"
+ "-Who get no rest at all!\n"
+ "-\n"
+ "-She sends'em abroad on her own affairs,\n"
+ "- From the second she opens her eyes—\n"
+ "-One million Hows, two million Wheres,\n"
+ "-And seven million Whys!\n"
+ "-\n"
+ "- -- Rudyard Kipling\n"
+ "-\n"
+ "diff --git a/songof7cities.txt b/sixserving.txt\n"
+ "similarity index 100%\n"
+ "rename from songof7cities.txt\n"
+ "rename to sixserving.txt\n";
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ find_opts.flags = GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, old_tree, new_tree, NULL));
+ cl_git_pass(git_diff_find_similar(diff, &find_opts));
+
+ cl_git_pass(git_diff_to_buf(&diff_buf, diff, GIT_DIFF_FORMAT_PATCH));
+
+ cl_assert_equal_s(expected, diff_buf.ptr);
+
+ git_buf_free(&diff_buf);
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
}
static bool found_master;
-static bool find_master_called;
+static bool found_haacked;
+static bool find_master_haacked_called;
-int find_master(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload)
+int find_master_haacked(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload)
{
GIT_UNUSED(remote_url);
GIT_UNUSED(oid);
GIT_UNUSED(payload);
- find_master_called = true;
+ find_master_haacked_called = true;
if (!strcmp("refs/heads/master", ref_name)) {
cl_assert(is_merge);
found_master = true;
}
+ if (!strcmp("refs/heads/haacked", ref_name)) {
+ cl_assert(is_merge);
+ found_haacked = true;
+ }
return 0;
}
{
git_remote *remote;
git_buf path = GIT_BUF_INIT;
- char *refspec = "refs/heads/master";
+ char *refspec1 = "refs/heads/master";
+ char *refspec2 = "refs/heads/haacked";
+ char *refspecs[] = { refspec1, refspec2 };
git_strarray specs = {
- &refspec,
- 1,
+ refspecs,
+ 2,
};
cl_set_cleanup(&cleanup_repository, "./test1");
cl_git_pass(git_remote_fetch(remote, &specs, NULL, NULL));
cl_assert(git_path_exists(path.ptr));
- cl_git_pass(git_repository_fetchhead_foreach(g_repo, find_master, NULL));
- cl_assert(find_master_called);
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, find_master_haacked, NULL));
+ cl_assert(find_master_haacked_called);
cl_assert(found_master);
+ cl_assert(found_haacked);
+
+ git_remote_free(remote);
+ git_buf_free(&path);
+}
+
+static bool count_refs_called;
+struct prefix_count {
+ const char *prefix;
+ int count;
+ int expected;
+};
+
+int count_refs(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ int i;
+ struct prefix_count *prefix_counts = (struct prefix_count *) payload;
+
+ GIT_UNUSED(remote_url);
+ GIT_UNUSED(oid);
+ GIT_UNUSED(is_merge);
+
+ count_refs_called = true;
+
+ for (i = 0; prefix_counts[i].prefix; i++) {
+ if (!git__prefixcmp(ref_name, prefix_counts[i].prefix))
+ prefix_counts[i].count++;
+ }
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__create_with_multiple_refspecs(void)
+{
+ git_remote *remote;
+ git_buf path = GIT_BUF_INIT;
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_pass(git_remote_create(&remote, g_repo, "origin", cl_fixture("testrepo.git")));
+ git_remote_free(remote);
+ cl_git_pass(git_remote_add_fetch(g_repo, "origin", "+refs/notes/*:refs/origin/notes/*"));
+ /* Pick up the new refspec */
+ cl_git_pass(git_remote_lookup(&remote, g_repo, "origin"));
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), "FETCH_HEAD"));
+ cl_assert(!git_path_exists(path.ptr));
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+ cl_assert(git_path_exists(path.ptr));
+
+ {
+ int i;
+ struct prefix_count prefix_counts[] = {
+ {"refs/notes/", 0, 1},
+ {"refs/heads/", 0, 12},
+ {"refs/tags/", 0, 7},
+ {NULL, 0, 0},
+ };
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, count_refs, &prefix_counts));
+ cl_assert(count_refs_called);
+ for (i = 0; prefix_counts[i].prefix; i++)
+ cl_assert_equal_i(prefix_counts[i].expected, prefix_counts[i].count);
+ }
git_remote_free(remote);
git_buf_free(&path);
from __future__ import with_statement
from string import Template
-import re, fnmatch, os, codecs, pickle
+import re, fnmatch, os, sys, codecs, pickle
class Module(object):
class Template(object):
class TestSuite(object):
- def __init__(self, path):
+ def __init__(self, path, output):
self.path = path
+ self.output = output
def should_generate(self, path):
if not os.path.isfile(path):
return modules
def load_cache(self):
- path = os.path.join(self.path, '.clarcache')
+ path = os.path.join(self.output, '.clarcache')
cache = {}
try:
return cache
def save_cache(self):
- path = os.path.join(self.path, '.clarcache')
+ path = os.path.join(self.output, '.clarcache')
with open(path, 'wb') as cache:
pickle.dump(self.modules, cache)
return sum(len(module.callbacks) for module in self.modules.values())
def write(self):
- output = os.path.join(self.path, 'clar.suite')
+ output = os.path.join(self.output, 'clar.suite')
if not self.should_generate(output):
return False
with open(output, 'w') as data:
- for module in self.modules.values():
+ modules = sorted(self.modules.values(), key=lambda module: module.name)
+
+ for module in modules:
t = Module.DeclarationTemplate(module)
data.write(t.render())
- for module in self.modules.values():
+ for module in modules:
t = Module.CallbacksTemplate(module)
data.write(t.render())
suites = "static struct clar_suite _clar_suites[] = {" + ','.join(
- Module.InfoTemplate(module).render() for module in sorted(self.modules.values(), key=lambda module: module.name)
+ Module.InfoTemplate(module).render() for module in modules
) + "\n};\n"
data.write(suites)
parser = OptionParser()
parser.add_option('-f', '--force', action="store_true", dest='force', default=False)
parser.add_option('-x', '--exclude', dest='excluded', action='append', default=[])
+ parser.add_option('-o', '--output', dest='output')
options, args = parser.parse_args()
-
- for path in args or ['.']:
- suite = TestSuite(path)
- suite.load(options.force)
- suite.disable(options.excluded)
- if suite.write():
- print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))
+ if len(args) > 1:
+ print("More than one path given")
+ sys.exit(1)
+
+ path = args.pop() if args else '.'
+ output = options.output or path
+ suite = TestSuite(path, output)
+ suite.load(options.force)
+ suite.disable(options.excluded)
+ if suite.write():
+ print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))
git_index_free(index);
}
+
+void test_index_filemodes__frombuffer_requires_files(void)
+{
+ git_index *index;
+ git_index_entry new_entry;
+ const git_index_entry *ret_entry;
+ const char *content = "hey there\n";
+
+ memset(&new_entry, 0, sizeof(new_entry));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* regular blob */
+ new_entry.path = "dummy-file.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_index_add_frombuffer(index,
+ &new_entry, content, strlen(content)));
+
+ cl_assert((ret_entry = git_index_get_bypath(index, "dummy-file.txt", 0)));
+ cl_assert_equal_s("dummy-file.txt", ret_entry->path);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, ret_entry->mode);
+
+ /* executable blob */
+ new_entry.path = "dummy-file.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_git_pass(git_index_add_frombuffer(index,
+ &new_entry, content, strlen(content)));
+
+ cl_assert((ret_entry = git_index_get_bypath(index, "dummy-file.txt", 0)));
+ cl_assert_equal_s("dummy-file.txt", ret_entry->path);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, ret_entry->mode);
+
+ /* links are also acceptable */
+ new_entry.path = "dummy-link.txt";
+ new_entry.mode = GIT_FILEMODE_LINK;
+
+ cl_git_pass(git_index_add_frombuffer(index,
+ &new_entry, content, strlen(content)));
+
+ cl_assert((ret_entry = git_index_get_bypath(index, "dummy-link.txt", 0)));
+ cl_assert_equal_s("dummy-link.txt", ret_entry->path);
+ cl_assert_equal_i(GIT_FILEMODE_LINK, ret_entry->mode);
+
+ /* trees are rejected */
+ new_entry.path = "invalid_mode.txt";
+ new_entry.mode = GIT_FILEMODE_TREE;
+
+ cl_git_fail(git_index_add_frombuffer(index,
+ &new_entry, content, strlen(content)));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));
+
+ /* submodules are rejected */
+ new_entry.path = "invalid_mode.txt";
+ new_entry.mode = GIT_FILEMODE_COMMIT;
+
+ cl_git_fail(git_index_add_frombuffer(index,
+ &new_entry, content, strlen(content)));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));
+
+ git_index_free(index);
+}
void test_index_tests__can_lock_index(void)
{
+ git_repository *repo;
git_index *index;
git_indexwriter one = GIT_INDEXWRITER_INIT,
two = GIT_INDEXWRITER_INIT;
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_indexwriter_init(&one, index));
cl_git_fail_with(GIT_ELOCKED, git_indexwriter_init(&two, index));
git_indexwriter_cleanup(&one);
git_indexwriter_cleanup(&two);
git_index_free(index);
+ cl_git_sandbox_cleanup();
}
cl_assert(entry->mode != GIT_FILEMODE_TREE);
}
- if (++count >= expected_flat)
- break;
+ cl_assert(++count <= expected_flat);
}
assert_at_end(i, v);
static const char *expect_base[] = {
"heads/br2",
"heads/dir",
+ "heads/executable",
"heads/ident",
"heads/long-file-name",
"heads/master",
cl_git_pass(git_iterator_for_filesystem(
&i, "testrepo/.git/refs", NULL));
- expect_iterator_items(i, 15, expect_base, 15, expect_base);
+ expect_iterator_items(i, 16, expect_base, 16, expect_base);
git_iterator_free(i);
}
/* should only have 13 items, since we're not asking for trees to be
* returned. the goal of this test is simply to not crash.
*/
- expect_iterator_items(i, 13, NULL, 13, NULL);
+ expect_iterator_items(i, 15, NULL, 15, NULL);
git_iterator_free(i);
git_buf_free(&parent);
}
cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i));
git_iterator_free(i);
+#else
+ cl_skip();
#endif
}
clar_test_init(argc, argv);
- git_libgit2_init();
+ res = git_libgit2_init();
+ if (res < 0) {
+ fprintf(stderr, "failed to init libgit2");
+ return res;
+ }
+
cl_global_trace_register();
cl_sandbox_set_search_path_defaults();
"This is a mighty fine recipe!\n" \
">>>>>>> branchF-2\n"
-#define CONFLICTING_RECURSIVE_H1_TO_H2_WITH_DIFF3 \
+#define CONFLICTING_RECURSIVE_H2_TO_H1_WITH_DIFF3 \
"VEAL SOUP.\n" \
"\n" \
"<<<<<<< HEAD\n" \
- "put into a pot three quarts of water, three onions cut small, one\n" \
- "||||||| merged common ancestors\n" \
- "<<<<<<< Temporary merge branch 1\n" \
- "Put into a pot three quarts of water, THREE ONIONS CUT SMALL, one\n" \
+ "Put Into A Pot Three Quarts of Water, Three Onions Cut Small, One\n" \
"||||||| merged common ancestors\n" \
- "Put into a pot three quarts of water, three onions cut small, one\n" \
- "=======\n" \
+ "<<<<<<<<< Temporary merge branch 1\n" \
"PUT INTO A POT three quarts of water, three onions cut small, one\n" \
- ">>>>>>> Temporary merge branch 2\n" \
+ "||||||||| merged common ancestors\n" \
+ "Put into a pot three quarts of water, three onions cut small, one\n" \
+ "=========\n" \
+ "Put into a pot three quarts of water, THREE ONIONS CUT SMALL, one\n" \
+ ">>>>>>>>> Temporary merge branch 2\n" \
"=======\n" \
- "Put Into A Pot Three Quarts of Water, Three Onions Cut Small, One\n" \
- ">>>>>>> branchH-2\n" \
+ "put into a pot three quarts of water, three onions cut small, one\n" \
+ ">>>>>>> branchH-1\n" \
"spoonful of black pepper pounded, and two of salt, with two or three\n" \
"slices of lean ham; let it boil steadily two hours; skim it\n" \
"occasionally, then put into it a shin of veal, let it boil two hours\n" \
git_merge_file_result_free(&result);
}
+
+void test_merge_files__crlf_conflict_markers_for_crlf_files(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+
+ const char *expected =
+ "<<<<<<< file.txt\r\nThis file\r\ndoes, too.\r\n"
+ "=======\r\nAnd so does\r\nthis one.\r\n>>>>>>> file.txt\r\n";
+ size_t expected_len = strlen(expected);
+
+ const char *expected_diff3 =
+ "<<<<<<< file.txt\r\nThis file\r\ndoes, too.\r\n"
+ "||||||| file.txt\r\nThis file has\r\nCRLF line endings.\r\n"
+ "=======\r\nAnd so does\r\nthis one.\r\n>>>>>>> file.txt\r\n";
+ size_t expected_diff3_len = strlen(expected_diff3);
+
+ ancestor.ptr = "This file has\r\nCRLF line endings.\r\n";
+ ancestor.size = 35;
+ ancestor.path = "file.txt";
+ ancestor.mode = 0100644;
+
+ ours.ptr = "This file\r\ndoes, too.\r\n";
+ ours.size = 23;
+ ours.path = "file.txt";
+ ours.mode = 0100644;
+
+ theirs.ptr = "And so does\r\nthis one.\r\n";
+ theirs.size = 24;
+ theirs.path = "file.txt";
+ theirs.mode = 0100644;
+
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+ cl_assert_equal_i(0, result.automergeable);
+ cl_assert_equal_i(expected_len, result.len);
+ cl_assert(memcmp(expected, result.ptr, expected_len) == 0);
+ git_merge_file_result_free(&result);
+
+ opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3;
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+ cl_assert_equal_i(0, result.automergeable);
+ cl_assert_equal_i(expected_diff3_len, result.len);
+ cl_assert(memcmp(expected_diff3, result.ptr, expected_len) == 0);
+ git_merge_file_result_free(&result);
+}
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
- { 0100644, "3a66812fed1e03ea4a6a7ee28d8a57aec1ca6537", 1, "veal.txt" },
+ { 0100644, "cfc01b0976122eae42a82064440bbf534eddd7a0", 1, "veal.txt" },
{ 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" },
{ 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" },
};
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
- { 0100644, "cd17a91513f3aee9e44114d1ede67932dd41d2fc", 1, "veal.txt" },
- { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" },
- { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" },
+ { 0100644, "0b01d2f70a1c6b9ab60c382f3f9cdc8173da6736", 1, "veal.txt" },
+ { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 2, "veal.txt" },
+ { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 3, "veal.txt" },
};
opts.file_flags |= GIT_MERGE_FILE_STYLE_DIFF3;
- cl_git_pass(merge_commits_from_branches(&index, repo, "branchH-1", "branchH-2", &opts));
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchH-2", "branchH-1", &opts));
cl_assert(merge_test_index(index, merge_index_entries, 8));
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
- { 0100644, "ce7e553c6feb6e5f3bd67e3c3be04182fe3094b4", 1, "gravy.txt" },
- { 0100644, "d8dd349b78f19a4ebe3357bacb8138f00bf5ed41", 2, "gravy.txt" },
- { 0100644, "e50fbbd701458757bdfe9815f58ed717c588d1b5", 3, "gravy.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
- { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ { 0100644, "53217e8ac3f52bccf7603b8fff0ed0f4817f9bb7", 1, "veal.txt" },
+ { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 2, "veal.txt" },
+ { 0100644, "68a2e1ee61a23a4728fe6b35580fbbbf729df370", 3, "veal.txt" },
};
opts.recursion_limit = 1;
- cl_git_pass(merge_commits_from_branches(&index, repo, "branchE-1", "branchE-2", &opts));
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchC-1", "branchC-2", &opts));
cl_assert(merge_test_index(index, merge_index_entries, 8));
git_index_free(index);
}
+/* There are multiple levels of criss-cross merges. This ensures
+ * that the virtual merge base parents are compared in the same
+ * order as git. If the base parents are created in the order as
+ * git does, then the file `targetfile.txt` is automerged. If not,
+ * `targetfile.txt` will be in conflict due to the virtual merge
+ * base.
+ */
+void test_merge_trees_recursive__merge_base_for_virtual_commit(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "1bde1883de4977ea3e664b315da951d1f614c3b1", 0, "targetfile.txt" },
+ { 0100644, "b7de2b52ba055688061355fad1599a5d214ce8f8", 1, "version.txt" },
+ { 0100644, "358efd6f589384fa8baf92234db9c7899a53916e", 2, "version.txt" },
+ { 0100644, "a664873b1c0b9a1ed300f8644dde536fdaa3a34f", 3, "version.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchJ-1", "branchJ-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+
+ git_index_free(index);
+}
+
+/* This test is the same as above, but the graph is constructed such
+ * that the 1st-recursion merge bases of the two heads are
+ * in a different order.
+ */
+void test_merge_trees_recursive__merge_base_for_virtual_commit_2(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "4a06b258fed8a4d15967ec4253ae7366b70f727d", 0, "targetfile.txt" },
+ { 0100644, "b6bd0f9952f396e757d3f91e08c59a7e91707201", 1, "version.txt" },
+ { 0100644, "f0856993e005c0d8ed2dc7cdc222cc1d89fb3c77", 2, "version.txt" },
+ { 0100644, "2cba583804a4a6fad1baf97c959be447238d1489", 3, "version.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchK-1", "branchK-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+
+ git_index_free(index);
+}
cl_git_pass(git_futils_readbuffer(&conflicting_buf, "merge-recursive/veal.txt"));
cl_assert_equal_s(CONFLICTING_RECURSIVE_F1_TO_F2, conflicting_buf.ptr);
-
+
git_index_free(index);
git_buf_free(&conflicting_buf);
}
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
- { 0100644, "cd17a91513f3aee9e44114d1ede67932dd41d2fc", 1, "veal.txt" },
- { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" },
- { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" },
+ { 0100644, "0b01d2f70a1c6b9ab60c382f3f9cdc8173da6736", 1, "veal.txt" },
+ { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 2, "veal.txt" },
+ { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 3, "veal.txt" },
};
opts.file_flags |= GIT_MERGE_FILE_STYLE_DIFF3;
checkout_opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
- cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchH-1", GIT_REFS_HEADS_DIR "branchH-2", &opts, &checkout_opts));
+ cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchH-2", GIT_REFS_HEADS_DIR "branchH-1", &opts, &checkout_opts));
cl_git_pass(git_repository_index(&index, repo));
cl_assert(merge_test_index(index, merge_index_entries, 8));
cl_git_pass(git_futils_readbuffer(&conflicting_buf, "merge-recursive/veal.txt"));
- cl_assert_equal_s(CONFLICTING_RECURSIVE_H1_TO_H2_WITH_DIFF3, conflicting_buf.ptr);
+ cl_assert_equal_s(CONFLICTING_RECURSIVE_H2_TO_H1_WITH_DIFF3, conflicting_buf.ptr);
git_index_free(index);
git_buf_free(&conflicting_buf);
#define SUBMODULE_MAIN_BRANCH "submodules"
#define SUBMODULE_OTHER_BRANCH "submodules-branch"
#define SUBMODULE_OTHER2_BRANCH "submodules-branch2"
+#define SUBMODULE_DELETE_BRANCH "delete-submodule"
#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
git_reference_free(their_ref);
git_reference_free(our_ref);
}
+
+
+void test_merge_workdir_submodules__update_delete_conflict(void)
+{
+ git_reference *our_ref, *their_ref;
+ git_commit *our_commit;
+ git_annotated_commit *their_head;
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 0, ".gitmodules" },
+ { 0100644, "5887a5e516c53bd58efb0f02ec6aa031b6fe9ad7", 0, "file1.txt" },
+ { 0100644, "4218670ab81cc219a9f94befb5c5dad90ec52648", 0, "file2.txt" },
+ { 0160000, "d3d806a4bef96889117fd7ebac0e3cb5ec152932", 1, "submodule"},
+ { 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 3, "submodule" },
+ };
+
+ cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_DELETE_BRANCH));
+ cl_git_pass(git_commit_lookup(&our_commit, repo, git_reference_target(our_ref)));
+ cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_MAIN_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head, 1, NULL, NULL));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(merge_test_index(index, merge_index_entries, 5));
+
+ git_index_free(index);
+ git_annotated_commit_free(their_head);
+ git_commit_free(our_commit);
+ git_reference_free(their_ref);
+ git_reference_free(our_ref);
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "message.h"
+
+static void assert_trailers(const char *message, git_message_trailer *trailers)
+{
+ git_message_trailer_array arr;
+ size_t i;
+
+ int rc = git_message_trailers(&arr, message);
+
+ cl_assert_equal_i(0, rc);
+
+ for(i=0; i<arr.count; i++) {
+ cl_assert_equal_s(arr.trailers[i].key, trailers[i].key);
+ cl_assert_equal_s(arr.trailers[i].value, trailers[i].value);
+ }
+
+ cl_assert_equal_i(0, rc);
+
+ git_message_trailer_array_free(&arr);
+}
+
+void test_message_trailer__simple(void)
+{
+ git_message_trailer trailers[] = {
+ {"Signed-off-by", "foo@bar.com"},
+ {"Signed-off-by", "someone@else.com"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Signed-off-by: foo@bar.com\n"
+ "Signed-off-by: someone@else.com\n"
+ , trailers);
+}
+
+void test_message_trailer__no_whitespace(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key:value\n"
+ , trailers);
+}
+
+void test_message_trailer__extra_whitespace(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key : value\n"
+ , trailers);
+}
+
+void test_message_trailer__no_newline(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key: value"
+ , trailers);
+}
+
+void test_message_trailer__not_last_paragraph(void)
+{
+ git_message_trailer trailers[] = {
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "More stuff\n"
+ , trailers);
+}
+
+void test_message_trailer__conflicts(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "Conflicts:\n"
+ "\tfoo.c\n"
+ , trailers);
+}
+
+void test_message_trailer__patch(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "---\n"
+ "More: stuff\n"
+ , trailers);
+}
+
+void test_message_trailer__continuation(void)
+{
+ git_message_trailer trailers[] = {
+ {"A", "b\n c"},
+ {"D", "e\n f: g h"},
+ {"I", "j"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "A: b\n"
+ " c\n"
+ "D: e\n"
+ " f: g h\n"
+ "I: j\n"
+ , trailers);
+}
+
+void test_message_trailer__invalid(void)
+{
+ git_message_trailer trailers[] = {
+ {"Signed-off-by", "some@one.com"},
+ {"Another", "trailer"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Signed-off-by: some@one.com\n"
+ "Not a trailer\n"
+ "Another: trailer\n"
+ , trailers);
+}
git_repository_free(repo);
}
-static void cleanup_sandbox(void *unused)
-{
- GIT_UNUSED(unused);
- cl_git_sandbox_cleanup();
-}
-
void test_network_fetchlocal__partial(void)
{
git_repository *repo = cl_git_sandbox_init("partial-testrepo");
options.callbacks.transfer_progress = transfer_cb;
options.callbacks.payload = &callcount;
- cl_set_cleanup(&cleanup_sandbox, NULL);
cl_git_pass(git_reference_list(&refnames, repo));
cl_assert_equal_i(1, (int)refnames.count);
git_strarray refnames = {0};
git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
- cl_set_cleanup(&cleanup_sandbox, NULL);
options.callbacks.transfer_progress = transfer_cb;
cl_git_pass(git_remote_set_url(repo, "test", cl_git_fixture_url("testrepo.git")));
cl_git_pass(git_remote_lookup(&test, repo, "test"));
return 0;
}
+struct note_create_payload {
+ const char *note_oid;
+ const char *object_oid;
+ unsigned seen;
+};
+
+static int note_list_create_cb(
+ const git_oid *blob_oid, const git_oid *annotated_obj_id, void *payload)
+{
+ git_oid expected_note_oid, expected_target_oid;
+ struct note_create_payload *notes = payload;
+ size_t i;
+
+ for (i = 0; notes[i].note_oid != NULL; i++) {
+ cl_git_pass(git_oid_fromstr(&expected_note_oid, notes[i].note_oid));
+
+ if (git_oid_cmp(&expected_note_oid, blob_oid) != 0)
+ continue;
+
+ cl_git_pass(git_oid_fromstr(&expected_target_oid, notes[i].object_oid));
+
+ if (git_oid_cmp(&expected_target_oid, annotated_obj_id) != 0)
+ continue;
+
+ notes[i].seen = 1;
+ return 0;
+ }
+
+ cl_fail("Did not see expected note");
+ return 0;
+}
+
+void assert_notes_seen(struct note_create_payload payload[], size_t n)
+{
+ size_t seen = 0, i;
+
+ for (i = 0; payload[i].note_oid != NULL; i++) {
+ if (payload[i].seen)
+ seen++;
+ }
+
+ cl_assert_equal_i(seen, n);
+}
+
+void test_notes_notes__can_create_a_note(void)
+{
+ git_oid note_oid;
+ static struct note_create_payload can_create_a_note[] = {
+ { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 },
+ { NULL, NULL, 0 }
+ };
+
+ create_note(¬e_oid, "refs/notes/i-can-see-dead-notes", can_create_a_note[0].object_oid, "I decorate 4a20\n");
+
+ cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note));
+
+ assert_notes_seen(can_create_a_note, 1);
+}
+
+void test_notes_notes__can_create_a_note_from_commit(void)
+{
+ git_oid oid;
+ git_oid notes_commit_out;
+ git_reference *ref;
+ static struct note_create_payload can_create_a_note_from_commit[] = {
+ { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 },
+ { NULL, NULL, 0 }
+ };
+
+ cl_git_pass(git_oid_fromstr(&oid, can_create_a_note_from_commit[0].object_oid));
+
+ cl_git_pass(git_note_commit_create(¬es_commit_out, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1));
+
+ /* create_from_commit will not update any ref,
+ * so we must manually create the ref, that points to the commit */
+ cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", ¬es_commit_out, 0, NULL));
+
+ cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note_from_commit));
+
+ assert_notes_seen(can_create_a_note_from_commit, 1);
+
+ git_reference_free(ref);
+}
+
+
+/* Test that we can create a note from a commit, given an existing commit */
+void test_notes_notes__can_create_a_note_from_commit_given_an_existing_commit(void)
+{
+ git_oid oid;
+ git_oid notes_commit_out;
+ git_commit *existing_notes_commit = NULL;
+ git_reference *ref;
+ static struct note_create_payload can_create_a_note_from_commit_given_an_existing_commit[] = {
+ { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 },
+ { "1aaf94147c21f981e0a20bf57b89137c5a6aae52", "9fd738e8f7967c078dceed8190330fc8648ee56a", 0 },
+ { NULL, NULL, 0 }
+ };
+
+ cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+
+ cl_git_pass(git_note_commit_create(¬es_commit_out, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0));
+
+ cl_git_pass(git_oid_fromstr(&oid, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+
+ git_commit_lookup(&existing_notes_commit, _repo, ¬es_commit_out);
+
+ cl_assert(existing_notes_commit);
+
+ cl_git_pass(git_note_commit_create(¬es_commit_out, NULL, _repo, existing_notes_commit, _sig, _sig, &oid, "I decorate 9fd7\n", 0));
+
+ /* create_from_commit will not update any ref,
+ * so we must manually create the ref, that points to the commit */
+ cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", ¬es_commit_out, 0, NULL));
+
+ cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note_from_commit_given_an_existing_commit));
+
+ assert_notes_seen(can_create_a_note_from_commit_given_an_existing_commit, 2);
+
+ git_commit_free(existing_notes_commit);
+ git_reference_free(ref);
+}
+
/*
* $ git notes --ref i-can-see-dead-notes add -m "I decorate a65f" a65fedf39aefe402d3bb6e24df4d4f5fe4547750
* $ git notes --ref i-can-see-dead-notes add -m "I decorate c478" c47800c7266a2be04c571c04d5a6614691ea99bd
#define MESSAGES_COUNT (sizeof(messages)/sizeof(messages[0])) - 1
+/* Test that we can read a note */
+void test_notes_notes__can_read_a_note(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note;
+
+ create_note(¬e_oid, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 4a20\n");
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+
+ cl_git_pass(git_note_read(¬e, _repo, "refs/notes/i-can-see-dead-notes", &target_oid));
+
+ cl_assert_equal_s(git_note_message(note), "I decorate 4a20\n");
+
+ git_note_free(note);
+}
+
+/* Test that we can read a note with from commit api */
+void test_notes_notes__can_read_a_note_from_a_commit(void)
+{
+ git_oid oid, notes_commit_oid;
+ git_commit *notes_commit;
+ git_note *note;
+
+ cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+
+ cl_git_pass(git_note_commit_create(¬es_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1));
+
+ git_commit_lookup(¬es_commit, _repo, ¬es_commit_oid);
+
+ cl_assert(notes_commit);
+
+ cl_git_pass(git_note_commit_read(¬e, _repo, notes_commit, &oid));
+
+ cl_assert_equal_s(git_note_message(note), "I decorate 4a20\n");
+
+ git_commit_free(notes_commit);
+ git_note_free(note);
+}
+
+/* Test that we can read a commit with no note fails */
+void test_notes_notes__attempt_to_read_a_note_from_a_commit_with_no_note_fails(void)
+{
+ git_oid oid, notes_commit_oid;
+ git_commit *notes_commit;
+ git_note *note;
+
+ cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+
+ cl_git_pass(git_note_commit_create(¬es_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1));
+
+ git_commit_lookup(¬es_commit, _repo, ¬es_commit_oid);
+
+ cl_git_pass(git_note_commit_remove(¬es_commit_oid, _repo, notes_commit, _sig, _sig, &oid));
+ git_commit_free(notes_commit);
+
+ git_commit_lookup(¬es_commit, _repo, ¬es_commit_oid);
+
+ cl_assert(notes_commit);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_note_commit_read(¬e, _repo, notes_commit, &oid));
+
+ git_commit_free(notes_commit);
+}
+
/*
* $ git ls-tree refs/notes/fanout
* 040000 tree 4b22b35d44b5a4f589edf3dc89196399771796ea 84
git_note_free(note);
}
+/* Can remove a note */
+void test_notes_notes__can_remove_a_note(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note;
+
+ create_note(¬e_oid, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 4a20\n");
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+ cl_git_pass(git_note_remove(_repo, "refs/notes/i-can-see-dead-notes", _sig, _sig, &target_oid));
+
+ cl_git_fail(git_note_read(¬e, _repo, "refs/notes/i-can-see-dead-notes", &target_oid));
+}
+
+/* Can remove a note from a commit */
+void test_notes_notes__can_remove_a_note_from_commit(void)
+{
+ git_oid oid, notes_commit_oid;
+ git_note *note = NULL;
+ git_commit *existing_notes_commit;
+ git_reference *ref;
+
+ cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+
+ cl_git_pass(git_note_commit_create(¬es_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0));
+
+ git_commit_lookup(&existing_notes_commit, _repo, ¬es_commit_oid);
+
+ cl_assert(existing_notes_commit);
+
+ cl_git_pass(git_note_commit_remove(¬es_commit_oid, _repo, existing_notes_commit, _sig, _sig, &oid));
+
+ /* remove_from_commit will not update any ref,
+ * so we must manually create the ref, that points to the commit */
+ cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", ¬es_commit_oid, 0, NULL));
+
+ cl_git_fail(git_note_read(¬e, _repo, "refs/notes/i-can-see-dead-notes", &oid));
+
+ git_commit_free(existing_notes_commit);
+ git_reference_free(ref);
+ git_note_free(note);
+}
+
+
void test_notes_notes__can_remove_a_note_in_an_existing_fanout(void)
{
git_oid target_oid;
cl_git_fail(git_note_iterator_new(&iter, _repo, "refs/notes/commits"));
}
+
+void test_notes_notes__iterate_from_commit(void)
+{
+ git_note_iterator *iter;
+ git_note *note;
+ git_oid note_id, annotated_id;
+ git_oid oids[2];
+ git_oid notes_commit_oids[2];
+ git_commit *notes_commits[2];
+ const char* note_message[] = {
+ "I decorate a65f\n",
+ "I decorate c478\n"
+ };
+ int i, err;
+
+ cl_git_pass(git_oid_fromstr(&(oids[0]), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
+ cl_git_pass(git_oid_fromstr(&(oids[1]), "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+
+ cl_git_pass(git_note_commit_create(¬es_commit_oids[0], NULL, _repo, NULL, _sig, _sig, &(oids[0]), note_message[0], 0));
+
+ git_commit_lookup(¬es_commits[0], _repo, ¬es_commit_oids[0]);
+ cl_assert(notes_commits[0]);
+
+ cl_git_pass(git_note_commit_create(¬es_commit_oids[1], NULL, _repo, notes_commits[0], _sig, _sig, &(oids[1]), note_message[1], 0));
+
+ git_commit_lookup(¬es_commits[1], _repo, ¬es_commit_oids[1]);
+ cl_assert(notes_commits[1]);
+
+ cl_git_pass(git_note_commit_iterator_new(&iter, notes_commits[1]));
+
+ for (i = 0; (err = git_note_next(¬e_id, &annotated_id, iter)) >= 0; ++i) {
+ cl_git_pass(git_note_commit_read(¬e, _repo, notes_commits[1], &annotated_id));
+ cl_assert_equal_s(git_note_message(note), note_message[i]);
+ git_note_free(note);
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, err);
+ cl_assert_equal_i(2, i);
+
+ git_note_iterator_free(iter);
+ git_commit_free(notes_commits[0]);
+ git_commit_free(notes_commits[1]);
+}
git_treebuilder *bld;
git_oid sm_id;
+ cl_git_pass(git_oid_fromstr(&sm_id, "da39a3ee5e6b4b0d3255bfef95601890afd80709"));
cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL));
cl_git_pass(git_treebuilder_insert(NULL, bld, "sm", &sm_id, GIT_FILEMODE_COMMIT));
git_treebuilder_free(bld);
test_inserting_submodule();
}
+void test_object_tree_write__invalid_null_oid(void)
+{
+ git_treebuilder *bld;
+ git_oid null_oid = {{0}};
+
+ cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL));
+ cl_git_fail(git_treebuilder_insert(NULL, bld, "null_oid_file", &null_oid, GIT_FILEMODE_BLOB));
+ cl_assert(giterr_last() && strstr(giterr_last()->message, "null OID") != NULL);
+
+ git_treebuilder_free(bld);
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "backend_helpers.h"
+#include "git2/sys/mempack.h"
+
+static git_odb *_odb;
+static git_oid _oid;
+static git_odb_object *_obj;
+static git_repository *_repo;
+
+void test_odb_backend_mempack__initialize(void)
+{
+ git_odb_backend *backend;
+
+ cl_git_pass(git_mempack_new(&backend));
+ cl_git_pass(git_odb_new(&_odb));
+ cl_git_pass(git_odb_add_backend(_odb, backend, 10));
+ cl_git_pass(git_repository_wrap_odb(&_repo, _odb));
+}
+
+void test_odb_backend_mempack__cleanup(void)
+{
+ git_odb_object_free(_obj);
+ git_odb_free(_odb);
+ git_repository_free(_repo);
+}
+
+void test_odb_backend_mempack__write_succeeds(void)
+{
+ const char *data = "data";
+ cl_git_pass(git_odb_write(&_oid, _odb, data, strlen(data) + 1, GIT_OBJ_BLOB));
+ cl_git_pass(git_odb_read(&_obj, _odb, &_oid));
+}
+
+void test_odb_backend_mempack__read_of_missing_object_fails(void)
+{
+ cl_git_pass(git_oid_fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4633"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&_obj, _odb, &_oid));
+}
+
+void test_odb_backend_mempack__exists_of_missing_object_fails(void)
+{
+ cl_git_pass(git_oid_fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4633"));
+ cl_assert(git_odb_exists(_odb, &_oid) == 0);
+}
+
+void test_odb_backend_mempack__exists_with_existing_objects_succeeds(void)
+{
+ const char *data = "data";
+ cl_git_pass(git_odb_write(&_oid, _odb, data, strlen(data) + 1, GIT_OBJ_BLOB));
+ cl_assert(git_odb_exists(_odb, &_oid) == 1);
+}
+
+void test_odb_backend_mempack__blob_create_frombuffer_succeeds(void)
+{
+ const char *data = "data";
+
+ cl_git_pass(git_blob_create_frombuffer(&_oid, _repo, data, strlen(data) + 1));
+ cl_assert(git_odb_exists(_odb, &_oid) == 1);
+}
cl_git_pass(git_odb_exists_prefix(&found, _odb, &_oid, 40));
cl_assert(git_oid_equal(&found, &_oid));
}
+
+void test_odb_backend_simple__null_oid_is_ignored(void)
+{
+ const fake_object objs[] = {
+ { "0000000000000000000000000000000000000000", "null oid content" },
+ { NULL, NULL }
+ };
+ git_oid null_oid = {{0}};
+ git_odb_object *obj;
+
+ setup_backend(objs);
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_assert(!git_odb_exists(_odb, &null_oid));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&obj, _odb, &null_oid));
+ cl_assert(giterr_last() && strstr(giterr_last()->message, "null OID"));
+}
--- /dev/null
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+#include "hash.h"
+#include "odb.h"
+
+#define LARGEFILE_SIZE 5368709122
+
+static git_repository *repo;
+static git_odb *odb;
+
+void test_odb_largefiles__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_odb(&odb, repo));
+}
+
+void test_odb_largefiles__cleanup(void)
+{
+ git_odb_free(odb);
+ cl_git_sandbox_cleanup();
+}
+
+static void writefile(git_oid *oid)
+{
+ static git_odb_stream *stream;
+ git_buf buf = GIT_BUF_INIT;
+ size_t i;
+
+ for (i = 0; i < 3041; i++)
+ cl_git_pass(git_buf_puts(&buf, "Hello, world.\n"));
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, LARGEFILE_SIZE, GIT_OBJ_BLOB));
+ for (i = 0; i < 126103; i++)
+ cl_git_pass(git_odb_stream_write(stream, buf.ptr, buf.size));
+
+ cl_git_pass(git_odb_stream_finalize_write(oid, stream));
+
+ git_odb_stream_free(stream);
+ git_buf_free(&buf);
+}
+
+void test_odb_largefiles__write_from_memory(void)
+{
+ git_oid expected, oid;
+ git_buf buf = GIT_BUF_INIT;
+ size_t i;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_INVASIVE_MEMORY") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ for (i = 0; i < (3041*126103); i++)
+ cl_git_pass(git_buf_puts(&buf, "Hello, world.\n"));
+
+ git_oid_fromstr(&expected, "3fb56989cca483b21ba7cb0a6edb229d10e1c26c");
+ cl_git_pass(git_odb_write(&oid, odb, buf.ptr, buf.size, GIT_OBJ_BLOB));
+
+ cl_assert_equal_oid(&expected, &oid);
+}
+
+void test_odb_largefiles__streamwrite(void)
+{
+ git_oid expected, oid;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ git_oid_fromstr(&expected, "3fb56989cca483b21ba7cb0a6edb229d10e1c26c");
+ writefile(&oid);
+
+ cl_assert_equal_oid(&expected, &oid);
+}
+
+void test_odb_largefiles__streamread(void)
+{
+ git_oid oid, read_oid;
+ git_odb_stream *stream;
+ char buf[10240];
+ char hdr[64];
+ size_t len, hdr_len, total = 0;
+ git_hash_ctx hash;
+ git_otype type;
+ int ret;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ writefile(&oid);
+
+ cl_git_pass(git_odb_open_rstream(&stream, &len, &type, odb, &oid));
+
+ cl_assert_equal_sz(LARGEFILE_SIZE, len);
+ cl_assert_equal_i(GIT_OBJ_BLOB, type);
+
+ cl_git_pass(git_hash_ctx_init(&hash));
+ cl_git_pass(git_odb__format_object_header(&hdr_len, hdr, sizeof(hdr), len, type));
+
+ cl_git_pass(git_hash_update(&hash, hdr, hdr_len));
+
+ while ((ret = git_odb_stream_read(stream, buf, 10240)) > 0) {
+ cl_git_pass(git_hash_update(&hash, buf, ret));
+ total += ret;
+ }
+
+ cl_assert_equal_sz(LARGEFILE_SIZE, total);
+
+ git_hash_final(&read_oid, &hash);
+
+ cl_assert_equal_oid(&oid, &read_oid);
+
+ git_hash_ctx_cleanup(&hash);
+ git_odb_stream_free(stream);
+}
+
+void test_odb_largefiles__read_into_memory(void)
+{
+ git_oid oid;
+ git_odb_object *obj;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_INVASIVE_MEMORY") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ writefile(&oid);
+ cl_git_pass(git_odb_read(&obj, odb, &oid));
+
+ git_odb_object_free(obj);
+}
+
+void test_odb_largefiles__read_into_memory_rejected_on_32bit(void)
+{
+ git_oid oid;
+ git_odb_object *obj = NULL;
+
+#ifdef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_INVASIVE_MEMORY") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ writefile(&oid);
+ cl_git_fail(git_odb_read(&obj, odb, &oid));
+
+ git_odb_object_free(obj);
+}
+
+void test_odb_largefiles__read_header(void)
+{
+ git_oid oid;
+ size_t len;
+ git_otype type;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ writefile(&oid);
+ cl_git_pass(git_odb_read_header(&len, &type, odb, &oid));
+
+ cl_assert_equal_sz(LARGEFILE_SIZE, len);
+ cl_assert_equal_i(GIT_OBJ_BLOB, type);
+}
git_odb_free(odb);
}
+static void test_read_header(object_data *data)
+{
+ git_oid id;
+ git_odb *odb;
+ size_t len;
+ git_otype type;
+
+ write_object_files(data);
+
+ cl_git_pass(git_odb_open(&odb, "test-objects"));
+ cl_git_pass(git_oid_fromstr(&id, data->id));
+ cl_git_pass(git_odb_read_header(&len, &type, odb, &id));
+
+ cl_assert_equal_sz(data->dlen, len);
+ cl_assert_equal_i(git_object_string2type(data->type), type);
+
+ git_odb_free(odb);
+}
+
+static void test_readstream_object(object_data *data, size_t blocksize)
+{
+ git_oid id;
+ git_odb *odb;
+ git_odb_stream *stream;
+ git_rawobj tmp;
+ char buf[2048], *ptr = buf;
+ size_t remain;
+ int ret;
+
+ write_object_files(data);
+
+ cl_git_pass(git_odb_open(&odb, "test-objects"));
+ cl_git_pass(git_oid_fromstr(&id, data->id));
+ cl_git_pass(git_odb_open_rstream(&stream, &tmp.len, &tmp.type, odb, &id));
+
+ remain = tmp.len;
+
+ while (remain) {
+ cl_assert((ret = git_odb_stream_read(stream, ptr, blocksize)) >= 0);
+ if (ret == 0)
+ break;
+
+ cl_assert(remain >= (size_t)ret);
+ remain -= ret;
+ ptr += ret;
+ }
+
+ cl_assert(remain == 0);
+
+ tmp.data = buf;
+
+ cmp_objects(&tmp, data);
+
+ git_odb_stream_free(stream);
+ git_odb_free(odb);
+}
+
void test_odb_loose__initialize(void)
{
p_fsync__cnt = 0;
test_read_object(&some);
}
+void test_odb_loose__streaming_reads(void)
+{
+ size_t blocksizes[] = { 1, 2, 4, 16, 99, 1024, 123456789 };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(blocksizes); i++) {
+ test_readstream_object(&commit, blocksizes[i]);
+ test_readstream_object(&tree, blocksizes[i]);
+ test_readstream_object(&tag, blocksizes[i]);
+ test_readstream_object(&zero, blocksizes[i]);
+ test_readstream_object(&one, blocksizes[i]);
+ test_readstream_object(&two, blocksizes[i]);
+ test_readstream_object(&some, blocksizes[i]);
+ }
+}
+
+void test_odb_loose__read_header(void)
+{
+ test_read_header(&commit);
+ test_read_header(&tree);
+ test_read_header(&tag);
+ test_read_header(&zero);
+ test_read_header(&one);
+ test_read_header(&two);
+ test_read_header(&some);
+}
+
void test_write_object_permission(
mode_t dir_mode, mode_t file_mode,
mode_t expected_dir_mode, mode_t expected_file_mode)
void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
{
- if (!_remote_url || !_remote_user)
- clar__skip();
+ _remote_url = git__strdup("https://github.com/libgit2/non-existent");
+ _remote_user = git__strdup("libgit2test");
g_options.fetch_opts.callbacks.credentials = cred_failure_cb;
{
size_t counter = 0;
- if (!_remote_url || !_remote_user)
- clar__skip();
+ _remote_url = git__strdup("https://github.com/libgit2/non-existent");
+ _remote_user = git__strdup("libgit2test");
g_options.fetch_opts.callbacks.credentials = cred_count_calls_cb;
g_options.fetch_opts.callbacks.payload = &counter;
void test_online_clone__bitbucket_style(void)
{
git_cred_userpass_payload user_pass = {
- "libgit2", "libgit2"
+ "libgit3", "libgit3"
};
g_options.fetch_opts.callbacks.credentials = git_cred_userpass;
cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL;
cl_fixture_cleanup("./foo");
+}
+
+void test_online_clone__bitbucket_uses_creds_in_url(void)
+{
+ git_cred_userpass_payload user_pass = {
+ "libgit2", "wrong"
+ };
+
+ g_options.fetch_opts.callbacks.credentials = git_cred_userpass;
+ g_options.fetch_opts.callbacks.payload = &user_pass;
- /* User and pass from URL */
- user_pass.password = "wrong";
+ /*
+ * Correct user and pass are in the URL; the (incorrect) creds in
+ * the `git_cred_userpass_payload` should be ignored.
+ */
cl_git_pass(git_clone(&g_repo, BB_REPO_URL_WITH_PASS, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL;
cl_fixture_cleanup("./foo");
+}
- /* Wrong password in URL, fall back to user_pass */
- user_pass.password = "libgit2";
+void test_online_clone__bitbucket_falls_back_to_specified_creds(void)
+{
+ git_cred_userpass_payload user_pass = {
+ "libgit2", "libgit2"
+ };
+
+ g_options.fetch_opts.callbacks.credentials = git_cred_userpass;
+ g_options.fetch_opts.callbacks.payload = &user_pass;
+
+ /*
+ * TODO: as of March 2018, bitbucket sporadically fails with
+ * 403s instead of replying with a 401 - but only sometimes.
+ */
+ cl_skip();
+
+ /*
+ * Incorrect user and pass are in the URL; the (correct) creds in
+ * the `git_cred_userpass_payload` should be used as a fallback.
+ */
cl_git_pass(git_clone(&g_repo, BB_REPO_URL_WITH_WRONG_PASS, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL;
cl_fixture_cleanup("./foo");
if (!_remote_ssh_fingerprint)
cl_skip();
- cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, "ssh://localhost/foo", "./foo", &g_options));
+ cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, _remote_url, "./foo", &g_options));
}
static char *read_key_file(const char *path)
void test_online_clone__proxy_credentials_request(void)
{
+ git_buf url = GIT_BUF_INIT;
+
if (!_remote_proxy_url || !_remote_proxy_user || !_remote_proxy_pass)
cl_skip();
+ cl_git_pass(git_buf_printf(&url, "http://%s/", _remote_proxy_url));
+
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
- g_options.fetch_opts.proxy_opts.url = _remote_proxy_url;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
g_options.fetch_opts.proxy_opts.credentials = proxy_creds;
called_proxy_creds = 0;
cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
cl_assert(called_proxy_creds);
+
+ git_buf_free(&url);
}
void test_online_clone__proxy_credentials_in_url(void)
{
- if (!_remote_proxy_url)
+ git_buf url = GIT_BUF_INIT;
+
+ if (!_remote_proxy_url || !_remote_proxy_user || !_remote_proxy_pass)
cl_skip();
+ cl_git_pass(git_buf_printf(&url, "http://%s:%s@%s/", _remote_proxy_user, _remote_proxy_pass, _remote_proxy_url));
+
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
- g_options.fetch_opts.proxy_opts.url = _remote_proxy_url;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
called_proxy_creds = 0;
cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
cl_assert(called_proxy_creds == 0);
+
+ git_buf_free(&url);
}
#include "clar_libgit2.h"
-static const char *refspec = "refs/heads/first-merge:refs/remotes/origin/first-merge";
+#define URL "git://github.com/libgit2/TestGitRepository"
+#define REFSPEC "refs/heads/first-merge:refs/remotes/origin/first-merge"
static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
{
GIT_UNUSED(payload);
- cl_git_pass(git_remote_create_with_fetchspec(out, repo, name, url, refspec));
+ cl_git_pass(git_remote_create_with_fetchspec(out, repo, name, url, REFSPEC));
return 0;
}
opts.remote_cb = remote_single_branch;
opts.checkout_branch = "first-merge";
- cl_git_pass(git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./single-branch", &opts));
+ cl_git_pass(git_clone(&repo, URL, "./single-branch", &opts));
cl_git_pass(git_reference_list(&refs, repo));
for (i = 0; i < refs.count; i++) {
cl_git_pass(git_remote_get_fetch_refspecs(&refs, remote));
cl_assert_equal_i(1, refs.count);
- cl_assert_equal_s(refspec, refs.strings[0]);
+ cl_assert_equal_s(REFSPEC, refs.strings[0]);
git_strarray_free(&refs);
git_remote_free(remote);
opts.remote_cb = remote_single_branch;
- cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./restrict-refspec", &opts));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, URL, "./restrict-refspec", &opts));
+}
+
+void test_online_remotes__detached_remote_fails_downloading(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_detached(&remote, URL));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_fail(git_remote_download(remote, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_online_remotes__detached_remote_fails_uploading(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_detached(&remote, URL));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_fail(git_remote_upload(remote, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_online_remotes__detached_remote_fails_pushing(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_detached(&remote, URL));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_fail(git_remote_push(remote, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_online_remotes__detached_remote_succeeds_ls(void)
+{
+ const char *refs[] = {
+ "HEAD",
+ "refs/heads/first-merge",
+ "refs/heads/master",
+ "refs/heads/no-parent",
+ "refs/tags/annotated_tag",
+ "refs/tags/annotated_tag^{}",
+ "refs/tags/blob",
+ "refs/tags/commit_tree",
+ "refs/tags/nearly-dangling",
+ };
+ const git_remote_head **heads;
+ git_remote *remote;
+ size_t i, j, n;
+
+ cl_git_pass(git_remote_create_detached(&remote, URL));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_pass(git_remote_ls(&heads, &n, remote));
+
+ cl_assert_equal_sz(n, 9);
+ for (i = 0; i < n; i++) {
+ char found = false;
+
+ for (j = 0; j < ARRAY_SIZE(refs); j++) {
+ if (!strcmp(heads[i]->name, refs[j])) {
+ found = true;
+ break;
+ }
+ }
+
+ cl_assert_(found, heads[i]->name);
+ }
+
+ git_remote_free(remote);
}
};
static const unsigned int thin_pack_len = 78;
+/*
+ * Packfile with one object. It references an object which is not in the
+ * packfile and has a corrupt length (states the deltified stream is 1 byte
+ * long, where it is actually 6).
+ */
+static const unsigned char corrupt_thin_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
+ 0x71, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95,
+ 0x10, 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63,
+ 0x62, 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x07,
+ 0x67, 0x03, 0xc5, 0x40, 0x99, 0x49, 0xb1, 0x3b, 0x7d, 0xae, 0x9b, 0x0e,
+ 0xdd, 0xde, 0xc6, 0x76, 0x43, 0x24, 0x64
+};
+static const unsigned int corrupt_thin_pack_len = 67;
+
+/*
+ * Packfile with a missing trailer.
+ */
+static const unsigned char missing_trailer_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x50, 0xf4, 0x3b,
+};
+static const unsigned int missing_trailer_pack_len = 12;
+
+/*
+ * Packfile that causes the packfile stream to open in a way in which it leaks
+ * the stream reader.
+ */
+static const unsigned char leaky_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03,
+ 0xf4, 0xbd, 0x51, 0x51, 0x51, 0x51, 0x51, 0x72, 0x65, 0x41, 0x4b, 0x63,
+ 0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, 0xbd, 0x41, 0x4b
+};
+static const unsigned int leaky_pack_len = 33;
+
static const unsigned char base_obj[] = { 07, 076 };
static const unsigned int base_obj_len = 2;
git_indexer_free(idx);
}
+void test_pack_indexer__missing_trailer(void)
+{
+ git_indexer *idx = 0;
+ git_transfer_progress stats = { 0 };
+
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+ cl_git_pass(git_indexer_append(
+ idx, missing_trailer_pack, missing_trailer_pack_len, &stats));
+ cl_git_fail(git_indexer_commit(idx, &stats));
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert_equal_i(giterr_last()->klass, GITERR_INDEXER);
+
+ git_indexer_free(idx);
+}
+
+void test_pack_indexer__leaky(void)
+{
+ git_indexer *idx = 0;
+ git_transfer_progress stats = { 0 };
+
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+ cl_git_pass(git_indexer_append(
+ idx, leaky_pack, leaky_pack_len, &stats));
+ cl_git_fail(git_indexer_commit(idx, &stats));
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert_equal_i(giterr_last()->klass, GITERR_INDEXER);
+
+ git_indexer_free(idx);
+}
+
void test_pack_indexer__fix_thin(void)
{
git_indexer *idx = NULL;
}
}
+void test_pack_indexer__corrupt_length(void)
+{
+ git_indexer *idx = NULL;
+ git_transfer_progress stats = { 0 };
+ git_repository *repo;
+ git_odb *odb;
+ git_oid id, should_id;
+
+ cl_git_pass(git_repository_init(&repo, "thin.git", true));
+ cl_git_pass(git_repository_odb(&odb, repo));
+
+ /* Store the missing base into your ODB so the indexer can fix the pack */
+ cl_git_pass(git_odb_write(&id, odb, base_obj, base_obj_len, GIT_OBJ_BLOB));
+ git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18");
+ cl_assert_equal_oid(&should_id, &id);
+
+ cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
+ cl_git_pass(git_indexer_append(
+ idx, corrupt_thin_pack, corrupt_thin_pack_len, &stats));
+ cl_git_fail(git_indexer_commit(idx, &stats));
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert_equal_i(giterr_last()->klass, GITERR_ZLIB);
+
+ git_indexer_free(idx);
+ git_odb_free(odb);
+ git_repository_free(repo);
+}
+
static int find_tmp_file_recurs(void *opaque, git_buf *path)
{
int error = 0;
strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
}
+void test_patch_parse__files_with_whitespaces_succeeds(void)
+{
+ git_patch *patch;
+ cl_git_pass(git_patch_from_buffer(&patch, PATCH_NAME_WHITESPACE, strlen(PATCH_NAME_WHITESPACE), NULL));
+ git_patch_free(patch);
+}
"@@ -9,0 +10 @@ below it!\n" \
"+insert at end\n"
-/* An insertion at the beginning and end of file (and the resultant patch) */
+#define PATCH_SIMPLE_COMMIT \
+ "commit 15e119375018fba121cf58e02a9f17fe22df0df8\n" \
+ "Author: Edward Thomson <ethomson@edwardthomson.com>\n" \
+ "Date: Wed Jun 14 13:31:20 2017 +0200\n" \
+ "\n" \
+ " CHANGELOG: document git_filter_init and GIT_FILTER_INIT\n" \
+ "\n" \
+ "diff --git a/CHANGELOG.md b/CHANGELOG.md\n" \
+ "index 1b9e0c90a..24ecba426 100644\n" \
+ "--- a/CHANGELOG.md\n" \
+ "+++ b/CHANGELOG.md\n" \
+ "@@ -96,6 +96,9 @@ v0.26\n" \
+ " * `git_transport_smart_proxy_options()' enables you to get the proxy options for\n" \
+ " smart transports.\n" \
+ "\n" \
+ "+* The `GIT_FILTER_INIT` macro and the `git_filter_init` function are provided\n" \
+ "+ to initialize a `git_filter` structure.\n" \
+ "+\n" \
+ " ### Breaking API changes\n" \
+ "\n" \
+ " * `clone_checkout_strategy` has been removed from\n"
+
+#define PATCH_MULTIPLE_HUNKS \
+ "diff --git a/x b/x\n" \
+ "index 0719398..fa0350c 100644\n" \
+ "--- a/x\n" \
+ "+++ b/x\n" \
+ "@@ -1,5 +1,4 @@\n" \
+ " 1\n" \
+ "-2\n" \
+ " 3\n" \
+ " 4\n" \
+ " 5\n" \
+ "@@ -7,3 +6,4 @@\n" \
+ " 7\n" \
+ " 8\n" \
+ " 9\n" \
+ "+10\n"
+
+#define PATCH_MULTIPLE_FILES \
+ "diff --git a/x b/x\n" \
+ "index 8a1218a..7059ba5 100644\n" \
+ "--- a/x\n" \
+ "+++ b/x\n" \
+ "@@ -1,5 +1,4 @@\n" \
+ " 1\n" \
+ " 2\n" \
+ "-3\n" \
+ " 4\n" \
+ " 5\n" \
+ "diff --git a/y b/y\n" \
+ "index e006065..9405325 100644\n" \
+ "--- a/y\n" \
+ "+++ b/y\n" \
+ "@@ -1,4 +1,5 @@\n" \
+ " a\n" \
+ " b\n" \
+ "+c\n" \
+ " d\n" \
+ " e\n"
#define FILE_PREPEND_AND_APPEND \
"first and\n" \
"+added line with no nl\n" \
"\\ No newline at end of file\n"
+#define PATCH_NAME_WHITESPACE \
+ "diff --git a/file with spaces.txt b/file with spaces.txt\n" \
+ "index 9432026..83759c0 100644\n" \
+ "--- a/file with spaces.txt\n" \
+ "+++ b/file with spaces.txt\n" \
+ "@@ -0,3 +0,2 @@\n" \
+ " and this\n" \
+ "-is additional context\n" \
+ " below it!\n" \
+
#define PATCH_CORRUPT_GIT_HEADER \
"diff --git a/file.txt\n" \
"index 9432026..0f39b9a 100644\n" \
#define ID_BRANCH_A "d853fb9f24e0fe63b3dce9fbc04fd9cfe17a030b"
#define ID_BRANCH_B "1ce9ea3ba9b4fa666602d52a5281d41a482cc58b"
-
-void test_perf_merge__initialize(void)
-{
-}
-
-void test_perf_merge__cleanup(void)
-{
-}
-
void test_perf_merge__m1(void)
{
-#if 1
- cl_skip();
-#else
perf__do_merge(SRC_REPO, "m1", ID_BRANCH_A, ID_BRANCH_B);
-#endif
}
--- /dev/null
+#include "precompiled.h"
--- /dev/null
+#include "common.h"
+#include "git2.h"
+#include "clar.h"
+#include "clar_libgit2.h"
#include "git2/rebase.h"
#include "posix.h"
#include "signature.h"
+#include "../submodule/submodule_helpers.h"
#include <fcntl.h>
// Fixture setup and teardown
void test_rebase_submodule__initialize(void)
{
+ git_index *index;
+ git_oid tree_oid, commit_id;
+ git_tree *tree;
+ git_commit *parent;
+ git_object *obj;
+ git_reference *master_ref;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
repo = cl_git_sandbox_init("rebase-submodule");
cl_git_pass(git_signature_new(&signature,
"Rebaser", "rebaser@rebaser.rb", 1405694510, 0));
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+
+ cl_git_pass(git_submodule_set_url(repo, "my-submodule", git_repository_path(repo)));
+
+ /* We have to commit the rewritten .gitmodules file */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, ".gitmodules"));
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
+
+ cl_git_pass(git_repository_head(&master_ref, repo));
+ cl_git_pass(git_commit_lookup(&parent, repo, git_reference_target(master_ref)));
+
+ cl_git_pass(git_commit_create_v(&commit_id, repo, git_reference_name(master_ref), signature, signature, NULL, "Fixup .gitmodules", tree, 1, parent));
+
+ /* And a final reset, for good measure */
+ cl_git_pass(git_object_lookup(&obj, repo, &commit_id, GIT_OBJ_COMMIT));
+ cl_git_pass(git_reset(repo, obj, GIT_RESET_HARD, &opts));
+
+ git_index_free(index);
+ git_object_free(obj);
+ git_commit_free(parent);
+ git_reference_free(master_ref);
+ git_tree_free(tree);
}
void test_rebase_submodule__cleanup(void)
git_buf untracked_path = GIT_BUF_INIT;
FILE *fp;
git_submodule *submodule;
- git_config *config;
cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus"));
cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
- git_repository_config(&config, repo);
-
- cl_git_pass(git_config_set_string(config, "submodule.my-submodule.url", git_repository_path(repo)));
-
- git_config_free(config);
-
cl_git_pass(git_submodule_lookup(&submodule, repo, "my-submodule"));
cl_git_pass(git_submodule_update(submodule, 1, NULL));
git_branch_create(&branch, repo, "inv@{id", target, 0));
}
-void test_refs_branches_create__default_reflog_message(void)
-{
- git_reflog *log;
- git_buf buf = GIT_BUF_INIT;
- const git_reflog_entry *entry;
- git_annotated_commit *annotated;
- git_signature *sig;
- git_config *cfg;
-
- cl_git_pass(git_repository_config(&cfg, repo));
- cl_git_pass(git_config_set_string(cfg, "user.name", "Foo Bar"));
- cl_git_pass(git_config_set_string(cfg, "user.email", "foo@example.com"));
- git_config_free(cfg);
-
- cl_git_pass(git_signature_default(&sig, repo));
-
- retrieve_known_commit(&target, repo);
- cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, false));
- cl_git_pass(git_reflog_read(&log, repo, "refs/heads/" NEW_BRANCH_NAME));
-
- entry = git_reflog_entry_byindex(log, 0);
- cl_git_pass(git_buf_printf(&buf, "branch: Created from %s", git_oid_tostr_s(git_commit_id(target))));
- cl_assert_equal_s(git_buf_cstr(&buf), git_reflog_entry_message(entry));
- cl_assert_equal_s(sig->email, git_reflog_entry_committer(entry)->email);
-
- cl_git_pass(git_reference_remove(repo, "refs/heads/" NEW_BRANCH_NAME));
- git_reference_free(branch);
- git_reflog_free(log);
- git_buf_clear(&buf);
-
- cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "e90810b8df3"));
- cl_git_pass(git_branch_create_from_annotated(&branch, repo, NEW_BRANCH_NAME, annotated, true));
- cl_git_pass(git_reflog_read(&log, repo, "refs/heads/" NEW_BRANCH_NAME));
-
- entry = git_reflog_entry_byindex(log, 0);
- cl_git_pass(git_buf_printf(&buf, "branch: Created from e90810b8df3"));
- cl_assert_equal_s(git_buf_cstr(&buf), git_reflog_entry_message(entry));
- cl_assert_equal_s(sig->email, git_reflog_entry_committer(entry)->email);
-
- git_annotated_commit_free(annotated);
- git_buf_free(&buf);
- git_reflog_free(log);
- git_signature_free(sig);
-}
-
static void assert_branch_matches_name(
const char *expected, const char *lookup_as)
{
git_reference_free(branch);
}
-void test_refs_branches_move__default_reflog_message(void)
-{
- git_reference *branch;
- git_reference *new_branch;
- git_reflog *log;
- const git_reflog_entry *entry;
- git_signature *sig;
- git_config *cfg;
- git_oid id;
-
- cl_git_pass(git_repository_config(&cfg, repo));
- cl_git_pass(git_config_set_string(cfg, "user.name", "Foo Bar"));
- cl_git_pass(git_config_set_string(cfg, "user.email", "foo@example.com"));
- git_config_free(cfg);
-
- cl_git_pass(git_signature_default(&sig, repo));
-
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
- git_oid_cpy(&id, git_reference_target(branch));
- cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0));
-
- cl_git_pass(git_reflog_read(&log, repo, git_reference_name(new_branch)));
- entry = git_reflog_entry_byindex(log, 0);
- cl_assert_equal_s("branch: renamed refs/heads/master to refs/heads/master2",
- git_reflog_entry_message(entry));
- cl_assert_equal_s(sig->email, git_reflog_entry_committer(entry)->email);
- cl_assert_equal_oid(&id, git_reflog_entry_id_old(entry));
- cl_assert_equal_oid(&id, git_reflog_entry_id_new(entry));
-
- git_reference_free(branch);
- git_reference_free(new_branch);
- git_reflog_free(log);
- git_signature_free(sig);
-}
-
void test_refs_branches_move__can_move_with_unicode(void)
{
git_reference *original_ref, *new_ref;
git_reference *ref, *ref2;
const char *REFNAME = "refs/heads/xxx";
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ repo = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_reference_symbolic_create(&ref, repo, REFNAME, "refs/heads/master", 0, NULL));
cl_git_pass(git_reference_lookup(&ref2, repo, REFNAME));
cl_git_pass(git_reference_delete(ref));
/* reference is gone from disk, so reloading it will fail */
cl_git_fail(git_reference_lookup(&ref2, repo, REFNAME));
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
}
+++ /dev/null
-#include "clar_libgit2.h"
-
-#include "repository.h"
-#include "git2/reflog.h"
-#include "reflog.h"
-#include "ref_helpers.h"
-
-static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
-
-static git_repository *g_repo;
-
-void test_refs_createwithlog__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_refs_createwithlog__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_refs_createwithlog__creating_a_direct_reference_adds_a_reflog_entry(void)
-{
- git_reference *reference;
- git_oid id;
- git_reflog *reflog;
- const git_reflog_entry *entry;
-
- const char *name = "refs/heads/new-head";
- const char *message = "You've been logged, mate!";
-
- git_oid_fromstr(&id, current_master_tip);
-
- cl_git_pass(
- git_reference_create(&reference, g_repo, name, &id, 0, message));
-
- cl_git_pass(git_reflog_read(&reflog, g_repo, name));
- cl_assert_equal_sz(1, git_reflog_entrycount(reflog));
-
- entry = git_reflog_entry_byindex(reflog, 0);
- cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
- cl_assert_equal_oid(&id, &entry->oid_cur);
- cl_assert_equal_s(message, entry->msg);
-
- git_reflog_free(reflog);
- git_reference_free(reference);
-}
void test_refs_iterator__initialize(void)
{
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ repo = cl_git_sandbox_init("testrepo.git");
}
void test_refs_iterator__cleanup(void)
{
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
}
static const char *refnames[] = {
"refs/tags/taggerless",
"refs/tags/test",
"refs/tags/wrapped_tag",
+ NULL
+};
+
+static const char *refnames_with_symlink[] = {
+ "refs/heads/br2",
+ "refs/heads/cannot-fetch",
+ "refs/heads/chomped",
+ "refs/heads/haacked",
+ "refs/heads/link/a",
+ "refs/heads/link/b",
+ "refs/heads/link/c",
+ "refs/heads/link/d",
+ "refs/heads/master",
+ "refs/heads/not-good",
+ "refs/heads/packed",
+ "refs/heads/packed-test",
+ "refs/heads/subtrees",
+ "refs/heads/test",
+ "refs/heads/track-local",
+ "refs/heads/trailing",
+ "refs/notes/fanout",
+ "refs/remotes/test/master",
+ "refs/tags/annotated_tag_to_blob",
+ "refs/tags/e90810b",
+ "refs/tags/hard_tag",
+ "refs/tags/point_to_blob",
+ "refs/tags/taggerless",
+ "refs/tags/test",
+ "refs/tags/wrapped_tag",
+ NULL
};
static int refcmp_cb(const void *a, const void *b)
return strcmp(refa->name, refb->name);
}
-static void assert_all_refnames_match(git_vector *output)
+static void assert_all_refnames_match(const char **expected, git_vector *names)
{
size_t i;
git_reference *ref;
- cl_assert_equal_sz(output->length, ARRAY_SIZE(refnames));
-
- git_vector_sort(output);
+ git_vector_sort(names);
- git_vector_foreach(output, i, ref) {
- cl_assert_equal_s(ref->name, refnames[i]);
+ git_vector_foreach(names, i, ref) {
+ cl_assert(expected[i] != NULL);
+ cl_assert_equal_s(expected[i], ref->name);
git_reference_free(ref);
}
+ cl_assert(expected[i] == NULL);
- git_vector_free(output);
+ git_vector_free(names);
}
void test_refs_iterator__list(void)
git_reference_iterator_free(iter);
- assert_all_refnames_match(&output);
+ assert_all_refnames_match(refnames, &output);
}
void test_refs_iterator__empty(void)
git_vector output;
cl_git_pass(git_vector_init(&output, 32, &refcmp_cb));
cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output));
- assert_all_refnames_match(&output);
+ assert_all_refnames_match(refnames, &output);
+}
+
+void test_refs_iterator__foreach_through_symlink(void)
+{
+ git_vector output;
+
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ cl_git_pass(git_vector_init(&output, 32, &refcmp_cb));
+
+ cl_git_pass(p_mkdir("refs", 0777));
+ cl_git_mkfile("refs/a", "1234567890123456789012345678901234567890");
+ cl_git_mkfile("refs/b", "1234567890123456789012345678901234567890");
+ cl_git_mkfile("refs/c", "1234567890123456789012345678901234567890");
+ cl_git_mkfile("refs/d", "1234567890123456789012345678901234567890");
+
+ cl_git_pass(p_symlink("../../../refs", "testrepo.git/refs/heads/link"));
+
+ cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output));
+ assert_all_refnames_match(refnames_with_symlink, &output);
}
static int refs_foreach_cancel_cb(git_reference *reference, void *payload)
cl_git_pass(
git_reference_foreach_name(repo, refs_foreach_name_cb, &output));
- cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames));
git_vector_sort(&output);
git_vector_foreach(&output, i, name) {
- cl_assert_equal_s(name, refnames[i]);
+ cl_assert(refnames[i] != NULL);
+ cl_assert_equal_s(refnames[i], name);
git__free(name);
}
const char *name;
int error;
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
repo = cl_git_sandbox_init("testrepo");
cl_git_pass(git_reference_iterator_new(&iter, repo));
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert_equal_i(full_count, concurrent_count);
-
- cl_git_sandbox_cleanup();
- repo = NULL;
}
/* We have exactly 12 refs in total if we include the packed ones:
* there is a reference that exists both in the packfile and as
* loose, but we only list it once */
- cl_assert_equal_i((int)ref_list.count, 17);
+ cl_assert_equal_i((int)ref_list.count, 18);
git_strarray_free(&ref_list);
}
"144344043ba4d4a405da03de3844aa829ae8be0e\n");
cl_git_pass(git_reference_list(&ref_list, g_repo));
- cl_assert_equal_i((int)ref_list.count, 17);
+ cl_assert_equal_i((int)ref_list.count, 18);
git_strarray_free(&ref_list);
}
"0df1a5865c8abfc09f1f2182e6a31be550e99f07",
GIT_OBJ_COMMIT);
}
+
+void test_refs_peel__can_peel_fully_peeled_tag_to_tag(void)
+{
+ assert_peel_generic(g_peel_repo,
+ "refs/tags/tag-inside-tags", GIT_OBJ_TAG,
+ "c2596aa0151888587ec5c0187f261e63412d9e11",
+ GIT_OBJ_TAG);
+ assert_peel_generic(g_peel_repo,
+ "refs/foo/tag-outside-tags", GIT_OBJ_TAG,
+ "c2596aa0151888587ec5c0187f261e63412d9e11",
+ GIT_OBJ_TAG);
+}
--- /dev/null
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "refs.h"
+#include "reflog_helpers.h"
+
+static const char *g_email = "foo@example.com";
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_refs_reflog_messages__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_set_ident(g_repo, "Foo Bar", g_email));
+}
+
+void test_refs_reflog_messages__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_reflog_messages__setting_head_updates_reflog(void)
+{
+ git_object *tag;
+ git_signature *sig;
+ git_annotated_commit *annotated;
+
+ cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); /* 4 */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/unborn"));
+ cl_git_pass(git_revparse_single(&tag, g_repo, "tags/test"));
+ cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(tag))); /* 3 */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); /* 2 */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/tags/test")); /* 1 */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/remotes/test/master")); /* 0 */
+
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 4,
+ NULL, "refs/heads/haacked",
+ "foo@example.com",
+ "checkout: moving from master to haacked");
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 3,
+ NULL, "tags/test^{commit}",
+ "foo@example.com",
+ "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 2,
+ "tags/test^{commit}", "refs/heads/haacked",
+ "foo@example.com",
+ "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 1,
+ "refs/heads/haacked", "tags/test^{commit}",
+ "foo@example.com",
+ "checkout: moving from haacked to test");
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "tags/test^{commit}", "refs/remotes/test/master",
+ "foo@example.com",
+ "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to test/master");
+
+ cl_git_pass(git_annotated_commit_from_revspec(&annotated, g_repo, "haacked~0"));
+ cl_git_pass(git_repository_set_head_detached_from_annotated(g_repo, annotated));
+
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ NULL, "refs/heads/haacked",
+ "foo@example.com",
+ "checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0");
+
+ git_annotated_commit_free(annotated);
+ git_object_free(tag);
+ git_signature_free(sig);
+}
+
+void test_refs_reflog_messages__setting_head_to_same_target_ignores_reflog(void)
+{
+ size_t nentries, nentries_after;
+
+ nentries = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
+
+ nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_assert_equal_i(nentries + 1, nentries_after);
+}
+
+void test_refs_reflog_messages__detaching_writes_reflog(void)
+{
+ git_signature *sig;
+ git_oid id;
+ const char *msg;
+
+ cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+ msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
+ git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d");
+ cl_git_pass(git_repository_set_head_detached(g_repo, &id));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "e90810b8df3e80c413d903f631643c716887138d",
+ NULL, msg);
+
+ msg = "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked";
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "e90810b8df3e80c413d903f631643c716887138d",
+ "258f0e2a959a364e40ed6603d5d44fbb24765b10",
+ NULL, msg);
+
+ git_signature_free(sig);
+}
+
+void test_refs_reflog_messages__orphan_branch_does_not_count(void)
+{
+ git_oid id;
+ const char *msg;
+
+ /* Have something known */
+ msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
+ git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d");
+ cl_git_pass(git_repository_set_head_detached(g_repo, &id));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "e90810b8df3e80c413d903f631643c716887138d",
+ NULL, msg);
+
+ /* Switching to an orphan branch does not write to the reflog */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/orphan"));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "e90810b8df3e80c413d903f631643c716887138d",
+ NULL, msg);
+
+ /* And coming back, we set the source to zero */
+ msg = "checkout: moving from orphan to haacked";
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "0000000000000000000000000000000000000000",
+ "258f0e2a959a364e40ed6603d5d44fbb24765b10",
+ NULL, msg);
+}
+
+void test_refs_reflog_messages__branch_birth(void)
+{
+ git_signature *sig;
+ git_oid id;
+ git_tree *tree;
+ git_reference *ref;
+ const char *msg;
+ size_t nentries, nentries_after;
+
+ nentries = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+ cl_git_pass(git_repository_head(&ref, g_repo));
+ cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJ_TREE));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/orphan"));
+
+ nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_assert_equal_i(nentries, nentries_after);
+
+ msg = "message 2";
+ cl_git_pass(git_commit_create(&id, g_repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
+
+ cl_assert_equal_i(1, reflog_entrycount(g_repo, "refs/heads/orphan"));
+
+ nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_assert_equal_i(nentries + 1, nentries_after);
+
+ git_signature_free(sig);
+ git_tree_free(tree);
+ git_reference_free(ref);
+}
+
+void test_refs_reflog_messages__commit_on_symbolic_ref_updates_head_reflog(void)
+{
+ git_signature *sig;
+ git_oid id;
+ git_tree *tree;
+ git_reference *ref1, *ref2;
+ const char *msg;
+ size_t nentries_head, nentries_master;
+
+ nentries_head = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+ cl_git_pass(git_repository_head(&ref1, g_repo));
+ cl_git_pass(git_reference_peel((git_object **) &tree, ref1, GIT_OBJ_TREE));
+
+ nentries_master = reflog_entrycount(g_repo, "refs/heads/master");
+
+ msg = "message 1";
+ cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, "refs/heads/master", "refs/heads/foo", 1, msg));
+
+ cl_assert_equal_i(0, reflog_entrycount(g_repo, "refs/heads/foo"));
+ cl_assert_equal_i(nentries_head, reflog_entrycount(g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(nentries_master, reflog_entrycount(g_repo, "refs/heads/master"));
+
+ msg = "message 2";
+ cl_git_pass(git_commit_create(&id, g_repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
+
+ cl_assert_equal_i(1, reflog_entrycount(g_repo, "refs/heads/foo"));
+ cl_assert_equal_i(nentries_head + 1, reflog_entrycount(g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(nentries_master, reflog_entrycount(g_repo, "refs/heads/master"));
+
+ git_signature_free(sig);
+ git_reference_free(ref1);
+ git_reference_free(ref2);
+ git_tree_free(tree);
+}
+
+void test_refs_reflog_messages__show_merge_for_merge_commits(void)
+{
+ git_oid b1_oid;
+ git_oid b2_oid;
+ git_oid merge_commit_oid;
+ git_commit *b1_commit;
+ git_commit *b2_commit;
+ git_signature *s;
+ git_commit *parent_commits[2];
+ git_tree *tree;
+
+ cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
+
+ cl_git_pass(git_reference_name_to_id(&b1_oid, g_repo, "HEAD"));
+ cl_git_pass(git_reference_name_to_id(&b2_oid, g_repo, "refs/heads/test"));
+
+ cl_git_pass(git_commit_lookup(&b1_commit, g_repo, &b1_oid));
+ cl_git_pass(git_commit_lookup(&b2_commit, g_repo, &b2_oid));
+
+ parent_commits[0] = b1_commit;
+ parent_commits[1] = b2_commit;
+
+ cl_git_pass(git_commit_tree(&tree, b1_commit));
+
+ cl_git_pass(git_commit_create(&merge_commit_oid,
+ g_repo, "HEAD", s, s, NULL,
+ "Merge commit", tree,
+ 2, (const struct git_commit **) parent_commits));
+
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ NULL,
+ git_oid_tostr_s(&merge_commit_oid),
+ NULL, "commit (merge): Merge commit");
+
+ git_tree_free(tree);
+ git_commit_free(b1_commit);
+ git_commit_free(b2_commit);
+ git_signature_free(s);
+}
+
+void test_refs_reflog_messages__creating_a_direct_reference(void)
+{
+ git_reference *reference;
+ git_oid id;
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+
+ const char *name = "refs/heads/new-head";
+ const char *message = "You've been logged, mate!";
+
+ cl_git_pass(git_reference_name_to_id(&id, g_repo, "HEAD"));
+
+ cl_git_pass(git_reference_create(&reference, g_repo, name, &id, 0, message));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, name));
+ cl_assert_equal_sz(1, git_reflog_entrycount(reflog));
+
+ entry = git_reflog_entry_byindex(reflog, 0);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
+ cl_assert_equal_oid(&id, &entry->oid_cur);
+ cl_assert_equal_s(message, entry->msg);
+
+ git_reflog_free(reflog);
+ git_reference_free(reference);
+}
+
+
+void test_refs_reflog_messages__renaming_ref(void)
+{
+ git_reference *ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_rename(&new_ref, ref, "refs/heads/renamed", false,
+ "message"));
+
+ cl_reflog_check_entry(g_repo, git_reference_name(new_ref), 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "foo@example.com", "message");
+
+ git_reference_free(ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_reflog_messages__updating_a_direct_reference(void)
+{
+ git_reference *ref, *ref_out, *target_ref;
+ git_oid target_id;
+ const char *message = "You've been logged, mate!";
+
+ git_reference_name_to_id(&target_id, g_repo, "refs/heads/haacked");
+ cl_git_pass(git_reference_lookup(&target_ref, g_repo, "refs/heads/haacked"));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_reference_set_target(&ref_out, ref, &target_id, message));
+
+ cl_reflog_check_entry(g_repo, "refs/heads/master", 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "258f0e2a959a364e40ed6603d5d44fbb24765b10",
+ NULL, message);
+
+ git_reference_free(target_ref);
+ git_reference_free(ref);
+ git_reference_free(ref_out);
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_reflog_messages__creating_branches_default_messages(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_annotated_commit *annotated;
+ git_object *obj;
+ git_commit *target;
+ git_reference *branch1, *branch2;
+
+ cl_git_pass(git_revparse_single(&obj, g_repo, "e90810b8df3"));
+ cl_git_pass(git_commit_lookup(&target, g_repo, git_object_id(obj)));
+ git_object_free(obj);
+
+ cl_git_pass(git_branch_create(&branch1, g_repo, NEW_BRANCH_NAME, target, false));
+
+ cl_git_pass(git_buf_printf(&buf, "branch: Created from %s", git_oid_tostr_s(git_commit_id(target))));
+ cl_reflog_check_entry(g_repo, "refs/heads/" NEW_BRANCH_NAME, 0,
+ GIT_OID_HEX_ZERO,
+ git_oid_tostr_s(git_commit_id(target)),
+ g_email, git_buf_cstr(&buf));
+
+ cl_git_pass(git_reference_remove(g_repo, "refs/heads/" NEW_BRANCH_NAME));
+
+ cl_git_pass(git_annotated_commit_from_revspec(&annotated, g_repo, "e90810b8df3"));
+ cl_git_pass(git_branch_create_from_annotated(&branch2, g_repo, NEW_BRANCH_NAME, annotated, true));
+
+ cl_reflog_check_entry(g_repo, "refs/heads/" NEW_BRANCH_NAME, 0,
+ GIT_OID_HEX_ZERO,
+ git_oid_tostr_s(git_commit_id(target)),
+ g_email, "branch: Created from e90810b8df3");
+
+ git_annotated_commit_free(annotated);
+ git_buf_free(&buf);
+ git_commit_free(target);
+ git_reference_free(branch1);
+ git_reference_free(branch2);
+}
+
+void test_refs_reflog_messages__moving_branch_default_message(void)
+{
+ git_reference *branch;
+ git_reference *new_branch;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/master"));
+ git_oid_cpy(&id, git_reference_target(branch));
+ cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0));
+
+ cl_reflog_check_entry(g_repo, git_reference_name(new_branch), 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ g_email,
+ "branch: renamed refs/heads/master to refs/heads/master2");
+
+ git_reference_free(branch);
+ git_reference_free(new_branch);
+}
+
+void test_refs_reflog_messages__detaching_head_default_message(void)
+{
+ git_reference *ref;
+
+ cl_assert_equal_i(false, git_repository_head_detached(g_repo));
+
+ cl_git_pass(git_repository_detach_head(g_repo));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL, "checkout: moving from master to a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_assert_equal_i(true, git_repository_head_detached(g_repo));
+
+ /* take the repo back to its original state */
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "HEAD", "refs/heads/master",
+ true, "REATTACH"));
+
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL, "REATTACH");
+
+ cl_assert_equal_i(false, git_repository_head_detached(g_repo));
+
+ git_reference_free(ref);
+}
#include "git2/reflog.h"
#include "reflog.h"
-static const char *merge_reflog_message = "commit (merge): Merge commit";
static const char *new_ref = "refs/heads/test-reflog";
static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
#define commit_msg "commit: bla bla"
assert_no_reflog_update();
}
-
-void test_refs_reflog_reflog__show_merge_for_merge_commits(void)
-{
- git_oid b1_oid;
- git_oid b2_oid;
- git_oid merge_commit_oid;
- git_commit *b1_commit;
- git_commit *b2_commit;
- git_signature *s;
- git_commit *parent_commits[2];
- git_tree *tree;
- git_reflog *log;
- const git_reflog_entry *entry;
-
- cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
-
- cl_git_pass(git_reference_name_to_id(&b1_oid, g_repo, "HEAD"));
- cl_git_pass(git_reference_name_to_id(&b2_oid, g_repo, "refs/heads/test"));
-
- cl_git_pass(git_commit_lookup(&b1_commit, g_repo, &b1_oid));
- cl_git_pass(git_commit_lookup(&b2_commit, g_repo, &b2_oid));
-
- parent_commits[0] = b1_commit;
- parent_commits[1] = b2_commit;
-
- cl_git_pass(git_commit_tree(&tree, b1_commit));
-
- cl_git_pass(git_commit_create(&merge_commit_oid,
- g_repo, "HEAD", s, s, NULL,
- "Merge commit", tree,
- 2, (const struct git_commit **) parent_commits));
-
- cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
- entry = git_reflog_entry_byindex(log, 0);
- cl_assert_equal_s(merge_reflog_message, git_reflog_entry_message(entry));
-
- git_reflog_free(log);
- git_tree_free(tree);
- git_commit_free(b1_commit);
- git_commit_free(b2_commit);
- git_signature_free(s);
-}
--- /dev/null
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "reflog.h"
+
+static int reflog_entry_tostr(git_buf *out, const git_reflog_entry *entry)
+{
+ char old_oid[GIT_OID_HEXSZ], new_oid[GIT_OID_HEXSZ];
+
+ assert(out && entry);
+
+ git_oid_tostr((char *)&old_oid, GIT_OID_HEXSZ, git_reflog_entry_id_old(entry));
+ git_oid_tostr((char *)&new_oid, GIT_OID_HEXSZ, git_reflog_entry_id_new(entry));
+
+ return git_buf_printf(out, "%s %s %s %s", old_oid, new_oid, "somesig", git_reflog_entry_message(entry));
+}
+
+size_t reflog_entrycount(git_repository *repo, const char *name)
+{
+ git_reflog *log;
+ size_t ret;
+
+ cl_git_pass(git_reflog_read(&log, repo, name));
+ ret = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ return ret;
+}
+
+void cl_reflog_check_entry_(git_repository *repo, const char *reflog, size_t idx,
+ const char *old_spec, const char *new_spec,
+ const char *email, const char *message, const char *file, int line)
+{
+ git_reflog *log;
+ const git_reflog_entry *entry;
+ git_buf result = GIT_BUF_INIT;
+
+ cl_git_pass(git_reflog_read(&log, repo, reflog));
+ entry = git_reflog_entry_byindex(log, idx);
+ if (entry == NULL)
+ clar__fail(file, line, "Reflog has no such entry", NULL, 1);
+
+ if (old_spec) {
+ git_object *obj = NULL;
+ if (git_revparse_single(&obj, repo, old_spec) == GIT_OK) {
+ if (git_oid_cmp(git_object_id(obj), git_reflog_entry_id_old(entry)) != 0) {
+ git_oid__writebuf(&result, "\tOld OID: \"", git_object_id(obj));
+ git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_old(entry));
+ git_buf_puts(&result, "\"\n");
+ }
+ git_object_free(obj);
+ } else {
+ git_oid *oid = git__calloc(1, sizeof(*oid));
+ git_oid_fromstr(oid, old_spec);
+ if (git_oid_cmp(oid, git_reflog_entry_id_old(entry)) != 0) {
+ git_oid__writebuf(&result, "\tOld OID: \"", oid);
+ git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_old(entry));
+ git_buf_puts(&result, "\"\n");
+ }
+ git__free(oid);
+ }
+ }
+ if (new_spec) {
+ git_object *obj = NULL;
+ if (git_revparse_single(&obj, repo, new_spec) == GIT_OK) {
+ if (git_oid_cmp(git_object_id(obj), git_reflog_entry_id_new(entry)) != 0) {
+ git_oid__writebuf(&result, "\tNew OID: \"", git_object_id(obj));
+ git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_new(entry));
+ git_buf_puts(&result, "\"\n");
+ }
+ git_object_free(obj);
+ } else {
+ git_oid *oid = git__calloc(1, sizeof(*oid));
+ git_oid_fromstr(oid, new_spec);
+ if (git_oid_cmp(oid, git_reflog_entry_id_new(entry)) != 0) {
+ git_oid__writebuf(&result, "\tNew OID: \"", oid);
+ git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_new(entry));
+ git_buf_puts(&result, "\"\n");
+ }
+ git__free(oid);
+ }
+ }
+
+ if (email && strcmp(email, git_reflog_entry_committer(entry)->email) != 0)
+ git_buf_printf(&result, "\tEmail: \"%s\" != \"%s\"\n", email, git_reflog_entry_committer(entry)->email);
+
+ if (message) {
+ const char *entry_msg = git_reflog_entry_message(entry);
+ if (entry_msg == NULL) entry_msg = "";
+
+ if (entry_msg && strcmp(message, entry_msg) != 0)
+ git_buf_printf(&result, "\tMessage: \"%s\" != \"%s\"\n", message, entry_msg);
+ }
+ if (git_buf_len(&result) != 0)
+ clar__fail(file, line, "Reflog entry mismatch", git_buf_cstr(&result), 1);
+
+ git_buf_free(&result);
+ git_reflog_free(log);
+}
+
+void reflog_print(git_repository *repo, const char *reflog_name)
+{
+ git_reflog *reflog;
+ size_t idx;
+ git_buf out = GIT_BUF_INIT;
+
+ git_reflog_read(&reflog, repo, reflog_name);
+
+ for (idx = 0; idx < git_reflog_entrycount(reflog); idx++) {
+ const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, idx);
+ reflog_entry_tostr(&out, entry);
+ git_buf_putc(&out, '\n');
+ }
+
+ fprintf(stderr, "%s", git_buf_cstr(&out));
+ git_buf_free(&out);
+ git_reflog_free(reflog);
+}
--- /dev/null
+size_t reflog_entrycount(git_repository *repo, const char *name);
+
+#define cl_reflog_check_entry(repo, reflog, idx, old_spec, new_spec, email, message) \
+ cl_reflog_check_entry_(repo, reflog, idx, old_spec, new_spec, email, message, __FILE__, __LINE__)
+
+void cl_reflog_check_entry_(git_repository *repo, const char *reflog, size_t idx,
+ const char *old_spec, const char *new_spec,
+ const char *email, const char *message, const char *file, int line);
+
+void reflog_print(git_repository *repo, const char *reflog_name);
git_reference_free(ref);
}
-
-void test_refs_rename__writes_to_reflog(void)
-{
- git_reference *ref, *new_ref;
- git_reflog *log;
- const git_reflog_entry *entry;
-
- cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
- cl_git_pass(git_reference_rename(&new_ref, ref, ref_one_name_new, false,
- "message"));
- cl_git_pass(git_reflog_read(&log, g_repo, git_reference_name(new_ref)));
- entry = git_reflog_entry_byindex(log, 0);
- cl_assert_equal_s("message", git_reflog_entry_message(entry));
- cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email);
-
- git_reflog_free(log);
- git_reference_free(ref);
- git_reference_free(new_ref);
-}
+++ /dev/null
-#include "clar_libgit2.h"
-
-#include "repository.h"
-#include "git2/reflog.h"
-#include "reflog.h"
-#include "ref_helpers.h"
-
-static const char *br2_tip = "a4a7dce85cf63874e984719f4fdd239f5145052f";
-static const char *master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
-static const char *br2_name = "refs/heads/br2";
-
-static git_repository *g_repo;
-
-void test_refs_settargetwithlog__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_refs_settargetwithlog__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry(void)
-{
- git_reference *reference, *reference_out;
- git_oid current_id, target_id;
- git_reflog *reflog;
- const git_reflog_entry *entry;
-
- const char *message = "You've been logged, mate!";
-
- git_oid_fromstr(¤t_id, br2_tip);
- git_oid_fromstr(&target_id, master_tip);
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, br2_name));
-
- cl_git_pass(git_reference_set_target(
- &reference_out, reference, &target_id, message));
-
- cl_git_pass(git_reflog_read(&reflog, g_repo, br2_name));
-
- entry = git_reflog_entry_byindex(reflog, 0);
- cl_assert_equal_oid(¤t_id, &entry->oid_old);
- cl_assert_equal_oid(&target_id, &entry->oid_cur);
- cl_assert_equal_s(message, entry->msg);
-
- git_reflog_free(reflog);
- git_reference_free(reference_out);
- git_reference_free(reference);
-}
cl_git_sandbox_cleanup();
}
-static void check_last_reflog_entry(const char *email, const char *message)
-{
- git_reflog *log;
- const git_reflog_entry *entry;
-
- cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
- cl_assert(git_reflog_entrycount(log) > 0);
- entry = git_reflog_entry_byindex(log, 0);
- if (email)
- cl_assert_equal_s(email, git_reflog_entry_committer(entry)->email);
- if (message)
- cl_assert_equal_s(message, git_reflog_entry_message(entry));
- git_reflog_free(log);
-}
-
-void test_repo_head__head_detached(void)
-{
- git_reference *ref;
-
- cl_assert_equal_i(false, git_repository_head_detached(repo));
-
- cl_git_pass(git_repository_detach_head(repo));
- check_last_reflog_entry(g_email, "checkout: moving from master to a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- cl_assert_equal_i(true, git_repository_head_detached(repo));
-
- /* take the repo back to it's original state */
- cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master",
- true, "REATTACH"));
- git_reference_free(ref);
-
- check_last_reflog_entry(g_email, "REATTACH");
- cl_assert_equal_i(false, git_repository_head_detached(repo));
-}
-
void test_repo_head__unborn_head(void)
{
git_reference *ref;
cl_assert_equal_i(false, git_repository_head_detached(repo));
}
-
-static void test_reflog(git_repository *repo, size_t idx,
- const char *old_spec, const char *new_spec,
- const char *email, const char *message)
-{
- git_reflog *log;
- const git_reflog_entry *entry;
-
- cl_git_pass(git_reflog_read(&log, repo, "HEAD"));
- entry = git_reflog_entry_byindex(log, idx);
-
- if (old_spec) {
- git_object *obj;
- cl_git_pass(git_revparse_single(&obj, repo, old_spec));
- cl_assert_equal_oid(git_object_id(obj), git_reflog_entry_id_old(entry));
- git_object_free(obj);
- }
- if (new_spec) {
- git_object *obj;
- cl_git_pass(git_revparse_single(&obj, repo, new_spec));
- cl_assert_equal_oid(git_object_id(obj), git_reflog_entry_id_new(entry));
- git_object_free(obj);
- }
-
- if (email) {
- cl_assert_equal_s(email, git_reflog_entry_committer(entry)->email);
- }
- if (message) {
- cl_assert_equal_s(message, git_reflog_entry_message(entry));
- }
-
- git_reflog_free(log);
-}
-
-void test_repo_head__setting_head_updates_reflog(void)
-{
- git_object *tag;
- git_signature *sig;
- git_annotated_commit *annotated;
-
- cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
-
- cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
- cl_git_pass(git_repository_set_head(repo, "refs/heads/unborn"));
- cl_git_pass(git_revparse_single(&tag, repo, "tags/test"));
- cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag)));
- cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
- cl_git_pass(git_repository_set_head(repo, "refs/tags/test"));
- cl_git_pass(git_repository_set_head(repo, "refs/remotes/test/master"));
-
- test_reflog(repo, 4, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from master to haacked");
- test_reflog(repo, 3, NULL, "tags/test^{commit}", "foo@example.com", "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
- test_reflog(repo, 2, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
- test_reflog(repo, 1, "refs/heads/haacked", "tags/test^{commit}", "foo@example.com", "checkout: moving from haacked to test");
- test_reflog(repo, 0, "tags/test^{commit}", "refs/remotes/test/master", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to test/master");
-
- cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "haacked~0"));
- cl_git_pass(git_repository_set_head_detached_from_annotated(repo, annotated));
-
- test_reflog(repo, 0, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0");
-
- git_annotated_commit_free(annotated);
- git_object_free(tag);
- git_signature_free(sig);
-}
-
-static void assert_head_reflog(git_repository *repo, size_t idx,
- const char *old_id, const char *new_id, const char *message)
-{
- git_reflog *log;
- const git_reflog_entry *entry;
- char id_str[GIT_OID_HEXSZ + 1] = {0};
-
- cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
- entry = git_reflog_entry_byindex(log, idx);
-
- git_oid_fmt(id_str, git_reflog_entry_id_old(entry));
- cl_assert_equal_s(old_id, id_str);
-
- git_oid_fmt(id_str, git_reflog_entry_id_new(entry));
- cl_assert_equal_s(new_id, id_str);
-
- cl_assert_equal_s(message, git_reflog_entry_message(entry));
-
- git_reflog_free(log);
-}
-
-void test_repo_head__detaching_writes_reflog(void)
-{
- git_signature *sig;
- git_oid id;
- const char *msg;
-
- cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
-
- msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
- git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d");
- cl_git_pass(git_repository_set_head_detached(repo, &id));
- assert_head_reflog(repo, 0, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- "e90810b8df3e80c413d903f631643c716887138d", msg);
-
- msg = "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked";
- cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
- assert_head_reflog(repo, 0, "e90810b8df3e80c413d903f631643c716887138d",
- "258f0e2a959a364e40ed6603d5d44fbb24765b10", msg);
-
- git_signature_free(sig);
-}
-
-void test_repo_head__orphan_branch_does_not_count(void)
-{
- git_oid id;
- const char *msg;
-
- /* Have something known */
- msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
- git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d");
- cl_git_pass(git_repository_set_head_detached(repo, &id));
- assert_head_reflog(repo, 0, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- "e90810b8df3e80c413d903f631643c716887138d", msg);
-
- /* Switching to an orphan branch does not write tot he reflog */
- cl_git_pass(git_repository_set_head(repo, "refs/heads/orphan"));
- assert_head_reflog(repo, 0, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- "e90810b8df3e80c413d903f631643c716887138d", msg);
-
- /* And coming back, we set the source to zero */
- msg = "checkout: moving from orphan to haacked";
- cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
- assert_head_reflog(repo, 0, "0000000000000000000000000000000000000000",
- "258f0e2a959a364e40ed6603d5d44fbb24765b10", msg);
-}
-
-void test_repo_head__set_to_current_target(void)
-{
- git_reflog *log;
- size_t nentries, nentries_after;
-
- cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
- nentries = git_reflog_entrycount(log);
- git_reflog_free(log);
-
- cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
- cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
-
- cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
- nentries_after = git_reflog_entrycount(log);
- git_reflog_free(log);
-
- cl_assert_equal_i(nentries + 1, nentries_after);
-}
-
-void test_repo_head__branch_birth(void)
-{
- git_signature *sig;
- git_oid id;
- git_tree *tree;
- git_reference *ref;
- const char *msg;
- git_reflog *log;
- size_t nentries, nentries_after;
-
- cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
- nentries = git_reflog_entrycount(log);
- git_reflog_free(log);
-
- cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
-
- cl_git_pass(git_repository_head(&ref, repo));
- cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJ_TREE));
- git_reference_free(ref);
-
- cl_git_pass(git_repository_set_head(repo, "refs/heads/orphan"));
-
- cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
- nentries_after = git_reflog_entrycount(log);
- git_reflog_free(log);
-
- cl_assert_equal_i(nentries, nentries_after);
-
- msg = "message 2";
- cl_git_pass(git_commit_create(&id, repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
-
- git_tree_free(tree);
-
- cl_git_pass(git_reflog_read(&log, repo, "refs/heads/orphan"));
- cl_assert_equal_i(1, git_reflog_entrycount(log));
- git_reflog_free(log);
-
- cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
- nentries_after = git_reflog_entrycount(log);
- git_reflog_free(log);
-
- cl_assert_equal_i(nentries + 1, nentries_after);
-
- git_signature_free(sig);
-
-}
-
-static size_t entrycount(git_repository *repo, const char *name)
-{
- git_reflog *log;
- size_t ret;
-
- cl_git_pass(git_reflog_read(&log, repo, name));
- ret = git_reflog_entrycount(log);
- git_reflog_free(log);
-
- return ret;
-}
-
-void test_repo_head__symref_chain(void)
-{
- git_signature *sig;
- git_oid id;
- git_tree *tree;
- git_reference *ref;
- const char *msg;
- size_t nentries, nentries_master;
-
- nentries = entrycount(repo, GIT_HEAD_FILE);
-
- cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
-
- cl_git_pass(git_repository_head(&ref, repo));
- cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJ_TREE));
- git_reference_free(ref);
-
- nentries_master = entrycount(repo, "refs/heads/master");
-
- msg = "message 1";
- cl_git_pass(git_reference_symbolic_create(&ref, repo, "refs/heads/master", "refs/heads/foo", 1, msg));
- git_reference_free(ref);
-
- cl_assert_equal_i(0, entrycount(repo, "refs/heads/foo"));
- cl_assert_equal_i(nentries, entrycount(repo, GIT_HEAD_FILE));
- cl_assert_equal_i(nentries_master, entrycount(repo, "refs/heads/master"));
-
- msg = "message 2";
- cl_git_pass(git_commit_create(&id, repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
- git_tree_free(tree);
-
- cl_assert_equal_i(1, entrycount(repo, "refs/heads/foo"));
- cl_assert_equal_i(nentries +1, entrycount(repo, GIT_HEAD_FILE));
- cl_assert_equal_i(nentries_master, entrycount(repo, "refs/heads/master"));
-
- git_signature_free(sig);
-
-}
assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
}
+void test_repo_init__empty_template_path(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ opts.template_path = "";
+
+ cl_git_pass(git_futils_mkdir("foo", 0755, 0));
+ cl_git_pass(git_repository_init_ext(&_repo, "foo", &opts));
+
+ cleanup_repository("foo");
+}
+
void test_repo_init__extended_0(void)
{
git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
/* Initialize the repository */
cl_git_pass(git_repository_init(&_repo, "committed", 0));
- /* Init will be automatically created when requested for a new repo */
+ /* Index will be automatically created when requested for a new repo */
cl_git_pass(git_repository_index(&index, _repo));
/* Create a file so we can commit it
cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
git_repository_free(repo2);
+#else
+ cl_skip();
#endif
}
--- /dev/null
+x\ 1\9d\8eK
+\ 21\10\ 5]ç\14¹\80\92_§\13\10ñ\10â>ééè@ÆHÌ\bÞÞñ
+î^\15\14<jË2\ fi4îFg\969Å\89
++\8cÁ\ 5\ e\f¤3ØL6{vX
+`ÙFÐâ\99:?\86´\11Ct\8e\1dM`y"Ò*iT)\9bR´3\ 5M\84\1c!\88´\8e{ëòòé3Ë+×\9aäqü ¾Ï·%Íõ@m9I\rÚ{ð¨\9cÜ« \94Øìvpð\1f©xµµ\13\8b/ø\90E\17
\ No newline at end of file
--- /dev/null
+x\ 1\9d\8eA
+\ 21\fE]÷\14¹\80\92¤vÚ\82\88+O î3mF\a¦\8e\94*x{ë\15\þ÷yðÒZÊÜ\80\897ªB\14æ\90H5ø\89ÃÀÈ\9a:sY\83õBÁ9D²£3O©úhàûk1\87\9cT-£Ø\91¦¨\99pt\81÷>N"1[!#¯v_+\>uV¸ê²\b\1cÚo,ïÓȼìÒZ\8e@\8e\ 6æî2l1 \9aN{`Ó?Ts6_\11?B*
\ No newline at end of file
--- /dev/null
+x\ 1\9d\8eQ
+\ 2!\10@ûö\14s\81BÇÕ\14"úé\ 6Ñÿè\8cµ m\88\ 5ݾí
+}¾\a\ f^^Z\9b\a ÁÍè"ÀÉ\19â\82ÉÇ(ÂÑJ\b\96ý\84®8ñÙiã\1dÅ\8cêI]\1e\ 3ö9\92Õ\1c8\8bXÔd\93)QØèä\ 2NûX\88"[2\8a^ã¾t¸|ú,p\95Z \ eã\aõ}º5\9aë./í\bÆ\19\8f¸¶\b[\1d´V«]\a\87ü\91ª³ú\ 2¯¯Bü
\ No newline at end of file
--- /dev/null
+x\ 1\9d\8eQ
+B!\10Eûv\15n Pç\8d\1aDôÑ\12¢\7f\9d\19ëÁ³\17bA»Ï¶Ðç¹\9c\v\87ÖZç®\9d\85Mo"\1a7)\87Hv\1f\80\89\ 3±dq0!S,T( Jð¬\9e©É£\ fß\ 2\12Æ8q`¶à\8a\14È\138¢\84Ñù\9cò>\16\b*½ú}múòi³è«,KÒ\87þ\83å}ºÕ4/;ZëQÛ\11àÑ»\88zk¢1j¬#°Ë\1fWuvê\vüÈCN
\ No newline at end of file
--- /dev/null
+x\ 1\9d\8fKj\ 31\10\ 5½Ö)t\81\98VëÛ\10\8cs\a\93}«Õ²\af2f\90\ 3¹½'\v_ÀËWT-\9e¬Ë2\r\8b \ ecSµ¹\93¸\96KË ¤v&Âì«8í)`#
+\92*s\14sçM\7f\86õàI ·\9a(J\11¢\ 6\9c%«Ç\ eÜ5\80²BUxù\98\9a\17
+!R\r\9d\19J\a êbI\99È\vûà¢\8f\84\86\1fã¶nöò·Mj¿u\9eÙ~\8eÿ1ÿ\9e¯\vOóQÖåd]t 1\16\fö\ 3
+\80Ùé~hè\e©ù2Oä¼P#
\ No newline at end of file
--- /dev/null
+f65de1834f57708e76d8dc25502b7f1ecbcce162
--- /dev/null
+b01de62cf11945685c98ec671edabdff3e90ddc5
--- /dev/null
+182d0d250d1d7adcc60c178be5be98358b3a2fd1
--- /dev/null
+0ce202f64fa8356c1a32835fce4058ca76b0c7ed
--- /dev/null
+x\ 1\9d\8e]
+Â0\10\84}Î)ö\ 2J\92vó\ 3">x\ 4/°I6Zh\eIÓ\9eß*\9eÀ§a\86ï\83\89e\9a\86\ 6ZûC«Ì`\89\9cF\876;KL¶O)«.yìÑ8¢N¦\1c\13\87^¼¨òÜ \e\83¡Ç}óa'¤ôÑ° öSØù\98Ù*\9b²\13´¶g©p£mHp_ëÌ\15ÎsÙh\1c\96ë/O¥>. Pi\85Fú\ e\8e\12¥\14ñû±íÂ?¶¨\13,k\98JZG\16o»¾JT
\ No newline at end of file
--- /dev/null
+50c5dc8cdfe40c688eb0a0e23be54dd57cae2e78
+++ /dev/null
-[submodule "my-submodule"]
- path = my-submodule
- url = bogus
--- /dev/null
+[submodule "my-submodule"]
+ path = my-submodule
+ url = bogus
+++ /dev/null
-Subproject commit efad0b11c47cb2f0220cbd6f5b0f93bb99064b00
--- /dev/null
+x\ 1\95\8eQjÃ0\10\ 5óSì\7f¡X+Û² \94\1e!4'Xi\9f\88Hì\18eC¯ß\90\etþg\98r_×fÄ\1c\ fÖ\ 1ZRT\95\ 5%Ç*\8c!q.¡&°ø\984\16\9f\870\8f\9aÝ.\1d\9b\91Oª\81µf?ñ ó\88)g\11,Z0£\8e\1cô\95©>8yÚåÞé$Ö[¹ÒÙж\8bt5:î\8fïýúølë\17ùÉ'Ï<óB\1fÃ\vWÞ\7f\86ÿ\9bî\a¿½\19H6%Å\r\ 6÷\auJI§
\ No newline at end of file
--- /dev/null
+be053a189b0bbde545e0a3f59ce00b46ad29ce0d
--- /dev/null
+84d8efa38af7ace2b302de0adbda16b1f1cd2e1b
--- /dev/null
+ref: refs/heads/master
--- /dev/null
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
--- /dev/null
+x\ 1%\89=
+\800\fF]í)\8a\a0à"\ e\1eÃI\1c\8c\ 6\15*\95|Éý-t{?\9c2ÇilV8¿ùô$±«Øm¡ýv»ã\1ck\bk*F\vDAÊ\ 4=(=|=6\90 ¬DAv=\ 4\ 4ÛÍA}\99&'\85\1fOò$=
\ No newline at end of file
--- /dev/null
+x\ 1\9d\8e[
+Â0\10EýÎ*f\ 3ʤS\93 \88\88KÐ\15¤é\8d\16ú\904Ý¿\ 5wà×\85Ã9pÓ2MC¥FôP\v@\9dãÜ\au.\84.¶\bpÚ!²OYá\83di\ 4YUÍ'\16Ì\958XïbPn¼ôÊ6
+ħÔ\9e\93¶1[qîÌ\16}0q«ï¥Ðc[W\8c#Ý1f\14ºÄR:àö\9bSZ¦+Y\91Æ+{µtdÏlvº\7f¬»þOm\9e¨u\98_´}è5Ôié·\11«ù\ 2` Kæ
\ No newline at end of file
--- /dev/null
+P pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
+
--- /dev/null
+97896810b3210244a62a82458b8e0819ecfc6850
--- /dev/null
+x\ 1¥N[
+B!\14ìÛUø\1f\84zôê\81\88~ÚA\e8W\8fÜ 3ÌK-?\8bvÐ|Í\ 3f&ÖR.]\1aë6½1Kç-\10pÖÖ%\98 \90Êd\82\ 1\88\84& ×ÚSò6\8b;5¾u©\103Í\14\81\90 Á9\fÎ\84h|\ eÓÄ`UÀh8gAk_j\93§ô¤\96äy©åQorÏÃý°#\7f\83\9fÚÅZ\ eR;\8d*¸1*·j@\fw\9cíüg\8dà\17ǵÓ|eñ\ 6\19§OÏ
\ No newline at end of file
--- /dev/null
+f9ed4af42472941da45a3ce44458455ed227a6be
/* walking */;
/* git log --branches --oneline | wc -l => 16 */
- cl_assert_equal_i(19, i);
+ cl_assert_equal_i(20, i);
}
void test_revwalk_basic__push_head(void)
refute_is_ignored("project/src/foo.c");
refute_is_ignored("project/src/foo/foo.c");
}
+
+void test_status_ignore__deeper(void)
+{
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/.gitignore",
+ "*.data\n"
+ "!dont_ignore/*.data\n");
+
+ cl_git_pass(p_mkdir("empty_standard_repo/dont_ignore", 0777));
+ cl_git_mkfile("empty_standard_repo/foo.data", "");
+ cl_git_mkfile("empty_standard_repo/bar.data", "");
+ cl_git_mkfile("empty_standard_repo/dont_ignore/foo.data", "");
+ cl_git_mkfile("empty_standard_repo/dont_ignore/bar.data", "");
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "foo.data"));
+ cl_assert_equal_i(1, ignored);
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "bar.data"));
+ cl_assert_equal_i(1, ignored);
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "dont_ignore/foo.data"));
+ cl_assert_equal_i(0, ignored);
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "dont_ignore/bar.data"));
+ cl_assert_equal_i(0, ignored);
+}
static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
#endif
+/*
+ * Create a file in NFD (canonically decomposed) format. Ensure
+ * that when core.precomposeunicode is false that we return paths
+ * in NFD, but when core.precomposeunicode is true, then we
+ * return paths precomposed (in NFC).
+ */
void test_status_renames__precomposed_unicode_rename(void)
{
#ifdef GIT_USE_ICONV
{ GIT_STATUS_WT_RENAMED, "sixserving.txt", nfc },
};
- rename_file(g_repo, "sixserving.txt", nfc);
+ rename_file(g_repo, "sixserving.txt", nfd);
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
cl_assert_equal_i(0, counts.wrong_status_flags_count);
cl_assert_equal_i(0, counts.wrong_sorted_path);
+#else
+ cl_skip();
#endif
}
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
cl_assert_equal_i(0, counts.wrong_status_flags_count);
cl_assert_equal_i(0, counts.wrong_sorted_path);
+#else
+ cl_skip();
#endif
}
git_status_list_free(statuslist);
}
+void test_status_worktree__at_head_parent(void)
+{
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *statuslist;
+ git_tree *parent_tree;
+ const git_status_entry *status;
+
+ cl_git_mkfile("empty_standard_repo/file1", "ping");
+ stage_and_commit(repo, "file1");
+
+ cl_git_pass(git_repository_head_tree(&parent_tree, repo));
+
+ cl_git_mkfile("empty_standard_repo/file2", "pong");
+ stage_and_commit(repo, "file2");
+
+ cl_git_rewritefile("empty_standard_repo/file2", "pyng");
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.baseline = parent_tree;
+ cl_git_pass(git_status_list_new(&statuslist, repo, &opts));
+
+ cl_assert_equal_sz(1, git_status_list_entrycount(statuslist));
+ status = git_status_byindex(statuslist, 0);
+ cl_assert(status != NULL);
+ cl_assert_equal_s("file2", status->index_to_workdir->old_file.path);
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW, status->status);
+
+ git_tree_free(parent_tree);
+ git_status_list_free(statuslist);
+}
g_repo = setup_fixture_submod2();
}
+void test_submodule_lookup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
void test_submodule_lookup__simple_lookup(void)
{
assert_submodule_exists(g_repo, "sm_unchanged");
cl_assert_equal_i(8, data.count);
}
-void test_submodule_lookup_cached(void) {
+void test_submodule_lookup__cached(void)
+{
git_submodule *sm;
git_submodule *sm2;
/* See that the simple tests still pass. */
git_submodule_free(sm);
git_submodule_free(sm2);
}
+
+void test_submodule_lookup__lookup_in_bare_repository_fails(void)
+{
+ git_submodule *sm;
+
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("submodules.git");
+
+ cl_git_fail(git_submodule_lookup(&sm, g_repo, "nonexisting"));
+}
+
+static int foreach_cb(git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(sm);
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return 0;
+}
+
+void test_submodule_lookup__foreach_in_bare_repository_fails(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("submodules.git");
+
+ cl_git_fail(git_submodule_foreach(g_repo, foreach_cb, NULL));
+}
"ssh+git://somehost:somepath",
"git+ssh://somehost:somepath",
"git@somehost:somepath",
+ "ssh://somehost:somepath%20with%20%spaces",
+ "ssh://somehost:somepath with spaces"
};
git_transport *transport;
unsigned i;